Skip to content

Never offer the default branch as a reclaimable worktree - #225

Merged
scgopi merged 1 commit into
mainfrom
fix/hygiene-keep-default-branch
Aug 30, 2026
Merged

Never offer the default branch as a reclaimable worktree#225
scgopi merged 1 commit into
mainfrom
fix/hygiene-keep-default-branch

Conversation

@scgopi

@scgopi scgopi commented Aug 30, 2026

Copy link
Copy Markdown
Owner

Problem

With main checked out in a linked worktree (here: graphcode-release-b3) while the primary checkout sits on a fix branch, worktree hygiene classified the main worktree as safe to remove: git cherry of main against a base of [refs/heads/main, origin/main] is trivially empty (landed), the tree was clean, pushed, and unbound. It was counted as reclaimable and preselected — and the removal path always runs branch -D, so accepting the offer would have deleted the trunk branch.

Fix

All in the pure classifier (WorktreeAssessment), so local and remote repos share it:

  • New isDefaultBranch (ref.branch == facts.defaultBranch).
  • Tier order is now in-use → default branch (.lookBeforeRemoving) → prunable → locked → safe: the trunk can never land in safeToRemove, even as a prunable admin file.
  • isRemovable is false for the default branch — no checkbox, since removal deletes the branch too.
  • The row summary says the default branch · never offered for removal instead of the nonsense merged into main.

Every removal route is covered: sweeper preselection, the reclaimable counts on chips and menus, the resolve-moment auto-remove/ask, and card reclaim offers all gate on this tier.

Tests

  • WorktreeHygieneTests: +3 (default branch never a candidate, not safe when prunable, running loop still reads in use) — 21/21.
  • WorktreeSweepFeatureTests + WorktreeRemovalTests: 31/31 across the three suites.
  • swiftlint and swift-format clean on the changed files.

main checked out in a linked worktree reads as landed against itself,
clean, pushed and unbound — every signal the safe tier asks for — so the
sweeper offered it as reclaimable, and removal would have deleted the
trunk with `branch -D main`. The default branch now classifies as look
before removing (or in use when a loop is running in it), is never
selectable, and its row says so instead of "merged into main".
@scgopi
scgopi merged commit 3782ea8 into main Aug 30, 2026
1 check passed
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.

1 participant