surface git stderr from common-dir resolution · Entire
surface git stderr from common-dir resolution
5362c37→main·
pfleidi·1w ago·2 files·+28 added/-9 removed
Extract the ExitError.Stderr formatting into gitOutputError and use it for both git invocations that parse stdout. Align the ignore-rule doc comment with the append-only-to-existing-.gitignore behavior.
Sessions
01KX700A428KXQ7V4WFPBHZ6X4View transcript
?\
Add trail checkout --worktree SupportClaude Code·3 steps
Changes
2
cmd/entire/cli
Mtrail_checkout_worktree.go+18/-9
Mtrail_checkout_worktree_test.go+10
53 unmodified lines
54
55
56
57
57
58
59
60
17 unmodified lines
78
79
80
81
82
83
81
82
83
84
85
86
87
88
332 unmodified lines
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
12 unmodified lines
450
451
452
440
441
442
443
444
453
454
455
456
53 unmodified lines
cmd := exec.CommandContext(ctx, "git", "rev-parse", "--git-common-dir")
output, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("failed to get git common dir: %w", err)
return "", gitOutputError("failed to get git common dir", err)
}
gitDir := strings.TrimSpace(string(output))
if !filepath.IsAbs(gitDir) {
17 unmodified lines
return filepath.Dir(gitDir), nil
}
// ensureTrailWorktreeIgnoreRule makes sure .entire/worktrees/ is git-ignored.
// Already ignored (any mechanism) → silent no-op. Otherwise the rule is
// appended to the repo-root .gitignore; committing it is the user's choice.
// ensureTrailWorktreeIgnoreRule appends the .entire/worktrees/ rule to an
// existing repo-root .gitignore when the directory isn't already ignored.
// Already ignored, or no .gitignore at all → silent no-op: the CLI doesn't
// impose ignore policy on a repo that hasn't opted into one, and committing
// the appended rule stays the user's choice.
func ensureTrailWorktreeIgnoreRule(ctx context.Context, w io.Writer, root string) error {
check := exec.CommandContext(ctx, "git", "check-ignore", "-q", trailWorktreesRelDir+"/")
check.Dir = root
332 unmodified lines
return nil
}
// gitOutputError formats a failed git invocation, including git's stderr
// (captured by cmd.Output in ExitError.Stderr) when it carries a diagnostic —
// the bare error is usually just "exit status 128".
func gitOutputError(action string, err error) error {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) && len(bytes.TrimSpace(exitErr.Stderr)) > 0 {
return fmt.Errorf("%s: %s: %w", action, bytes.TrimSpace(exitErr.Stderr), err)
}
return fmt.Errorf("%s: %w", action, err)
}
func staleTrailWorktreeError(branch, path string) error {
return fmt.Errorf("branch %q is registered to a missing worktree at %s; run 'git worktree prune' to clear it", branch, path)
}
12 unmodified lines
cmd.Dir = root
output, err := cmd.Output()
if err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) && len(bytes.TrimSpace(exitErr.Stderr)) > 0 {
return trailWorktreeMatch{}, false, fmt.Errorf("failed to list worktrees: %s: %w", bytes.TrimSpace(exitErr.Stderr), err)
}
return trailWorktreeMatch{}, false, fmt.Errorf("failed to list worktrees: %w", err)
return trailWorktreeMatch{}, false, gitOutputError("failed to list worktrees", err)
}
// Empty currentRoot: match any worktree, including the current checkout.
path, found := parseWorktreeForBranch(string(output), branch, "")
Mcmd/entire/cli/trail_checkout_worktree.go+18/-9
642 unmodified lines
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
642 unmodified lines
}
}
func TestGitCommonDirForTrailWorktree_SurfacesGitError(t *testing.T) {
testutil.IsolateGitConfigEnv(t)
t.Chdir(t.TempDir())
_, err := gitCommonDirForTrailWorktree(context.Background())
if err == nil || !strings.Contains(err.Error(), "not a git repository") {
t.Fatalf("error = %v, want git stderr in message", err)
}
}
func TestCheckoutTrailWorktree_RejectsInvalidBranch(t *testing.T) {
t.Parallel()