Skip to content
Back to skills

Branch Hygiene

BSecurity

Classify and clean up merged, stale, and orphaned git branches and worktrees. Detects squash-merges that git branch --merged cannot see, stale worktrees, and remote-gone branches. Use when user asks to clean up branches, prune worktrees, tidy the repo, or perform git housekeeping.

  • 7 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 6, 2026
toolsrustgobashgit

Security analysis

B85/100
  • highPerforms destructive filesystem operations

Pro scans all 2 files and shows the line behind each finding

Scanned September 6, 2026

npx -y skills add shrwnsan/vibekit-claude-plugins --skill branch-hygiene --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Branch Hygiene?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Branch Hygiene
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/shrwnsan-branch-hygiene/badge)](https://www.skillsdirectory.com/skills/shrwnsan-branch-hygiene)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: branch-hygiene
description: Classify and clean up merged, stale, and orphaned git branches and worktrees. Detects squash-merges that git branch --merged cannot see, stale worktrees, and remote-gone branches. Use when user asks to clean up branches, prune worktrees, tidy the repo, or perform git housekeeping.
user-invocable: true
allowed-tools:
  - bash(git:*)
  - bash(sh:*)
  - bash(bash:*)
  - Read
---

# Branch & Worktree Hygiene

Classifies every local/remote branch and worktree against the mainline, then
cleans up what is resolved. Never guesses — every deletion is backed by a
verified verdict.

## The core problem

`git branch --merged` is **useless in squash-merge workflows**: it reports
nothing mergeable even when branches are fully resolved (content landed via
squashed PRs). The only reliable test is content-level: PR-number log matching,
file presence, and feature greps.

## Workflow

1. **Classify** — run the read-only classifier:
   ```bash
   sh <skill-dir>/scripts/classify-branches.sh [mainline-ref]
   ```
   Default mainline: `main` (falls back to `master`). Emits one line per item:
   `<verdict>	<kind>	<ref-or-path>	<evidence>`

2. **Verify** — the classifier's output is *evidence, not authority*. Before
   acting on any verdict, spot-check it: open the cited PR commit, diff the
   named files, confirm the feature grep. Classifiers hallucinate less than
   models but more than never.

3. **Act per tier**:

   | Verdict | Action | Confirmation |
   |---|---|---|
   | `merged-ancestry` / `merged-content` (branch) | `git branch -D` | none (reflog recovers) |
   | `remote-gone` (local branch, upstream deleted) | `git branch -D` + remove any worktree | none |
   | `stale-base` (branch predates mainline; merging would regress) | surface to user | user decides |
   > `stale-base` is currently emitted by review judgment, not the classifier — treat divergence without squash evidence as `unmerged` and assess manually.
   | `superseded` (content landed, mainline evolved past it) | surface to user | user decides |
   | `unmerged` (genuinely unique commits) | surface to user, never delete | user decides |
   | `worktree-prunable` (directory gone, registry entry remains) | `git worktree prune` | none (metadata only) |
   | `worktree-clean-merged` (clean tree, branch resolved) | `git worktree remove` | **always confirm** (deletes a directory) |
   | `worktree-dirty` / `worktree-unmerged` | never touch | — |
   | `orphan-dir` (looks like a worktree, not registered) | surface, move with `trash` on confirm | user decides |

4. **Remote branches** — deleting `origin/*` refs is outward-facing: list them
   with verdicts, delete only after explicit user approval, in one batch push.

## Squash-merge detection (how the classifier thinks)

For each branch not mergeable by ancestry:
1. Extract its commit subjects; grep mainline log for the same subjects with
   `(#N)` suffixes — squash-merges preserve the subject.
2. Check file presence: key files the branch adds, present on mainline?
3. Feature greps: distinctive strings from the branch's diff, present in
   mainline's version of those files?
All three hit → `merged-content`. Content present but mainline's copy is
larger/newer → `superseded`. Diff would *remove* lines mainline has → `stale-base`.

## Worktree specifics

- `git worktree list --porcelain` is the source of truth; each entry's `branch`
  or detached `HEAD` gets the same verdict engine as free branches.
- Detached-HEAD worktrees: verdict keyed on the commit — `merged-ancestry`
  if contained in mainline and tree is clean.
- Tool-created worktrees (e.g. `.claude/worktrees/`) are included; their
  auto-cleanup is best-effort, this is the backstop.

## Known limitations

- **Origin only**: the remote scan covers `origin/*` exclusively. Repos with
  additional remotes (`upstream/`, `fork/`) only get their origin refs
  classified — run with awareness, or extend the remote loop.
- **Squash-merge detection is heuristic**: subject matching can false-positive
  on generic subjects ("fix bugs"); the verify step exists for exactly this
  reason. Never trust a verdict without checking its evidence.

## Hard rules

- The classifier script is **read-only** — it never deletes, pushes, or prunes.
- Dirty worktrees and `unmerged` refs are never modified, only reported.
- `git worktree remove` and remote ref deletion always require user confirmation.
- Use `trash`, never `rm -rf`, for orphaned directories.

Files in this skill

  • SKILL.md4.4 KB
  • scripts/classify-branches.sh6.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.