print bare worktree path when stdout is not a terminal · Entire
print bare worktree path when stdout is not a terminal
30c2a35→main·
pfleidi·1w ago·3 files·+35 added/-13 removed
Progress and notices for trail worktree checkout now go to stderr, and
a non-terminal stdout carries only the worktree path, so scripts can
use cd "$(entire trail checkout
Sessions
01KX6Z9SGE1SBQEMVEW6JREBNZView transcript
[?
Add trail checkout --worktree SupportClaude Code·2 steps](/content/gh/entireio/cli/session/c5215fe7-28db-4a83-9d2e-482217df292d#timeline-01KX6Z9SGE1SBQEMVEW6JREBNZ/index.html)
Changes
3
cmd/entire/cli
Mtrail_checkout_worktree.go+19/-7
Mtrail_checkout_worktree_test.go+12/-4
Mtrail_cmd.go+4/-2
325 unmodified lines
326
327
328
329
330
329
330
331
332
2 unmodified lines
335
336
337
339
338
339
340
341
342
344
343
344
345
346
348
347
348
349
350
11 unmodified lines
362
363
364
366
367
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
325 unmodified lines
if err := validateTrailWorktreeReuse(ctx, match.path, branch); err != nil {
return staleTrailWorktreeError(branch, match.path)
}
fmt.Fprintf(w, "Worktree already exists at %s\n", match.path)
fmt.Fprintf(w, "cd %s\n", shellQuote(match.path))
printTrailWorktreeLocation(w, errW, "Worktree already exists at "+match.path, match.path)
return nil
case errors.Is(statErr, fs.ErrNotExist):
return staleTrailWorktreeError(branch, match.path)
2 unmodified lines
}
}
proceed, err := ensureTrailWorktreeBranchAvailable(ctx, w, branch, force)
proceed, err := ensureTrailWorktreeBranchAvailable(ctx, errW, branch, force)
if err != nil {
return err
}
if !proceed {
fmt.Fprintf(w, "Checkout of branch %s cancelled.\n", branch)
fmt.Fprintf(errW, "Checkout of branch %s cancelled.\n", branch)
return nil
}
if err := ensureTrailWorktreeIgnoreRule(ctx, w, root); err != nil {
if err := ensureTrailWorktreeIgnoreRule(ctx, errW, root); err != nil {
return err
}
11 unmodified lines
fmt.Fprintf(errW, "warning: could not copy %s files: %v\n", worktreeIncludeFile, err)
}
fmt.Fprintf(w, "Worktree ready at %s\n", worktreePath)
fmt.Fprintf(w, "cd %s\n", shellQuote(worktreePath))
printTrailWorktreeLocation(w, errW, "Worktree ready at "+worktreePath, worktreePath)
return nil
}
// printTrailWorktreeLocation reports where the worktree lives. On a terminal
// it prints the note and a copy-paste cd hint. Otherwise stdout carries only
// the bare path — so `cd "$(entire trail checkout <n> --worktree)"` works —
// and the note goes to stderr.
func printTrailWorktreeLocation(w, errW io.Writer, note, path string) {
if interactive.IsTerminalWriter(w) {
fmt.Fprintln(w, note)
fmt.Fprintf(w, "cd %s\n", shellQuote(path))
return
}
fmt.Fprintln(errW, note)
fmt.Fprintln(w, path)
}
func validateTrailWorktreeReuse(ctx context.Context, path, branch string) error {
expectedCommonDir, err := gitCommonDirForTrailWorktree(ctx)
if err != nil {
Mcmd/entire/cli/trail_checkout_worktree.go+19/-7
395 unmodified lines
396
397
398
399
400
399
400
401
402
403
404
405
406
73 unmodified lines
480
481
482
480
481
483
484
485
486
487
488
489
490
491
492
395 unmodified lines
}
wantPath := filepath.Join(repoDir, ".entire", "worktrees", "trail-7-feature-test")
if !strings.Contains(out.String(), "cd "+shellQuote(wantPath)) {
t.Fatalf("output = %q, want cd hint for %q", out.String(), wantPath)
if got, want := out.String(), wantPath+"\n"; got != want {
t.Fatalf("stdout = %q, want bare path %q for script use", got, want)
}
if !strings.Contains(errOut.String(), "Worktree ready at "+wantPath) {
t.Fatalf("stderr = %q, want progress notice", errOut.String())
}
if got := currentBranchInDir(t, repoDir); got != startBranch {
t.Fatalf("current branch = %q, want unchanged %q", got, startBranch)
}
73 unmodified lines
if err := checkoutTrailWorktree(context.Background(), &out2, &err2, "feature/reuse", false, 9); err != nil {
t.Fatalf("second checkout: %v; stderr: %s", err, err2.String())
}
if !strings.Contains(out2.String(), "Worktree already exists") {
t.Fatalf("second output = %q, want existing-worktree message", out2.String())
wantPath := filepath.Join(repoDir, ".entire", "worktrees", "trail-9-feature-reuse")
gotPath := strings.TrimSuffix(out2.String(), "\n")
if strings.Contains(gotPath, "\n") || normalizeWorktreePath(gotPath) != normalizeWorktreePath(wantPath) {
t.Fatalf("second stdout = %q, want bare path %q for script use", out2.String(), wantPath)
}
if !strings.Contains(err2.String(), "Worktree already exists") {
t.Fatalf("second stderr = %q, want existing-worktree message", err2.String())
}
}
Mcmd/entire/cli/trail_checkout_worktree_test.go+12/-4
1240 unmodified lines
1241
1242
1243
1244
1244
1245
1246
1247
1248
1249
45 unmodified lines
1295
1296
1297
1296
1298
1299
1300
1301
1240 unmodified lines
With --worktree, the branch is checked out into a git worktree under
.entire/worktrees at the repo root instead of switching this checkout, and the
command prints a cd command for the new worktree. Gitignored files matching
.worktreeinclude patterns are copied into the worktree.
.worktreeinclude patterns are copied into the worktree. When stdout is not a
terminal, only the worktree path is printed, so scripts can use
cd "$(entire trail checkout <trail> --worktree)".
This must be run from within a clone of the repository the trail belongs to; the
trail is looked up against that repository's origin remote.`
45 unmodified lines
}
if opts.Worktree {
fmt.Fprintf(w, "Checking out %s in a worktree\n", describeTrailRef(found))
fmt.Fprintf(errW, "Checking out %s in a worktree\n", describeTrailRef(found))
return checkoutTrailWorktree(ctx, w, errW, branch, opts.Force, found.Number)
}