clean-branches

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Clean Git Branches

清理Git分支

Safely remove merged and stale git branches with confirmation.
安全地移除已合并和陈旧的Git分支,并需确认操作。

Process

流程

If
$ARGUMENTS
is provided, treat it as a glob pattern to filter branch candidates (e.g.,
feature/*
) and pass it to the candidate script in Step 1.
1. Fetch latest state
bash
git fetch --all --prune
If fetch fails (no remotes configured), note remote data is unavailable and continue with local analysis only.
2. Identify candidates
Run the candidate detection script, passing the optional pattern filter:
bash
bash ${CLAUDE_PLUGIN_ROOT}/skills/clean-branches/scripts/find-candidates.sh "$PATTERN"
The script outputs two labeled sections (
=== MERGED ===
and
=== STALE ===
), one branch per line. Branches carry two optional annotations:
  • [worktree:/path/to/wt]
    — branch is checked out in a worktree
  • [squash-merged]
    — branch was merged via squash or rebase PR; git does not recognize it as merged locally (requires force-delete in Step 5)
Parse each section into its own list, preserving both annotations.
After parsing, apply these worktree rules before building the candidate lists. Process the MERGED list first, then the STALE list — a branch that appears in both (merged AND older than 30 days) is governed by the MERGED rule only; skip it when processing STALE.
  • Stale branch + active worktree → move immediately to the blocked list, regardless of worktree state. A stale branch with a live worktree may still have in-progress work — never offer it for cleanup. Skip branches already classified as merged.
  • Merged branch + active worktree → check whether the worktree is clean:
    bash
    git -C /path/to/wt status --porcelain
    If the command returns any output (uncommitted changes), move to the blocked list. If clean, keep in the merged candidate list with the worktree annotation.
3. Present results
Display branches in four groups:
  • Merged (safe to delete) — branches fully merged into base; those with a clean worktree show "(+ worktree at /path)"
  • Stale (no recent commits, no active worktree) — only branches without a worktree appear here
  • Protected (never touch) — main, master, develop, release/*
  • Blocked — branches skipped because they have an active worktree with uncommitted work, or are stale with any active worktree; list each with its worktree path so the user knows what to resolve manually
Do NOT say "will be removed" for worktrees — removal is gated on confirmation in Step 4. If both the merged and stale candidate lists are empty, report "No branches to clean" and stop.
4. Confirm before deletion
Use AskUserQuestion. For merged branches that carry a worktree annotation, the confirmation option must name both the branch and its worktree path — the user is authorizing removal of both in one selection.
Structure:
  • Header: "Branch cleanup"
  • For merged branches: one option per branch. If the branch has a worktree: label = "branch-name + worktree", description = "Removes branch and worktree at /path". If the branch is squash/rebase-merged: append "(squash/rebase PR — force delete)" to the description so the user knows
    -D
    will be used. If no worktree and not squash-merged: label = branch name, description = "Removes local branch". Include a "Keep all merged branches" fallback. If there are multiple candidates with no worktrees, a "Delete all N" batch option is acceptable.
  • For stale branches: use multiSelect:true. Each option: label = branch name, description = age. (No stale branch with a worktree will appear here — they were moved to blocked in Step 2.)
  • Always include a "Skip — keep all" option
The user selecting a branch-with-worktree option is explicit authorization to remove both. Never remove a worktree that was not explicitly included in a confirmed selection.
5. Execute deletion
Delete only what the user confirmed. For each confirmed branch:
  1. If the branch has a
    [worktree:/path]
    annotation, remove the worktree first:
    bash
    git worktree remove /path/to/wt
    If the command fails (the worktree acquired changes in the window between Step 2 and now), report the error and skip that branch — do not force-remove.
  2. Then delete the branch:
    • Regular merged (no
      [squash-merged]
      annotation):
      git branch -d <branch-name>
    • Squash/rebase-merged (
      [squash-merged]
      annotation):
      git branch -D <branch-name>
    -D
    is required for squash/rebase-merged branches because git does not recognise their commits as merged into base — using
    -d
    will fail. The
    [squash-merged]
    annotation on a confirmed selection is explicit user authorisation to force-delete.
  3. After local deletions are complete, offer remote cleanup:
    Use AskUserQuestion with multiSelect:true listing every branch that was successfully deleted locally and has a known remote (
    git ls-remote --heads origin <branch-name>
    to verify). Let the user select which remotes to also delete. If none have a remote, skip this step.
    For each selected remote:
    bash
    git push origin --delete <branch-name>
若提供了
$ARGUMENTS
,将其视为用于筛选分支候选的 glob 模式(例如:
feature/*
),并传递给步骤1中的候选脚本。
1. 获取最新状态
bash
git fetch --all --prune
若拉取失败(未配置远程仓库),则记录远程数据不可用,仅继续本地分析。
2. 识别候选分支
运行候选分支检测脚本,传入可选的模式筛选参数:
bash
bash ${CLAUDE_PLUGIN_ROOT}/skills/clean-branches/scripts/find-candidates.sh "$PATTERN"
脚本会输出两个带标签的区块(
=== MERGED ===
=== STALE ===
),每行一个分支。分支带有两个可选注释:
  • [worktree:/path/to/wt]
    — 分支已在工作树中检出
  • [squash-merged]
    — 分支通过 squash 或 rebase PR 合并;git 本地无法识别其已合并(步骤5中需要强制删除)
将每个区块解析为独立列表,保留所有注释。
解析完成后,在构建候选列表前应用以下工作树规则。先处理MERGED列表,再处理STALE列表——同时出现在两个列表中的分支(已合并且超过30天)仅遵循MERGED规则;处理STALE列表时跳过该分支。
  • 陈旧分支 + 活跃工作树 → 直接移至已阻止列表,无论工作树状态如何。带有活跃工作树的陈旧分支可能仍有进行中的工作——绝不能将其列为清理候选。跳过已归类为已合并的分支。
  • 已合并分支 + 活跃工作树 → 检查工作树是否干净:
    bash
    git -C /path/to/wt status --porcelain
    若命令返回任何输出(存在未提交更改),则移至已阻止列表。若工作树干净,则保留在已合并候选列表中并附带工作树注释。
3. 展示结果
将分支分为四组展示:
  • 已合并(可安全删除)——已完全合并到基准分支的分支;带有干净工作树的分支会显示"(+ 工作树位于 /path)"
  • 陈旧(近期无提交,无活跃工作树)——仅显示无工作树的分支
  • 受保护(禁止操作)——main、master、develop、release/*
  • 已阻止——因存在带未提交更改的活跃工作树,或陈旧分支带有任何活跃工作树而被跳过的分支;列出每个分支及其工作树路径,以便用户手动解决问题
对于工作树分支,不要说"将被移除"——移除操作需在步骤4中获得确认。若已合并和陈旧候选列表均为空,则报告"无分支可清理"并终止流程。
4. 删除前确认
使用AskUserQuestion。对于带有工作树注释的已合并分支,确认选项必须同时包含分支名称和工作树路径——用户需一次性授权移除两者。
结构:
  • 标题:"分支清理"
  • 已合并分支:每个分支对应一个选项。若分支带有工作树:标签 = "分支名称 + 工作树",描述 = "移除分支及位于 /path 的工作树"。若分支是 squash/rebase 合并的:在描述后追加"(squash/rebase PR — 强制删除)",让用户知晓将使用
    -D
    参数。若无工作树且非 squash 合并:标签 = 分支名称,描述 = "移除本地分支"。需包含"保留所有已合并分支"的备选选项。若存在多个无工作树的候选分支,可提供"删除全部N个"的批量选项。
  • 陈旧分支:使用multiSelect:true。每个选项:标签 = 分支名称,描述 = 存在时长。(此处不会出现带有工作树的陈旧分支——它们已在步骤2中移至已阻止列表。)
  • 始终包含"跳过——保留所有分支"选项
用户选择带工作树的分支选项即表示明确授权移除两者。绝不能移除未在确认选择中明确包含的工作树。
5. 执行删除
仅删除用户确认的分支。对于每个确认的分支:
  1. 若分支带有
    [worktree:/path]
    注释,先移除工作树:
    bash
    git worktree remove /path/to/wt
    若命令失败(在步骤2到当前期间工作树产生了更改),则报告错误并跳过该分支——不要强制移除。
  2. 然后删除分支:
    • 常规已合并分支(无
      [squash-merged]
      注释):
      git branch -d <branch-name>
    • Squash/rebase合并分支(带有
      [squash-merged]
      注释):
      git branch -D <branch-name>
    对于squash/rebase合并的分支,必须使用
    -D
    ,因为git无法识别其提交已合并到基准分支——使用
    -d
    会失败。确认选择中的
    [squash-merged]
    注释即表示用户明确授权强制删除。
  3. 本地删除完成后,提供远程清理选项:
    使用AskUserQuestion并设置multiSelect:true,列出所有已成功本地删除且存在对应远程分支的分支(通过
    git ls-remote --heads origin <branch-name>
    验证)。让用户选择要删除的远程分支。若没有对应远程分支,则跳过此步骤。
    对于每个选中的远程分支:
    bash
    git push origin --delete <branch-name>

Output

输出

Summary of actions taken:
  • Branches deleted (local)
  • Branches deleted (remote, if requested)
  • Branches kept
  • Any errors encountered
操作总结:
  • 已删除的分支(本地)
  • 已删除的分支(远程,若用户要求)
  • 保留的分支
  • 遇到的任何错误