From 2d5ac1096644e937377d6cbe7e25209a52cbc47e Mon Sep 17 00:00:00 2001 From: ofer Date: Wed, 11 Mar 2026 16:56:00 -0700 Subject: [PATCH] show worktree locations in av tree output --- cmd/av/tree.go | 13 ++++++++++++ internal/git/worktree.go | 46 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 internal/git/worktree.go diff --git a/cmd/av/tree.go b/cmd/av/tree.go index a7e3b533..bdc5e2f8 100644 --- a/cmd/av/tree.go +++ b/cmd/av/tree.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "path/filepath" "strings" "github.com/aviator-co/av/internal/git" @@ -36,6 +37,14 @@ var treeCmd = &cobra.Command{ return err } + worktrees, _ := repo.WorktreeList(ctx) + worktreesByBranch := make(map[string]string) + for _, wt := range worktrees { + if wt.Branch != "" { + worktreesByBranch[wt.Branch] = filepath.Base(wt.Path) + } + } + var ss []string currentBranch := status.CurrentBranch tx := db.ReadTx() @@ -59,6 +68,7 @@ var treeCmd = &cobra.Command{ currentBranch, branchName, isTrunk, + worktreesByBranch, ) }), ) @@ -96,6 +106,7 @@ func renderStackTreeBranchInfo( currentBranchName string, branchName string, isTrunk bool, + worktrees map[string]string, ) string { bi, _ := tx.Branch(branchName) @@ -104,6 +115,8 @@ func renderStackTreeBranchInfo( var stats []string if branchName == currentBranchName { stats = append(stats, styles.HEAD.Render("HEAD")) + } else if wtName, ok := worktrees[branchName]; ok { + stats = append(stats, colors.Faint("worktree: "+wtName)) } if bi.ExcludeFromSyncAll { descendants := meta.SubsequentBranches(tx, branchName) diff --git a/internal/git/worktree.go b/internal/git/worktree.go new file mode 100644 index 00000000..b8b4d704 --- /dev/null +++ b/internal/git/worktree.go @@ -0,0 +1,46 @@ +package git + +import ( + "context" + "strings" +) + +type WorktreeInfo struct { + Path string + HEAD string + Branch string +} + +func (r *Repo) WorktreeList(ctx context.Context) ([]WorktreeInfo, error) { + out, err := r.Git(ctx, "worktree", "list", "--porcelain") + if err != nil { + return nil, err + } + out = strings.TrimSpace(out) + if out == "" { + return nil, nil + } + + var worktrees []WorktreeInfo + var current WorktreeInfo + for line := range strings.SplitSeq(out, "\n") { + if line == "" { + if current.Path != "" { + worktrees = append(worktrees, current) + } + current = WorktreeInfo{} + continue + } + if rest, ok := strings.CutPrefix(line, "worktree "); ok { + current.Path = rest + } else if rest, ok := strings.CutPrefix(line, "HEAD "); ok { + current.HEAD = rest + } else if rest, ok := strings.CutPrefix(line, "branch "); ok { + current.Branch = strings.TrimPrefix(rest, "refs/heads/") + } + } + if current.Path != "" { + worktrees = append(worktrees, current) + } + return worktrees, nil +}