Skip to content

Commit 72e6887

Browse files
javoireclaude
andauthored
feat: improve error message when branch already checked out in worktree (#59)
When running `stack worktree <branch>` for a branch that's already checked out in another worktree, show helpful suggestions to navigate to or remove the existing worktree instead of a raw git error. Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent bd64571 commit 72e6887

1 file changed

Lines changed: 48 additions & 2 deletions

File tree

cmd/worktree.go

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,19 @@ func createNewBranchWorktree(gitClient git.GitClient, branchName, baseBranch, wo
269269

270270
// Create worktree with new branch
271271
if err := gitClient.AddWorktreeNewBranch(worktreePath, branchName, baseRef); err != nil {
272+
if existingPath := parseWorktreeInUseError(err); existingPath != "" {
273+
return fmt.Errorf("branch %s is already checked out in another worktree\n\n"+
274+
"Existing worktree: %s\n\n"+
275+
"Options:\n"+
276+
" 1. Navigate to existing worktree:\n"+
277+
" %s\n\n"+
278+
" 2. Remove existing worktree and retry:\n"+
279+
" %s",
280+
ui.Branch(branchName),
281+
existingPath,
282+
ui.Command(fmt.Sprintf("cd %s", existingPath)),
283+
ui.Command(fmt.Sprintf("git worktree remove %s", existingPath)))
284+
}
272285
return fmt.Errorf("failed to create worktree: %w", err)
273286
}
274287

@@ -293,6 +306,19 @@ func createWorktreeForExisting(gitClient git.GitClient, branchName, worktreePath
293306
if gitClient.BranchExists(branchName) {
294307
fmt.Printf("Creating worktree for local branch %s\n", ui.Branch(branchName))
295308
if err := gitClient.AddWorktree(worktreePath, branchName); err != nil {
309+
if existingPath := parseWorktreeInUseError(err); existingPath != "" {
310+
return fmt.Errorf("branch %s is already checked out in another worktree\n\n"+
311+
"Existing worktree: %s\n\n"+
312+
"Options:\n"+
313+
" 1. Navigate to existing worktree:\n"+
314+
" %s\n\n"+
315+
" 2. Remove existing worktree and retry:\n"+
316+
" %s",
317+
ui.Branch(branchName),
318+
existingPath,
319+
ui.Command(fmt.Sprintf("cd %s", existingPath)),
320+
ui.Command(fmt.Sprintf("git worktree remove %s", existingPath)))
321+
}
296322
return fmt.Errorf("failed to create worktree: %w", err)
297323
}
298324
if !dryRun {
@@ -493,8 +519,7 @@ func getWorktreesBaseDir(gitClient git.GitClient) (string, error) {
493519
}
494520

495521
expanded := os.ExpandEnv(configured)
496-
if strings.HasPrefix(expanded, "~") {
497-
trimmed := strings.TrimPrefix(expanded, "~")
522+
if trimmed, found := strings.CutPrefix(expanded, "~"); found {
498523
trimmed = strings.TrimPrefix(trimmed, string(os.PathSeparator))
499524
expanded = filepath.Join(homeDir, trimmed)
500525
}
@@ -542,3 +567,24 @@ func symlinkEnvFile(gitClient git.GitClient, worktreePath string) {
542567

543568
fmt.Println(ui.Success("Symlinked .env file"))
544569
}
570+
571+
// parseWorktreeInUseError extracts the existing worktree path from a git error.
572+
// Returns the path if error matches "is already used by worktree at", empty string otherwise.
573+
func parseWorktreeInUseError(err error) string {
574+
if err == nil {
575+
return ""
576+
}
577+
errStr := err.Error()
578+
// Git error format: fatal: 'branch' is already used by worktree at 'path'
579+
marker := "is already used by worktree at '"
580+
idx := strings.Index(errStr, marker)
581+
if idx == -1 {
582+
return ""
583+
}
584+
pathStart := idx + len(marker)
585+
pathEnd := strings.LastIndex(errStr, "'")
586+
if pathEnd <= pathStart {
587+
return ""
588+
}
589+
return errStr[pathStart:pathEnd]
590+
}

0 commit comments

Comments
 (0)