deadbranch

v2026.09.24

deadbranch CLI for stale-branch cleanup — dry-run preview, TUI or non-interactive delete, protects main/develop/WIP. Use when asked to clean up branches, prune branches, or remove stale branches.

GitHub
安装命令
npx skhub add laurigates/deadbranch
Markdown
SKILL.md

/git:deadbranch

Survey stale git branches and clean them up safely. Always previews before deleting; backup system means every deletion is recoverable.

When to Use This Skill

Use this skill when...Use another approach when...
Asked to clean up, prune, or remove stale branches across a repoDeleting a single known branch — git branch -d <name> is simpler
You want a survey of branch health (age, merge status, count) firstDoing routine gc / repo maintenance — use /git:maintain
You want recoverable deletion with a dry-run preview and backupsPruning remote-tracking refs only — git remote prune origin

Parameters

Parse from $ARGUMENTS (all optional):

FlagDefaultDescription
--days N30Age threshold — branches older than N days are stale
--localoffTarget local branches only
--remoteoffTarget remote branches only
--forceoffInclude unmerged branches (dangerous — ask for explicit confirmation)
--interactiveoffOpen full-screen TUI for visual selection
--dry-runonShow what would be deleted without deleting (always on for the preview step)
--yesoffSkip confirmation prompts (only use if user explicitly asks for automation)
--stats-onlyoffRun stats + list only, skip the clean step entirely

When no flags are given, default to the survey-first workflow below.

Workflow

Step 1: Survey

Run in parallel:

deadbranch stats
deadbranch list

Report both outputs. The stats command shows total branch count, stale count, and age distribution. The list shows each stale branch with its age, merge status, last commit date, and author.

If --days N was provided, pass -d N to both commands. If --local or --remote was provided, pass the flag to list.

If no stale branches are found, stop here and report the repo is clean. If --stats-only was provided, stop here after reporting.

Step 1.5: Reclassify squash-merged branches

deadbranch classifies "merged" via commit ancestry (git branch --merged-style). On repos that squash-merge — the release-please / conventional-commit default — the squash collapses a branch into a single new commit on the base with a fresh SHA, so the branch's own commits are never ancestors and it reads as unmerged. The "safe to delete" count is therefore a lower bound on squash-merge repos (in one real cleanup, only 4 of 28 actually-merged branches were flagged safe).

After the survey, reclassify each branch deadbranch marked unmerged using the signals below in this order of authority. A branch confirmed by any of them is safe to delete; stop at the first one that answers.

  1. A MERGED PR for the branch head — authoritative. GitHub records that the squash landed the work at merge time, regardless of any later drift on the base:

    gh pr list --state all --head <branch> --json number,state,mergedAt
    

    A MERGED state means the branch's work landed (read state / mergedAt, never a merged field — see .claude/rules/gh-json-fields.md). --head <branch> is an exact per-branch query, so it is unaffected by gh pr list's 30-item default page — never build a branch→PR map from a repo-wide gh pr list --limit N, which silently drops every PR outside the window and misclassifies those branches as "no PR".

  2. git cherry — patch-equivalence, which survives squash-merge and cherry-pick even after the base drifts:

    git cherry <base> <branch>   # every commit marked '-' => already upstream
    
  3. merge-tree — a positive-containment shortcut only (git ≥ 2.38). When the merged tree equals the base's tree, the branch contributes nothing not already present:

    base_tree=$(git rev-parse <base>^{tree})
    merged=$(git merge-tree --write-tree <base> <branch>)
    [ "$merged" = "$base_tree" ] && echo "MERGED (contained in base)"
    

    A match proves containment; a non-match proves nothing. Once the base drifts over the same files, merging an already-landed branch back would re-introduce its older versions, so the trees differ for work that fully landed. Never use this as the primary signal: doing so is what made the just -g branch-audit recipe's REVIEW bucket ~90% false on two real repos (issue #2268). Treat a non-match as "review", never as "unmerged".

Report a "squash-merged (reclassified safe)" group alongside deadbranch's own merged set so the user sees the true safe-to-delete count. Branches matching none of the signals stay in the keep/review group — do not sweep them with --force, which also deletes genuinely-unmerged work.

