Skip to content

fix(remove): remove a worktree by its path, not its branch name - #97

Merged
baldurpan merged 3 commits into
mainfrom
77-worktree-remove-fails-when-another-worktree-path
Oct 3, 2026
Merged

baldurpan merged 3 commits into
mainfrom
77-worktree-remove-fails-when-another-worktree-path

Conversation

@baldurpan

Copy link
Copy Markdown
Contributor

Closes #77

What was wrong

gitNukeWorktreeCmd ran git worktree remove <branchName>. Git resolves that argument by matching it against the trailing path components of every worktree. When two worktrees end in the same name, the match is ambiguous and git falls back to a path relative to the cwd. The removal then fails with fatal: '<name>' is not a working tree. Two ways to hit it:

  • the main checkout directory has the same name as the branch, e.g. checkout demo and branch demo
  • branches demo and x/demo are checked out side by side

The fix

  • src/lib/git.ts: gitNukeWorktreeCmd and gitNukeWorktree now take the worktree's list entry (branchName + path) instead of a branch string.
    • git worktree remove now gets the path.
    • git branch -D and the spinner and error messages still use the branch name.
    • This covers single worktree remove, multi-select remove and worktree cleanup.
  • src/lib/git.test.ts: the pinned command lines use a path that does not look like the branch name, so a swap would show.
  • src/agent-mode.test.ts: two new real-git end-to-end cases, one per reproduction above. Both fail when the remove call is switched back to the branch name. The scenario comment that named the checkout checkout to work around this bug is corrected.
  • .changeset/brave-paths-match.md: a patch note.

Verification

  • pnpm check, pnpm typecheck and pnpm build: all exit 0.
  • pnpm test: 962 passed.
  • pnpm docs:test: 68 passed.
  • Independent review verdict: PASS WITH NOTES, nothing blocking.

`git worktree remove <branch>` resolves its argument by the trailing path
components of every worktree. When two end in the same name (a checkout
directory named like the branch, or branches `demo` and `x/demo` side by
side) git falls back to a cwd-relative path and fails with "is not a
working tree". Every removal path now hands git the worktree's path, and
keeps the branch name for `git branch -D` and the messages.

Closes #77
@baldurpan
baldurpan enabled auto-merge (squash) October 2, 2026 21:54

@craig-o-curtis craig-o-curtis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well that's a sneaky one, git quietly matching a bare name against the tail end of every worktree path. Nice find, and a tidy fix. Looks real good to me.

Went through the whole diff:

  • gitNukeWorktreeCmd and gitNukeWorktree take { branchName, path } now. git worktree remove gets the path, and git branch -D, the spinner and the error text still get the branch name. All three callers (gitRemoveWorktree, gitRemoveWorktreesWithProgress, and gitNukeWorktree itself) hand over the list entry they already had, so single remove, multi-select and cleanup are all covered, and there's no other git worktree remove hiding in src/.
  • The path passed back is the one git worktree list printed in the first place, so git is being handed its own spelling of it.
  • The unit tests use a path that looks nothing like the branch (/repo/project.worktrees/some-dir), so swapping the two would show up right away. That's a thoughtful touch.
  • Two real-git cases in agent-mode.test.ts, one per reproduction, and the old comment that worked around the bug by naming the checkout checkout is put right.
  • patch changeset, and no CLI surface or --json shape changes, so that level's just right.

I didn't want to take git's word for it secondhand, so I tried it in a scratch repo on git 2.54:

Case By branch name By path
checkout dir demo + branch demo fatal: 'demo' is not a working tree removed
branches demo and x/demo side by side fatal: 'demo' is not a working tree both removed
worktree directory already deleted (prunable) n/a removed, using the listed path
run from a subdirectory of the checkout n/a removed

The third row was the one I was a little worried about, since cleanup treats a missing directory as safe to remove and would send that path along. It's fine.

Couple things to know, neither worth holding it up over:

  1. It's one commit behind main now (#98, the worker URL). I trial-merged it into current main and alongside #95 (they both touch src/lib/git.test.ts) and it all comes out clean.
  2. The "962 passed" in the description won't match once #95 lands too, so whichever goes second may want to give the snapshot in context/verify.md a glance.

ci and changeset are green on this head. I ran the git reproductions above myself, but not the project's own suite.

That'll do just fine. Thanks a bunch!

@baldurpan
baldurpan merged commit bf8adc9 into main Oct 3, 2026
2 checks passed
@baldurpan
baldurpan deleted the 77-worktree-remove-fails-when-another-worktree-path branch October 3, 2026 10:40
@baldurpan baldurpan mentioned this pull request Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

worktree remove fails when another worktree path ends in the same name as the branch

2 participants