with one click
cleanup-branches
// When the user wants to prune deleted remote refs and remove merged local branches after one or more PRs have been squash-merged, use this skill.
// When the user wants to prune deleted remote refs and remove merged local branches after one or more PRs have been squash-merged, use this skill.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | cleanup-branches |
| description | When the user wants to prune deleted remote refs and remove merged local branches after one or more PRs have been squash-merged, use this skill. |
Remove stale local branches after squash-merges. Squash-merge auto-deletes the
remote branch (per pr-review.md ยง3) but the local branch persists with its
upstream marked [gone] โ that's the primary signal. Do not rely on
git branch --merged main for the common case: squash creates a fresh commit
on main with no link back to the original branch tip, so --merged returns
nothing for squash-merged branches even though their work is in main.
Prune remote-tracking refs:
git fetch --prune origin
List branches whose upstream was deleted (squash-merge target):
git for-each-ref --format='%(refname:short) %(upstream:track)' refs/heads \
| awk '$2 == "[gone]" { print $1 }'
This catches the common squash-merge case. git branch --merged main
only catches merge-commit (non-squash) flows โ keep it as a secondary
check for those.
Cross-check candidates against worktree occupancy and the current branch:
git branch --show-current # never delete this one
git worktree list # branches checked out elsewhere need worktree removal first
Annotate each candidate with its state: [current], [worktree at <path>],
or unblocked.
Show the annotated candidate list to the user. Confirm before any deletion
(the project's worktree pattern means most stale branches sit in a sibling
worktree โ full cleanup is the pair git worktree remove <path> +
git branch -D <name>).
For each confirmed candidate (sequential, never bulk):
git -C <path> status shows no
uncommitted changes), archive .local/ session state
(cp -R <path>/docs/.local/<branch>/ <main-worktree>/docs/.local/<branch>/
โ .local/ is gitignored so worktree removal would lose plan / critic
/ PR / coderabbit drafts), then git worktree remove <path>, then
git branch -D <name>.git branch -D <name> directly.Use -D (force) rather than -d: squash-merged branches fail the safe
-d check because their tip is not an ancestor of main. The [gone]
signal in step 2 is your "merged" evidence.
Local-only safety branches (e.g. backup/m5-pre-rebase, wip/... โ
never had a remote, so step 2 misses them). Surface them via:
git for-each-ref --format='%(refname:short)' refs/heads/backup refs/heads/wip 2>/dev/null
Confirm with the user case-by-case before git branch -D โ these often
guard work in progress.
git branch --show-current first.git worktree remove a worktree with uncommitted changes.
git -C <path> status first; if dirty, ask the user before forcing.-D, not -d. Squash-merged branches will fail -d's "is ancestor"
check. Don't fall back to -d after step 2 โ that re-introduces the original
bug.xargs -r. macOS BSD xargs lacks -r (--no-run-if-empty); a
plain for loop or an if [ -n "$list" ] guard is portable..local/ before worktree removal. docs/.local/<branch>/ is
gitignored, so git worktree remove permanently deletes session drafts
(plan / critic / PR / coderabbit). Always cp -R the directory to the
main worktree's docs/.local/<branch>/ first โ recoverable archive for
future PRs that revisit similar decisions.After all confirmed deletions, print:
../stepg-backend-<slug>/). Cleanup is typically the full pair โ
worktree removal + branch deletion together.git config --global fetch.prune true makes step 1
automatic on every fetch.