This reclassification is read-only — it never deletes. Deletion still flows through the Step 2 dry-run and Step 3 confirmation below, with the squash-merged branches included in the set the user approves.

Step 2: Dry-run preview

Always run a dry-run before any real deletion:

deadbranch clean --dry-run [--days N] [--local|--remote] [--force]

Report the dry-run output. If nothing would be deleted (e.g. all stale branches are unmerged and --force was not set), explain why and offer to re-run with --force if the user wants to include unmerged branches.

Step 3: Confirm and clean

After the dry-run, ask the user what to do:

AskUserQuestion("Proceed with branch deletion?", options=[
  "Yes — delete all shown branches",
  "Interactive TUI — let me pick",
  "No — survey only, skip deletion"
])
  • Yes: run deadbranch clean [options]
  • Interactive: run deadbranch clean --interactive [options] (note: the TUI requires a real terminal — warn the user that this opens a full-screen interface they must interact with directly via ! deadbranch clean --interactive)
  • No: print the summary and stop

If the user passed --interactive in $ARGUMENTS, skip the question and go straight to TUI mode. If --yes was explicitly passed, skip confirmation and delete directly.

Step 4: Report

After deletion, confirm with:

deadbranch backup list

Report how many branches were deleted and that their SHAs are backed up (recoverable via deadbranch backup restore <branch>).

Safety Rules

SituationAction
--force requestedShow extra warning: "This deletes unmerged branches. These are NOT recoverable from the remote." before dry-run
--remote requestedNote that remote deletion affects everyone on the team
Branch is WIP / draftdeadbranch skips these automatically; mention it in the report
Protected branches (main/master/develop/staging/production)deadbranch skips these automatically

Recovery

If the user wants to recover a deleted branch:

deadbranch backup list              # see what's saved
deadbranch backup restore <branch>  # restore by name

Backups store the branch tip SHA. Restoration creates the branch locally pointing at that SHA.

Common Invocations

# Default survey + dry-run + confirm
deadbranch list
deadbranch stats
deadbranch clean --dry-run

# Local branches only, 60-day threshold
deadbranch list --local --days 60
deadbranch clean --local --days 60 --dry-run

# Remote branches — team-visible action, ask twice
deadbranch list --remote
deadbranch clean --remote --dry-run

# Interactive TUI (run by user in their terminal)
! deadbranch clean --interactive

# Non-interactive automation (CI / scripts)
deadbranch clean --yes --merged

Interactive TUI Note

deadbranch clean --interactive opens a full-screen TUI with:

  • Vim-style navigation (hjkl, / to fuzzy search)
  • Visual range selection
  • 6-column sort
  • Space to toggle individual branches for deletion

Claude Code cannot operate the TUI — instruct the user to run it themselves with the ! prefix: ! deadbranch clean --interactive.

Configuration

deadbranch reads ~/.deadbranch/config.toml. View with:

deadbranch config show

Key settings:

SettingDefaultDescription
general.default_days30Default age threshold
branches.default_branchauto-detectDefault branch name
branches.protectedmain, master, develop, staging, productionNever deleted
branches.exclude_patternswip/, draft/, */wip, */draftPattern-excluded

To change a setting:

deadbranch config set general.default_days 60

Installation (if not present)

Check availability first:

which deadbranch || echo "not installed"

Install via cargo (preferred — already in mise/Rust toolchain):

cargo install deadbranch

Or via Homebrew:

brew install armgabrielyan/tap/deadbranch

Agentic Optimizations

ContextCommand
Quick health statsdeadbranch stats
List stale (machine-survey)deadbranch list --days 30
Safe preview before deletedeadbranch clean --dry-run
Non-interactive cleanupdeadbranch clean --yes --merged
Recover a deleted branchdeadbranch backup restore <branch>
发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

Sep 24, 2026

分类

未分类

许可证

MIT

源路径

git-plugin/skills/deadbranch

默认分支

main

最新提交

1668324

Tree SHA

b2d4cc3