Git清理Skill git-cleanup

这个技能用于安全地分析和清理本地Git分支和工作树,通过分类为合并、squash合并、被取代或活跃工作,帮助开发者高效管理版本控制仓库。关键词:Git清理、分支管理、worktree、DevOps、版本控制、代码管理、git

DevOps 0 次安装 0 次浏览 更新于 3/14/2026

名称:git-cleanup 描述:“安全地分析和清理本地git分支和工作树,通过将它们分类为合并、squash合并、被取代或活跃工作。” 禁用模型调用:true 允许的工具:

  • Bash
  • Read
  • Grep
  • AskUserQuestion

Git清理

安全地清理积累的git工作树和本地分支,通过将它们分类为:安全可删除(合并)、潜在相关(相似主题)和活跃工作(保留)。

何时使用

  • 当用户积累了许多本地分支和工作树时
  • 当分支已合并但未在本地清理时
  • 当远程分支已删除但本地跟踪分支仍存在时

何时不使用

  • 不要用于远程分支管理(这只是本地清理)
  • 不要用于仓库维护任务如gc或prune
  • 不设计用于无头或非交互式自动化(需要在两个关卡进行用户确认)

核心原则:安全第一

未经明确用户确认,永不删除任何内容。 此技能使用门控工作流,用户在每次步骤前必须批准任何破坏性操作。

关键实现注意事项

Squash合并的分支需要强制删除

重要: git branch -d 总会失败于squash合并的分支,因为git无法检测到工作已被纳入。这是预期行为,不是错误。

当识别分支为squash合并时:

  • 计划从一开始使用 git branch -D(强制删除)
  • 不要先尝试 git branch -d 然后再问 -D - 这浪费用户确认
  • 在确认步骤中,为squash合并的分支显示 git branch -D

在分类前分组相关分支

强制: 在分类单个分支前,按名称前缀分组它们:

# 从分支名中提取公共前缀
# 例如,feature/auth-*, feature/api-*, fix/login-*

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

  1. 按提交日期找到最旧和最新的
  2. 检查较新分支是否包含较旧分支的提交
  3. 检查哪些PR合并了每个分支的工作
  4. 确定较旧分支是否被取代

一起呈现相关分支,并给出清晰建议,不要分散在各个类别中。

彻底调查PR历史

不要依赖简单关键词匹配。对于 [gone] 分支:

# 1. 获取分支的提交,这些不在默认分支中
git log --oneline "$default_branch".."$branch"

# 2. 在默认分支中搜索包含此工作的PR
# 通过:分支名、提交消息关键词、PR号搜索
git log --oneline "$default_branch" | grep -iE "(branch-name|keyword|#[0-9]+)"

# 3. 对于相关分支组,追踪哪些PR合并了哪些工作
git log --oneline "$default_branch" | grep -iE "(#[0-9]+)" | head -20

工作流

阶段1:全面分析

在分类前收集所有信息:

# 获取默认分支名
default_branch=$(git symbolic-ref refs/remotes/origin/HEAD \
  2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo "main")

# 受保护分支 - 从不分析或删除
protected='^(main|master|develop|release/.*)$'

# 列出所有本地分支及跟踪信息
git branch -vv

# 列出所有工作树
git worktree list

# 获取并prune以同步远程状态
git fetch --prune

# 获取合并到默认分支的分支
git branch --merged "$default_branch"

# 获取最近的PR合并历史(检测squash合并)
git log --oneline "$default_branch" | grep -iE "#[0-9]+" | head -30

# 对于每个非受保护分支,获取唯一提交和同步状态
for branch in $(git branch --format='%(refname:short)' \
  | grep -vE "$protected"); do
  echo "=== $branch ==="
  echo "不在 $default_branch 中的提交:"
  git log --oneline "$default_branch".."$branch" 2>/dev/null \
    | head -5
  echo "未推送到远程的提交:"
  git log --oneline "origin/$branch".."$branch" 2>/dev/null \
    | head -5 || echo "(无远程跟踪)"
done

注意分支名: Git分支名可以包含破坏shell扩展的字符。在命令中始终引用 "$branch"

阶段2:分组相关分支

在个别分类前执行此操作。

通过共享前缀识别分支组:

# 列出分支并提取前缀
git branch --format='%(refname:short)' | sed 's/-[^-]*$//' | sort | uniq -c | sort -rn

对于每组有2+分支:

  1. 比较提交历史 - 哪些分支包含其他分支的提交?
  2. 查找合并证据 - 哪些PR合并了此组的工作?
  3. 识别“最终”分支 - 通常是最新或最完整的
  4. 标记被取代分支 - 旧迭代,其工作在main中或在较新分支中

被取代需要证据,不仅仅是共享前缀:

  • 一个PR合并工作到main,OR
  • 较新分支包含所有旧分支的提交
  • 仅名称前缀不足 – 相似命名的分支可能包含独立工作

示例分析 feature/api-* 分支:

### 相关分支组:feature/api-*

| 分支 | 提交 | PR合并 | 状态 |
|--------|---------|-----------|--------|
| feature/api | 12 | #29(初始API) | 被取代 - 工作在main |
| feature/api-v2 | 8 | #45(API改进) | 被取代 - 工作在main |
| feature/api-refactor | 5 | #67(重构) | 被取代 - 工作在main |
| feature/api-final | 4 | 未找到 | 被上述PR取代 |

**建议:** 所有4个分支可删除 - 工作通过PR #29, #45, #67合并

阶段3:分类剩余分支

对于不在相关组的分支,个别分类:

