Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions cmd/av/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"context"
"fmt"
"path/filepath"
"strings"

"github.com/aviator-co/av/internal/git"
Expand Down Expand Up @@ -36,6 +37,14 @@ var treeCmd = &cobra.Command{
return err
}

worktrees, _ := repo.WorktreeList(ctx)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The error returned by repo.WorktreeList(ctx) is ignored. If the command fails (e.g., due to a Git error), this will fail silently. The error should be handled by printing a warning to the user so they are aware that the worktree information might be missing.

		worktrees, err := repo.WorktreeList(ctx)
		if err != nil {
			// Not a fatal error, just print a warning and continue.
			_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "warning: could not read worktree locations: %v\n", err)
		}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

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()
Expand All @@ -59,6 +68,7 @@ var treeCmd = &cobra.Command{
currentBranch,
branchName,
isTrunk,
worktreesByBranch,
)
}),
)
Expand Down Expand Up @@ -96,6 +106,7 @@ func renderStackTreeBranchInfo(
currentBranchName string,
branchName string,
isTrunk bool,
worktrees map[string]string,
) string {
bi, _ := tx.Branch(branchName)

Expand All @@ -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)
Expand Down
46 changes: 46 additions & 0 deletions internal/git/worktree.go
Original file line number Diff line number Diff line change
@@ -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
Comment on lines +24 to +45
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The use of for line := range strings.SplitSeq(out, "\n") requires Go 1.22. For better compatibility with older Go versions, it's safer to use strings.Split. Additionally, the parsing logic can be simplified by splitting the output into records first, which also removes the duplicated code for appending the last worktree. This improves readability and maintainability.

Suggested change
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
var worktrees []WorktreeInfo
// Each worktree record is separated by a blank line.
for _, record := range strings.Split(out, "\n\n") {
var current WorktreeInfo
for _, line := range strings.Split(record, "\n") {
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

}
Loading