resume picker: make worktree porcelain parsing block-scoped (trail finding) · Entire

resume picker: make worktree porcelain parsing block-scoped (trail finding)

5ef1842→main·

Soph·1mo ago·2 files·+59 added/-5 removed

Factor the git worktree list --porcelain parsing in branchCheckedOutElsewhere into parseWorktreeForBranch and make it block-scoped: reset the worktree path at each blank-line block boundary and only consider a branch line when a worktree line was seen in the same block. A detached worktree (no branch line) can no longer pair a branch with a stale path or return an empty path. Also drops the now-dead rawRoot!="" guard (the empty case returns earlier).

Adds TestParseWorktreeForBranch covering a detached worktree.

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

Changes

2

440 unmodified lines

```go

dec := exec.CommandContext(ctx, "git", "worktree", "list", "--porcelain")
if rawRoot != "" {
    gitCmd.Dir = rawRoot
}
gitCmd.Dir = rawRoot
out, err := gitCmd.Output()
if err != nil {
    return "", false
}

return parseWorktreeForBranch(string(out), branch, currentRoot)
}

// parseWorktreeForBranch scans `git worktree list --porcelain` output and returns
// the path of a worktree (other than currentRoot) that has branch checked out.
//
// Each worktree is a block beginning with a `worktree <path>` line and separated
// by a blank line; a `branch <ref>` line only appears for non-detached worktrees.
// curPath is reset at each block boundary and a branch line is only considered
// when a worktree line was seen in the same block, so a detached worktree (no
// branch line) can never pair a branch with a stale path or return an empty one.
func parseWorktreeForBranch(porcelain, branch, currentRoot string) (string, bool) {
    var curPath string
    for _, line := range strings.Split(string(out), "\n") {
    for _, line := range strings.Split(porcelain, "\n") {
        switch {
        case line == "":
            curPath = "" // block boundary
        case strings.HasPrefix(line, "worktree "):
            curPath = strings.TrimPrefix(line, "worktree ")
        case strings.HasPrefix(line, "branch "):
        case strings.HasPrefix(line, "branch ") && curPath != "":
            name := strings.TrimPrefix(strings.TrimPrefix(line, "branch "), "refs/heads/")
            if name == branch && normalizeWorktreePath(curPath) != currentRoot {
                return curPath, true
            }
        }
    }
}

Mcmd/entire/cli/resume_picker.go+17/-5

480 unmodified lines


// TestParseWorktreeForBranch covers the porcelain parser, including a detached
// worktree (no branch line) that must not cause a stale or empty-path match.
func TestParseWorktreeForBranch(t *testing.T) {
    t.Parallel()

porcelain := strings.Join([]string{
        "worktree /repo/main",
        "HEAD 1111111111111111111111111111111111111111",
        "branch refs/heads/main",
        "",
        "worktree /repo/wt-feat",
        "HEAD 2222222222222222222222222222222222222222",
        "branch refs/heads/feat",
        "",
        "worktree /repo/wt-detached",
        "HEAD 3333333333333333333333333333333333333333",
        "detached",
        "",
    }, "\n")

// A branch checked out in another worktree is found, with its path.
    if path, ok := parseWorktreeForBranch(porcelain, "feat", "/repo/main"); !ok || path != "/repo/wt-feat" {
        t.Errorf("feat: got (%q, %v), want (/repo/wt-feat, true)", path, ok)
    }

// The current worktree's own branch is not "elsewhere".
    if path, ok := parseWorktreeForBranch(porcelain, "main", "/repo/main"); ok {
        t.Errorf("main from its own worktree should not match, got (%q, %v)", path, ok)
    }

// A branch on no worktree is not found.
    if _, ok := parseWorktreeForBranch(porcelain, "nope", "/repo/main"); ok {
        t.Error("unknown branch should not match")
    }

// A name equal to the detached worktree's path must not match (the detached
    // block has no branch line, so nothing is attributed to it).
    if _, ok := parseWorktreeForBranch(porcelain, "/repo/wt-detached", "/repo/main"); ok {
        t.Error("detached worktree must not produce a branch match")
    }
}

// TestBranchCheckedOutElsewhere verifies worktree awareness: a branch checked
// out in another worktree is detected (with its path), while the current
// worktree's own branch and unknown branches are not flagged.