What's wrong today
Running wt new <name> against a name that already corresponds to an existing worktree fails with a low-information error. The natural expectation is that re-running wt new demo after exiting the previous wt new demo session should drop the user back into the same worktree, the way mkdir -p is idempotent or git checkout -b falls through gracefully when the branch already exists.
Reproduction
$ wt new demo
Entering worktree: demo
(wt) $ exit
--- Exiting wt shell ---
Working tree clean.
$ wt new demo
Error: Worktree path already exists: "/.../.worktrees/demo"
The check in question is a bare directory-existence test that conflates two genuinely different states:
- Resumable — git already tracks the directory as a registered worktree (most common; user exited a previous
wt new and is coming back).
- Orphan — the directory exists on disk but is not registered with git (e.g. leftover from a failed
wt rm or external rm -rf).
These cases warrant different responses.
What to build
Make wt new <name> idempotent for the common case of returning to a worktree that was already created. Specifically:
-
If git already tracks <name> as a worktree (registered in git worktree list):
- Skip the stash/migrate step.
- Print:
Worktree '<name>' already exists, entering it. to stderr.
- Spawn the wt shell rooted at the existing worktree path (equivalent to
wt use <name>).
- Honour
--print-path: print the existing worktree path to stdout and exit, instead of spawning a shell.
-
If the directory exists but is not a registered worktree (orphan): bail with an actionable message such as
Error: Path '<path>' exists but is not a registered worktree.
Run 'git worktree prune' or remove the directory before retrying.
Do not auto-clean — the orphan could contain user work.
-
The fresh-create path is unchanged.
Expected behaviour after this lands
$ wt new demo
Worktree 'demo' already exists, entering it.
(wt) $
Implementation notes
- The split belongs in
cmd_new, before the migration logic and before WorktreeManager::create_worktree is called. After the name is resolved, query WorktreeManager::worktree_exists(&name):
- If
true: load info via get_worktree_info, then either spawn the wt shell or print the path (when --print-path). Return.
- If
false: continue the existing migration + create flow.
- The orphan check belongs in
WorktreeManager::create_worktree, replacing the current bare directory-exists() bail. Cross-reference git worktree list --porcelain to confirm git does not already know about the path; if it doesn't and the path exists, emit the actionable error.
- Skipping the migration when resuming is important: today's
migrate_from_current_branch would needlessly stash uncommitted changes and switch branches even though no new worktree is being created.
- The existing helpers
WorktreeManager::worktree_exists, WorktreeManager::get_worktree_info, and wt::shell::spawn_wt_shell (already used by wt use) are reusable as-is.
Acceptance criteria
Out of scope
- A
--no-resume / --strict flag for create-or-fail semantics. Could be added in a follow-up if scripts need it.
- Auto-cleaning orphan directories. Stays manual — too risky to delete without explicit user opt-in.
Blocked by
None — can start immediately.
What's wrong today
Running
wt new <name>against a name that already corresponds to an existing worktree fails with a low-information error. The natural expectation is that re-runningwt new demoafter exiting the previouswt new demosession should drop the user back into the same worktree, the waymkdir -pis idempotent orgit checkout -bfalls through gracefully when the branch already exists.Reproduction
The check in question is a bare directory-existence test that conflates two genuinely different states:
wt newand is coming back).wt rmor externalrm -rf).These cases warrant different responses.
What to build
Make
wt new <name>idempotent for the common case of returning to a worktree that was already created. Specifically:If git already tracks
<name>as a worktree (registered ingit worktree list):Worktree '<name>' already exists, entering it.to stderr.wt use <name>).--print-path: print the existing worktree path to stdout and exit, instead of spawning a shell.If the directory exists but is not a registered worktree (orphan): bail with an actionable message such as
Do not auto-clean — the orphan could contain user work.
The fresh-create path is unchanged.
Expected behaviour after this lands
Implementation notes
cmd_new, before the migration logic and beforeWorktreeManager::create_worktreeis called. After thenameis resolved, queryWorktreeManager::worktree_exists(&name):true: load info viaget_worktree_info, then either spawn the wt shell or print the path (when--print-path). Return.false: continue the existing migration + create flow.WorktreeManager::create_worktree, replacing the current bare directory-exists()bail. Cross-referencegit worktree list --porcelainto confirm git does not already know about the path; if it doesn't and the path exists, emit the actionable error.migrate_from_current_branchwould needlessly stash uncommitted changes and switch branches even though no new worktree is being created.WorktreeManager::worktree_exists,WorktreeManager::get_worktree_info, andwt::shell::spawn_wt_shell(already used bywt use) are reusable as-is.Acceptance criteria
wt new <name>twice in succession (withexitbetween) succeeds both times and leaves the user in the existing worktree on the second run.Worktree '<name>' already exists, entering it.is printed to stderr.wt new <name> --print-pathon a name that already exists prints the existing worktree path to stdout and exits 0 without spawning a shell.wt new <name>exits non-zero with an actionable error naming the path and suggestinggit worktree pruneor manual removal. The directory is not modified.wt new <name>against a brand-new name continues to work exactly as before (regression).Out of scope
--no-resume/--strictflag for create-or-fail semantics. Could be added in a follow-up if scripts need it.Blocked by
None — can start immediately.