分支是否合并到默认分支?
├─ 是 → SAFE_TO_DELETE(使用 -d)
└─ 否 → 是否跟踪远程?
        ├─ 是 → 远程删除?([gone])
        │        ├─ 是 → 工作是否squash合并?(检查main中的PR)
        │        │        ├─ 是 → SQUASH_MERGED(使用 -D)
        │        │        └─ 否 → REMOTE_GONE(需要审查)
        │        └─ 否 → 本地是否领先于远程?(检查:git log origin/<branch>..<branch>)
        │                ├─ 是(有输出) → UNPUSHED_WORK(保留)
        │                └─ 否(空输出) → SYNCED_WITH_REMOTE(保留)
        └─ 否 → 有唯一提交?
                ├─ 是 → LOCAL_WORK(保留)
                └─ 否 → SAFE_TO_DELETE(使用 -d)

类别定义:

类别 含义 删除命令
SAFE_TO_DELETE 合并到默认分支 git branch -d
SQUASH_MERGED 工作通过squash合并纳入 git branch -D
SUPERSEDED 属于组的一部分,工作通过PR或较新分支验证在main中 git branch -D
REMOTE_GONE 远程删除,工作未在main中找到 需要审查
UNPUSHED_WORK 有未推送到远程的提交 保留
LOCAL_WORK 未跟踪分支但有唯一提交 保留
SYNCED_WITH_REMOTE 与远程同步 保留

阶段4:脏状态检测

检查所有工作树和当前目录是否有未提交更改:

# 对于每个工作树路径
git -C <worktree-path> status --porcelain

# 对于当前目录
git status --porcelain

突出显示警告:

警告:../proj-auth 有未提交更改:
  M  src/auth.js
  ?? new-file.txt

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

门1:呈现完整分析

在一个综合视图中呈现所有内容。将相关分支分组:

## Git清理分析

### 相关分支组

**组:feature/api-*(4个分支)**
| 分支 | 状态 | 证据 |
|--------|--------|----------|
| feature/api | 被取代 | 工作在PR #29合并 |
| feature/api-v2 | 被取代 | 工作在PR #45合并 |
| feature/api-refactor | 被取代 | 工作在PR #67合并 |
| feature/api-final | 被取代 | 旧迭代,分歧 |

建议:删除所有4个(工作在main中)

---

### 个别分支

**安全删除(合并,使用 -d)**
| 分支 | 合并到 |
|--------|-------------|
| fix/typo | main |

**安全删除(squash合并,需要 -D)**
| 分支 | 合并为 |
|--------|-----------|
| feature/login | PR #42 |

**需要审查(远程[gone],未找到PR)**
| 分支 | 最后提交 |
|--------|-------------|
| experiment/old | abc1234 "WIP something" |

**保留(活跃工作)**
| 分支 | 状态 |
|--------|--------|
| wip/new-feature | 5个未推送提交 |

### 工作树
| 路径 | 分支 | 状态 |
|------|--------|--------|
| ../proj-auth | feature/auth | STALE(合并) |

---

**总结:**
- 4个相关分支(feature/api-*)- 建议删除所有
- 1个合并分支 - 安全删除
- 1个squash合并分支 - 安全删除
- 1个需要审查
- 1个保留

您想清理哪些?

使用AskUserQuestion,带有清晰选项:

  • 删除所有建议的(组 + 合并 + squash合并)
  • 删除特定组/类别
  • 让我选择个别分支

用户响应前不要继续。

门2:最终确认与确切命令

显示将运行的确切命令,带有正确标志:

我将执行:

# 合并分支(安全删除)
git branch -d fix/typo

# Squash合并分支(强制删除 - 工作在main中通过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 worktree remove ../proj-auth

确认?(是/否)

重要: 这是删除所需的唯一确认。如果 -D 是必需的,不要添加额外确认。

阶段5:执行

作为单独命令运行每个删除,以便部分失败不阻止剩余删除。报告每个结果:

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

如果删除失败,报告错误并继续剩余删除。

阶段6:报告

## 清理完成

### 删除的
- fix/typo
- feature/login
- feature/api
- feature/api-v2
- feature/api-refactor
- feature/api-final
- 工作树:../proj-auth

### 剩余的(4个分支)
| 分支 | 状态 |
|--------|--------|
| main | 当前 |
| wip/new-feature | 活跃工作 |
| experiment/old | 需要审查 |

安全规则

  1. 永不自动调用 - 仅当用户显式使用 /git-cleanup 时运行
  2. 仅两个确认门 - 分析审查,然后删除确认
  3. 使用正确删除命令 - -d 用于合并, -D 用于squash合并/被取代
  4. 永不触及受保护分支 - main, master, develop, release/*(编程过滤)
  5. 阻止脏工作树删除 - 未经明确数据丢失确认拒绝
  6. 分组相关分支 - 不要将它们分散在各个类别中

要拒绝的理性化

这些是导致数据丢失的常见快捷方式。拒绝它们:

理性化 为什么它是错误的
“分支很旧,可能安全删除” 年龄不指示合并状态。旧分支可能包含未合并工作。
“如果需要,我可以从reflog恢复” Reflog条目过期。用户通常不知道如何使用reflog。不要依赖它作为安全网。
“只是一个本地分支,不重要” 本地分支可能包含唯一的工作副本,未推送到任何地方。
“PR已合并,所以分支安全” Squash合并不保留分支历史。验证特定提交已纳入。
“我只删除所有 [gone] 分支” [gone] 仅意味着远程已删除。本地分支可能有未推送提交。
“用户似乎想删除一切” 总是首先呈现分析。让用户选择删除什么。
“分支有不在main中的提交,所以有未推送工作” “不在main中” ≠ “未推送”。分支可以与远程同步但未合并到main。始终检查 git log origin/<branch>..<branch>