git-cleanup

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Git Cleanup

Git 清理

Safely clean up accumulated git worktrees and local branches by categorizing them into: safely deletable (merged), potentially related (similar themes), and active work (keep).
通过将累积的git工作树和本地分支归类为可安全删除(已合并)、潜在相关(主题相似)和活跃工作(保留),来安全地清理它们。

When to Use

适用场景

  • When the user has accumulated many local branches and worktrees
  • When branches have been merged but not cleaned up locally
  • When remote branches have been deleted but local tracking branches remain
  • 当用户累积了大量本地分支和工作树时
  • 当分支已合并但未在本地清理时
  • 当远程分支已被删除但本地跟踪分支仍存在时

When NOT to Use

不适用场景

  • Do not use for remote branch management (this is local cleanup only)
  • Do not use for repository maintenance tasks like gc or prune
  • Not designed for headless or non-interactive automation (requires user confirmations at two gates)
  • 不要用于远程分支管理(此工具仅用于本地清理)
  • 不要用于仓库维护任务如gc或prune
  • 不适合无头或非交互式自动化(需要在两个关卡获得用户确认)

Core Principle: SAFETY FIRST

核心原则:安全第一

Never delete anything without explicit user confirmation. This skill uses a gated workflow where users must approve each step before any destructive action.
绝不未经用户明确确认就删除任何内容。 此技能采用 gated 工作流,用户必须在每一步操作前批准,才能执行任何破坏性动作。

Critical Implementation Notes

关键实现说明

Squash-Merged Branches Require Force Delete

Squash合并分支需要强制删除

IMPORTANT:
git branch -d
will ALWAYS fail for squash-merged branches because git cannot detect that the work was incorporated. This is expected behavior, not an error.
When you identify a branch as squash-merged:
  • Plan to use
    git branch -D
    (force delete) from the start
  • Do NOT try
    git branch -d
    first and then ask again for
    -D
    - this wastes user confirmations
  • In the confirmation step, show
    git branch -D
    for squash-merged branches
重要提示:
git branch -d
对squash合并的分支总会执行失败,因为git无法检测到工作内容已被合并。这是预期行为,并非错误。
当你识别出某个分支是squash合并时:
  • 从一开始就计划使用
    git branch -D
    (强制删除)
  • 不要先尝试
    git branch -d
    然后再要求使用
    -D
    ——这会浪费用户的确认步骤
  • 在确认步骤中,为squash合并的分支显示
    git branch -D
    命令

Group Related Branches BEFORE Categorization

在分类前先对相关分支分组

MANDATORY: Before categorizing individual branches, group them by name prefix:
bash
undefined
强制性要求: 在对单个分支进行分类之前,先按名称前缀对它们进行分组:
bash
undefined

Extract common prefixes from branch names

Extract common prefixes from branch names

e.g., feature/auth-, feature/api-, fix/login-*

e.g., feature/auth-, feature/api-, fix/login-*


Branches sharing a prefix (e.g., `feature/api`, `feature/api-v2`, `feature/api-refactor`) are almost certainly related iterations. Analyze them as a group:

1. Find the oldest and newest by commit date
2. Check if newer branches contain commits from older ones
3. Check which PRs merged work from each
4. Determine if older branches are superseded

Present related branches together with a clear recommendation, not scattered across categories.

共享前缀的分支(例如 `feature/api`、`feature/api-v2`、`feature/api-refactor`)几乎可以肯定是相关的迭代版本。将它们作为一个组进行分析:

1. 根据提交日期找出最早和最新的分支
2. 检查较新的分支是否包含旧分支的提交
3. 检查哪些PR合并了每个分支的工作内容
4. 判断旧分支是否已被取代

将相关分支放在一起展示并给出明确建议,不要分散到不同分类中。

Thorough PR History Investigation

全面调查PR历史

Don't rely on simple keyword matching. For
[gone]
branches:
bash
undefined
不要依赖简单的关键词匹配。对于标记为
[gone]
的分支:
bash
undefined

1. Get the branch's commits that aren't in default branch

1. Get the branch's commits that aren't in default branch

git log --oneline "$default_branch".."$branch"
git log --oneline "$default_branch".."$branch"

2. Search default branch for PRs that incorporated this work

2. Search default branch for PRs that incorporated this work

Search by: branch name, commit message keywords, PR numbers

Search by: branch name, commit message keywords, PR numbers

