TimZander / TimZander/claude

standards: squash-merge makes `git branch --merged` useless — use PR state as the worktree/branch cleanup signal

Open Beginner friendly
#214 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
0
Forks
1
Avg merge
1d 3h
Merged PRs (30d)
7

Description

Problem

Cleaning up a worktree and branch after a PR merges is blocked by a mechanical trap that reads as developer forgetfulness.

Under squash-merge, the merge commit is a brand-new commit with no ancestry link to the branch. So git branch --merged <base> reports every squash-merged branch as not merged. Consequences:

  • git branch -d <branch> refuses — "not fully merged"
  • git worktree remove succeeds but strands the branch
  • The only way through is git branch -D (force), which is indistinguishable in risk from discarding genuinely unmerged work

The safe command is blocked and the unsafe one is the only option available. That is why post-merge cleanup does not happen — not discipline, mechanics.

Evidence

Measured in TimZander/AudioClassifier on 2026-08-05, immediately after merging a PR:

12 worktrees:  10 MERGED PRs,  1 CLOSED PR,  1 OPEN PR

11 of 12 were stale. git branch --merged origin/main listed exactly one branch — main. Every one of the 10 merged branches looked unmerged to git.

That repo's CLAUDE.md already documents worktree creation carefully and mandates "work in a worktree, never the primary checkout" — so this is not a repo lacking discipline. The safety check simply gives the wrong answer for its merge strategy.

The rule to add

Add to standards/CLAUDE.md, near the existing Git Push Safety / Git Hygiene material:

Cleaning up after a merge

git branch --merged is meaningless under squash-merge. A squash creates a new commit with no
ancestry to the branch, so every merged branch reports as unmerged and git branch -d refuses it.
Do not read that refusal as "there is unmerged work here."

The merge signal is the pull request, not git ancestry:

gh pr list --head <branch> --state all --json number,state --jq '.[0].state'

MERGED is authoritative. Only then is git branch -D correct rather than reckless.

Do not use "the remote branch is gone" as the signal. It is an inference from the host's
auto-delete-on-merge setting, not proof of merge — and it stays true for a branch someone deleted
by hand. Measured counter-case: an abandoned (CLOSED, never merged) PR's remote branch was still
present, so the heuristic happens to correlate but does not establish anything.

Order matters, and check for work first:

git -C <worktree> status --porcelain   # must be empty
git worktree remove <worktree>         # refuses if dirty — do not reach for --force
git branch -D <branch>                 # -D, not -d: see above
git worktree prune

Never remove the worktree you are currently inside, and never remove the primary checkout.

Acceptance criteria

  • standards/CLAUDE.md states that git branch --merged is unreliable under squash-merge, with the reason
  • It gives the gh pr list --head check as the authoritative merge signal
  • It explicitly rejects "remote branch gone" as a substitute, noting it reflects a host setting
  • It specifies the dirty check, the removal order, and the never-remove-current / never-remove-primary constraints
  • It explains why -D is the correct flag here, so the rule does not read as advocating force-delete generally

Notes

Companion issue: a /tidy-worktrees skill to perform this, since a documented manual procedure demonstrably does not get run — the 11 stale worktrees above accumulated in a repo that documents worktree discipline well.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Edit standards/CLAUDE.md near the existing Git Push Safety / Git Hygiene material. Start by reviewing that section and the acceptance criteria, then add the squash-merge explanation, gh pr list check, dirty-worktree safeguards, removal order, and constraints. Done means all five acceptance criteria are explicitly covered.

Written by the indexing model from the issue text.

Assessment

Tech stack
git, github
Domain
documentation
Issue type
Documentation
Difficulty
1/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
88/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.