Skip to content

fix(remove): plan no branch deletion for an orphan worktree's unborn branch - #4233

Merged
max-sixty merged 2 commits into
mainfrom
prune-fail
Sep 22, 2026
Merged

max-sixty merged 2 commits into
mainfrom
prune-fail

Conversation

@max-sixty

Copy link
Copy Markdown
Owner

An orphan worktree (git worktree add --orphan) has an unborn branch until its first commit: HEAD names refs/heads/<branch>, but no such ref exists. wt remove planned a branch deletion for it anyway, so it misreported the branch or failed:

  • A live orphan worktree: wt remove fresh printed ↳ Branch unmerged; to delete, run wt remove -D fresh, a command that then fails with No branch named fresh. With --foreground it removed the worktree and then exited 1 with git's fatal: Needed a single revision.
  • A stale one (directory or .git gone): wt remove fresh printed ✓ Pruned stale worktree for fresh followed by ○ Branch fresh retained; has unmerged changes and the same wt remove -D hint.

Now the removal planner treats an unborn branch as having nothing to delete and plans Keep for it, the way it already handles a branch that a sibling checkout still uses. The output is just ✓ Removed fresh worktree (or ◎ Removing fresh worktree in background) or ✓ Pruned stale worktree for fresh. The planner reads this from the ref snapshot it already holds, so it adds no git call. The picker's removal goes through the same planner.

test_remove_orphan_worktree removes a live and a stale orphan worktree, each with a remote-tracking branch of the same name. The remote branch catches a check that counts remote branches, which would take the branch for existing.

This was written by Claude Code on behalf of max-sixty

🤖 Generated with Claude Code

…branch

An orphan worktree's branch has no ref until its first commit, so
`wt remove` hinted at a `wt remove -D` that then fails, or (with
--foreground) removed the worktree and exited 1 with git's
"Needed a single revision". The planner now reads the unborn state from
its ref snapshot and plans `Keep`, as it does for a branch a sibling
checkout still uses, for both a live and a stale worktree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@worktrunk-bot worktrunk-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

BranchFate::NotAttempted in src/commands/worktree/types.rs enumerates the reasons a plan retains the branch — "deletion_mode Keep — shared checkout, or --no-delete-branch" — and that list was exhaustive before this change. Unborn is now a third reason, and it is the one a reader is most likely to be chasing: wt remove <orphan> --format json emits "branch_outcome": "not_attempted" and the docstring is where they'd look to interpret it. Worth adding to the parenthetical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@max-sixty
max-sixty merged commit 2a4aa28 into main Sep 22, 2026
37 checks passed
@max-sixty
max-sixty deleted the prune-fail branch September 22, 2026 09:37
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.

2 participants