git log --oneline "$default_branch" | grep -iE "(branch-name|keyword|#[0-9]+)"
git log --oneline "$default_branch" | grep -iE "(branch-name|keyword|#[0-9]+)"

3. For related branch groups, trace which PRs merged which work

3. For related branch groups, trace which PRs merged which work

git log --oneline "$default_branch" | grep -iE "(#[0-9]+)" | head -20
undefined
git log --oneline "$default_branch" | grep -iE "(#[0-9]+)" | head -20
undefined

Workflow

工作流

Phase 1: Comprehensive Analysis

阶段1:全面分析

Gather ALL information upfront before any categorization:
bash
undefined
在进行任何分类之前,先收集所有信息:
bash
undefined

Get default branch name

Get default branch name

default_branch=$(git symbolic-ref refs/remotes/origin/HEAD
2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main")
default_branch=$(git symbolic-ref refs/remotes/origin/HEAD
2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main")

Protected branches - never analyze or delete

Protected branches - never analyze or delete

protected='^(main|master|develop|release/.*)$'
protected='^(main|master|develop|release/.*)$'

List all local branches with tracking info

List all local branches with tracking info

git branch -vv
git branch -vv

List all worktrees

List all worktrees

git worktree list
git worktree list

Fetch and prune to sync remote state

Fetch and prune to sync remote state

git fetch --prune
git fetch --prune

Get merged branches (into default branch)

Get merged branches (into default branch)

git branch --merged "$default_branch"
git branch --merged "$default_branch"

Get recent PR merge history (squash-merge detection)

Get recent PR merge history (squash-merge detection)

git log --oneline "$default_branch" | grep -iE "#[0-9]+" | head -30
git log --oneline "$default_branch" | grep -iE "#[0-9]+" | head -30

For EACH non-protected branch, get unique commits and sync status

For EACH non-protected branch, get unique commits and sync status

for branch in $(git branch --format='%(refname:short)'
| grep -vE "$protected"); do echo "=== $branch ===" echo "Commits not in $default_branch:" git log --oneline "$default_branch".."$branch" 2>/dev/null
| head -5 echo "Commits not pushed to remote:" git log --oneline "origin/$branch".."$branch" 2>/dev/null
| head -5 || echo "(no remote tracking)" done

**Note on branch names:** Git branch names can contain characters that break shell expansion. Always quote `"$branch"` in commands.
for branch in $(git branch --format='%(refname:short)'
| grep -vE "$protected"); do echo "=== $branch ===" echo "Commits not in $default_branch:" git log --oneline "$default_branch".."$branch" 2>/dev/null
| head -5 echo "Commits not pushed to remote:" git log --oneline "origin/$branch".."$branch" 2>/dev/null
| head -5 || echo "(no remote tracking)" done

**关于分支名称的注意事项:** Git分支名称可能包含会破坏shell展开的字符。在命令中始终用引号包裹 `"$branch"`。

Phase 2: Group Related Branches

阶段2:对相关分支分组

Do this BEFORE individual categorization.
Identify branch groups by shared prefixes:
bash
undefined
在对单个分支分类之前完成此步骤。
通过共享前缀识别分支组:
bash
undefined

List branches and extract prefixes

List branches and extract prefixes

git branch --format='%(refname:short)' | sed 's/-[^-]*$//' | sort | uniq -c | sort -rn

For each group with 2+ branches:

1. **Compare commit histories** - Which branches contain commits from others?
2. **Find merge evidence** - Which PRs incorporated work from this group?
3. **Identify the "final" branch** - Usually the most recent or most complete
4. **Mark superseded branches** - Older iterations whose work is in main or in a newer branch

**SUPERSEDED requires evidence, not just shared prefix:**
- A PR merged the work into main, OR
- A newer branch contains all commits from the older branch
- Name prefix alone is NOT sufficient — similarly named branches may contain independent work

Example analysis for `feature/api-*` branches:

```markdown
git branch --format='%(refname:short)' | sed 's/-[^-]*$//' | sort | uniq -c | sort -rn

对于每个包含2个及以上分支的组:

1. **比较提交历史** - 哪些分支包含其他分支的提交?
2. **查找合并证据** - 哪些PR合并了该组的工作内容?
3. **确定"最终"分支** - 通常是最新或最完整的分支
4. **标记已被取代的分支** - 工作内容已通过PR合并到主分支或存在于较新分支中的旧迭代版本

**已被取代的判断需要证据,不能仅靠共享前缀:**
- PR已将工作内容合并到主分支,或者
- 较新的分支包含旧分支的所有提交
- 仅名称前缀不足以判断——名称相似的分支可能包含独立的工作内容

`feature/api-*`分支组的分析示例:

```markdown

Related Branch Group: feature/api-*

相关分支组:feature/api-*

BranchCommitsPR MergedStatus
feature/api12#29 (initial API)Superseded - work in main
feature/api-v28#45 (API improvements)Superseded - work in main
feature/api-refactor5#67 (refactor)Superseded - work in main
feature/api-final4None foundSuperseded by above PRs
Recommendation: All 4 branches can be deleted - work incorporated via PRs #29, #45, #67
undefined
分支提交数合并的PR状态
feature/api12#29 (初始API)已被取代 - 工作内容已合并到主分支
feature/api-v28#45 (API优化)已被取代 - 工作内容已合并到主分支
feature/api-refactor5#67 (重构)已被取代 - 工作内容已合并到主分支
feature/api-final4未找到被上述PR的工作内容取代
建议: 可删除全部4个分支 - 工作内容已合并到主分支
undefined

Phase 3: Categorize Remaining Branches

阶段3:对剩余分支进行分类

For branches NOT in a related group, categorize individually:
Is branch merged into default branch?
├─ YES → SAFE_TO_DELETE (use -d)
└─ NO → Is tracking a remote?
        ├─ YES → Remote deleted? ([gone])
        │        ├─ YES → Was work squash-merged? (check main for PR)
        │        │        ├─ YES → SQUASH_MERGED (use -D)
        │        │        └─ NO → REMOTE_GONE (needs review)
        │        └─ NO → Local ahead of remote? (check: git log origin/<branch>..<branch>)
        │                ├─ YES (has output) → UNPUSHED_WORK (keep)
        │                └─ NO (empty output) → SYNCED_WITH_REMOTE (keep)
        └─ NO → Has unique commits?
                ├─ YES → LOCAL_WORK (keep)
                └─ NO → SAFE_TO_DELETE (use -d)
Category definitions:
CategoryMeaningDelete Command
SAFE_TO_DELETEMerged into default branch
git branch -d
SQUASH_MERGEDWork incorporated via squash merge
git branch -D
SUPERSEDEDPart of a group, work verified in main via PR or in newer branch
git branch -D
REMOTE_GONERemote deleted, work NOT found in mainReview needed
UNPUSHED_WORKHas commits not pushed to remoteKeep
LOCAL_WORKUntracked branch with unique commitsKeep
SYNCED_WITH_REMOTEUp to date with remoteKeep
对于不属于任何相关组的分支,单独进行分类:
分支是否已合并到默认分支?
├─ 是 → 可安全删除(使用-d)
└─ 否 → 是否跟踪远程分支?
        ├─ 是 → 远程分支已删除?([gone])
        │        ├─ 是 → 工作内容是否通过squash合并?(检查主分支的PR)
        │        │        ├─ 是 → Squash合并(使用-D)
        │        │        └─ 否 → 远程已删除(需要审核)
        │        └─ 否 → 本地分支是否领先于远程?(检查: git log origin/<branch>..<branch>)
        │                ├─ 是(有输出)→ 未推送的工作内容(保留)
        │                └─ 否(无输出)→ 与远程同步(保留)
        └─ 否 → 是否有唯一提交?
                ├─ 是 → 本地工作内容(保留)
                └─ 否 → 可安全删除(使用-d)
分类定义:
分类含义删除命令
SAFE_TO_DELETE已合并到默认分支
git branch -d
SQUASH_MERGED工作内容通过squash合并被纳入
git branch -D
SUPERSEDED属于某个组,工作内容已通过PR或较新分支验证存在于主分支
git branch -D
REMOTE_GONE远程分支已删除,工作内容未在主分支找到需要审核
UNPUSHED_WORK包含未推送到远程的提交保留
LOCAL_WORK未被跟踪且包含唯一提交的分支保留
SYNCED_WITH_REMOTE与远程分支同步保留

Phase 4: Dirty State Detection

阶段4:脏状态检测

Check ALL worktrees and current directory for uncommitted changes:
bash
undefined
检查所有工作树和当前目录的未提交更改:
bash
undefined

For each worktree path

For each worktree path

git -C <worktree-path> status --porcelain
git -C <worktree-path> status --porcelain

For current directory

For current directory

git status --porcelain

**Display warnings prominently:**

```markdown
WARNING: ../proj-auth has uncommitted changes:
  M  src/auth.js
  ?? new-file.txt

These changes will be LOST if you remove this worktree.
git status --porcelain

**突出显示警告:**

```markdown
警告:../proj-auth 存在未提交的更改:
  M  src/auth.js
  ?? new-file.txt

如果删除此工作树,这些更改将丢失。

GATE 1: Present Complete Analysis

关卡1:展示完整分析结果

Present everything in ONE comprehensive view. Group related branches together:
markdown
undefined
在一个完整的视图中展示所有信息。将相关分支分组展示:
markdown
undefined

Git Cleanup Analysis

Git清理分析结果

Related Branch Groups

相关分支组

Group: feature/api- (4 branches)*
BranchStatusEvidence
feature/apiSupersededWork merged in PR #29
feature/api-v2SupersededWork merged in PR #45
feature/api-refactorSupersededWork merged in PR #67
feature/api-finalSupersededOlder iteration, diverged
Recommendation: Delete all 4 (work is in main)

组:feature/api-*(4个分支)
分支状态证据
feature/api已被取代工作内容通过PR #29合并
feature/api-v2已被取代工作内容通过PR #45合并
feature/api-refactor已被取代工作内容通过PR #67合并
feature/api-final已被取代旧迭代版本,已分叉
建议:删除全部4个分支(工作内容已在主分支)

Individual Branches

单个分支

Safe to Delete (merged with -d)
BranchMerged Into
fix/typomain
Safe to Delete (squash-merged, requires -D)
BranchMerged As
feature/loginPR #42
Needs Review ([gone] remotes, no PR found)
BranchLast Commit
experiment/oldabc1234 "WIP something"
Keep (active work)
BranchStatus
wip/new-feature5 unpushed commits
可安全删除(已合并,使用-d)
分支合并到
fix/typomain
可安全删除(squash合并,需要使用-D)
分支合并方式
feature/loginPR #42
需要审核([gone]远程分支,未找到PR)
分支最后一次提交
experiment/oldabc1234 "WIP something"
保留(活跃工作)
分支状态
wip/new-feature5个未推送的提交

Worktrees

工作树

PathBranchStatus
../proj-authfeature/authSTALE (merged)

Summary:
  • 4 related branches (feature/api-*) - recommend delete all
  • 1 merged branch - safe to delete
  • 1 squash-merged branch - safe to delete
  • 1 needs review
  • 1 to keep
Which would you like to clean up?

Use AskUserQuestion with clear options:
- Delete all recommended (groups + merged + squash-merged)
- Delete specific groups/categories
- Let me pick individual branches

**Do not proceed until user responds.**
路径分支状态
../proj-authfeature/auth已过期(已合并)

摘要:
  • 4个相关分支(feature/api-*)- 建议全部删除
  • 1个已合并分支 - 可安全删除
  • 1个squash合并分支 - 可安全删除
  • 1个需要审核
  • 1个需要保留
你想要清理哪些内容?

使用AskUserQuestion提供清晰选项:
- 删除所有推荐的内容(分支组 + 已合并 + squash合并)
- 删除特定分支组/分类
- 让我选择单个分支

**在用户回复前不要继续执行。**

GATE 2: Final Confirmation with Exact Commands

关卡2:使用精确命令进行最终确认

Show the EXACT commands that will run, with correct flags:
markdown
I will execute:
展示将执行的精确命令,包含正确的参数:
markdown
我将执行以下命令:

Merged branches (safe delete)

已合并分支(安全删除)

git branch -d fix/typo
git branch -d fix/typo

Squash-merged branches (force delete - work is in main via PRs)

Squash合并分支(强制删除 - 工作内容已通过PR合并到主分支)

git branch -D feature/login git branch -D feature/api git branch -D feature/api-v2 git branch -D feature/api-refactor git branch -D feature/api-final
git branch -D feature/login git branch -D feature/api git branch -D feature/api-v2 git branch -D feature/api-refactor git branch -D feature/api-final

Worktrees

工作树

git worktree remove ../proj-auth
Confirm? (yes/no)

**IMPORTANT:** This is the ONLY confirmation needed for deletion. Do not add extra confirmations if `-D` is required.
git worktree remove ../proj-auth
确认执行?(是/否)

**重要提示:** 这是删除操作所需的唯一确认步骤。如果需要使用`-D`,不要额外添加确认步骤。

Phase 5: Execute

阶段5:执行操作

Run each deletion as a separate command so partial failures don't block remaining deletions. Report the result of each:
bash
git branch -d fix/typo
git branch -D feature/login
git branch -D feature/api
git branch -D feature/api-v2
git branch -D feature/api-refactor
git branch -D feature/api-final
git worktree remove ../proj-auth
If a deletion fails, report the error and continue with remaining deletions.
将每个删除操作作为单独的命令运行,这样部分失败不会阻止剩余操作的执行。报告每个操作的结果:
bash
git branch -d fix/typo
git branch -D feature/login
git branch -D feature/api
git branch -D feature/api-v2
git branch -D feature/api-refactor
git branch -D feature/api-final
git worktree remove ../proj-auth
如果某个删除操作失败,报告错误并继续执行剩余操作。

Phase 6: Report

阶段6:生成报告

markdown
undefined
markdown
undefined

Cleanup Complete

清理完成

Deleted

已删除

  • fix/typo
  • feature/login
  • feature/api
  • feature/api-v2
  • feature/api-refactor
  • feature/api-final
  • Worktree: ../proj-auth
  • fix/typo
  • feature/login
  • feature/api
  • feature/api-v2
  • feature/api-refactor
  • feature/api-final
  • 工作树:../proj-auth

Remaining (4 branches)

剩余分支(4个)

BranchStatus
maincurrent
wip/new-featureactive work
experiment/oldneeds review
undefined
分支状态
main当前分支
wip/new-feature活跃工作
experiment/old需要审核
undefined

Safety Rules

安全规则

  1. Never invoke automatically - Only run when user explicitly uses
    /git-cleanup
  2. Two confirmation gates only - Analysis review, then deletion confirmation
  3. Use correct delete command -
    -d
    for merged,
    -D
    for squash-merged/superseded
  4. Never touch protected branches - main, master, develop, release/* (filtered programmatically)
  5. Block dirty worktree removal - Refuse without explicit data loss acknowledgment
  6. Group related branches - Don't scatter them across categories
  1. 绝不自动执行 - 仅当用户明确使用
    /git-cleanup
    时才运行
  2. 仅设置两个确认关卡 - 分析结果审核,然后是删除确认
  3. 使用正确的删除命令 - 已合并分支使用
    -d
    ,squash合并/已被取代分支使用
    -D
  4. 绝不触碰受保护分支 - main、master、develop、release/*(通过程序过滤)
  5. 阻止删除脏工作树 - 未经用户明确确认数据丢失风险,拒绝执行
  6. 对相关分支分组 - 不要将它们分散到不同分类中

Rationalizations to Reject

需拒绝的不合理操作理由

These are common shortcuts that lead to data loss. Reject them:
RationalizationWhy It's Wrong
"The branch is old, it's probably safe to delete"Age doesn't indicate merge status. Old branches may contain unmerged work.
"I can recover from reflog if needed"Reflog entries expire. Users often don't know how to use reflog. Don't rely on it as a safety net.
"It's just a local branch, nothing important"Local branches may contain the only copy of work not pushed anywhere.
"The PR was merged, so the branch is safe"Squash merges don't preserve branch history. Verify the specific commits were incorporated.
"I'll just delete all the
[gone]
branches"
[gone]
only means the remote was deleted. The local branch may have unpushed commits.
"The user seems to want everything deleted"Always present analysis first. Let the user choose what to delete.
"The branch has commits not in main, so it has unpushed work""Not in main" ≠ "not pushed". A branch can be synced with its remote but not merged to main. Always check
git log origin/<branch>..<branch>
.
这些常见的捷径会导致数据丢失,应拒绝:
不合理理由错误原因
"这个分支很旧了,删除应该没问题"分支的存在时间无法说明其合并状态。旧分支可能包含未合并的工作内容。
"如果需要,我可以从reflog恢复"Reflog条目会过期。用户通常不知道如何使用reflog。不要将其作为安全保障依赖。
"这只是本地分支,没什么重要的"本地分支可能包含唯一未推送到任何地方的工作内容副本。
"PR已合并,所以这个分支可以安全删除"Squash合并不会保留分支历史。需要验证特定提交是否已被纳入。
"我要删除所有
[gone]
分支"
[gone]
仅表示远程分支已删除。本地分支可能包含未推送的提交。
"用户似乎想要删除所有内容"始终先展示分析结果。让用户选择要删除的内容。
"这个分支有主分支没有的提交,所以有未推送的工作内容""不在主分支中"不等于"未推送"。分支可能已与远程同步但未合并到主分支。始终检查
git log origin/<branch>..<branch>