用途
commit-split 用于把一堆未提交的改动拆分成若干个原子化、便于审查的提交。它解决两个核心问题:一是用户不必手工对改动做上下文分类,分析和提出方案由该技能负责;二是结果必须可验证,即证明拆分前后没有一个字节的代码差异。
整体流程为:读取状态 → 分析改动 → 提出方案 → 用户确认 → 执行 → 验证无损。技能明确要求:在提出方案之前,不要让用户对任何内容进行分类;应先分析、给出方案,再让用户调整。
主要能力
第 0 步:读取状态
使用以下命令了解仓库状态:
git status --porcelain # 变更列表;已暂存/未暂存/未跟踪
git branch --show-current
git log --oneline -20 # 了解提交信息约定
git rev-parse HEAD # 记录起点(用于验证)
git diff HEAD | git patch-id --stable # 原始变更的指纹(用于无损验证)
遇到以下情况应停止:正在进行 merge/rebase,或存在冲突文件。此时报告状态并中止。
需要注意:
- 已暂存的改动——告知用户。拆分无法保留现有暂存状态,因此需先就“取消全部暂存并重新拆分整体”达成一致,然后运行
git restore --staged .。 - 未跟踪文件——将其纳入方案表格,但单独标记。如果它们看起来像构建垃圾(日志、临时文件),建议将其排除在提交之外。
第 1 步:分析改动
读取完整的 git diff HEAD 以及未跟踪文件的内容,然后识别逻辑单元。不要按文件名分组——必须阅读 diff 才能发现“一个文件里混了两种上下文”,而发现这一点正是该技能存在的意义。
分组规则,按优先级排序:
- 拆开就会坏的东西——新函数及其调用点、签名变更及其调用方、实现及其测试,放在同一个提交里。
- 意图不同,提交不同——feature / bugfix / refactor / config / docs 各自分开。
- 领域不同,提交不同——即使意图相同,不相关的模块也要分开。
当一个文件包含两种上下文时,标记为需要按 hunk 拆分。从 git log 中学习提交信息风格(前缀格式、语言、scope 写法)。
第 2 步:提出方案
以表格形式呈现分析结果。不要断言提交数量——给出推荐值以及粒度备选方案。
示例:
## Split plan (recommended: 3)
| # | Draft commit message | Target |
|---|---|---|
| 1 | feat(auth): add login attempt limit | auth/service.py, auth/tests/test_lockout.py |
| 2 | fix(payment): correct refund rounding | payment/service.py (hunks 1,3), payment/tests/... |
| 3 | chore: raise payment timeout | config/settings.py, payment/service.py (hunk 2) |
- Coarser (2): merge 2 and 3 as payment-related
- Finer (4): split the tests out of 1
对于需要按 hunk 拆分的文件,在表格中写明哪个 hunk 归入哪个提交。
第 3 步:用户确认
在可用时使用 AskUserQuestion。一次性问清所有问题:
- 粒度:推荐 N / 更粗 / 更细 / 自定义(通过 Other 自由输入)
- 仅当信息风格不明确时才询问信息风格(仓库约定清晰时跳过)
如果用户调整了边界,更新方案表格,再次展示,然后继续。在无法收集回答的非交互环境中,说明将按推荐方案继续并执行。
第 4 步:执行
按方案顺序逐组暂存并提交。
- 整组为完整文件:
git add <files> - 需要按 hunk 拆分的文件:使用随附脚本(
git add -p是交互式的,此处不可用)
python3 scripts/stage_hunks.py list <file> # 列出带编号的 hunk
python3 scripts/stage_hunks.py stage <file> 1 3 # 仅暂存 hunk 1 和 3
脚本路径相对于该技能的目录。list 只显示未暂存的 hunk,因此暂存其中一部分后,再次 list 会对剩余部分重新编号——需注意这一点。
使用已确认的草稿作为提交信息。如果 pre-commit 钩子修改了文件并失败(格式化工具),将修改后的内容重新加入该组并再次提交。
安全规则:
- 该技能只运行 add 和 commit。绝不 push、reset、stash 或 checkout。
- 如果运行中途出错,不要尝试自动恢复——报告当前状态并停止(拆分从不触碰工作区文件,因此即使最坏情况代码也仍在)。
- 撤销指引:若结果不佳或运行中断,要恢复起始状态,命令是
git reset --mixed <startHEAD>。提交会回退,工作区精确回到拆分前的未提交状态(不会丢失任何东西——文件内容保存在提交里)。不要自己运行此命令;只需在失败报告和最终报告中连同起始 HEAD 一起给出。
第 5 步:验证无损
提交完成后,进行证明并报告:
git status --porcelain # 是否有超出预期排除项的残留改动?
git diff <startHEAD> HEAD | git patch-id --stable # 是否与第 0 步的指纹一致?
git log --oneline <startHEAD>..HEAD # 提交是否按计划落地?
patch-id 一致即可证明拆分前后变更内容完全相同。如果某些文件被有意排除在提交之外,patch-id 会不同——此时应改为验证残留改动是否与排除列表完全一致。
最终报告模板:
## Split complete (<N> commits)
| Commit | Message | Target |
|---|---|---|
| <7-char sha> | <commit message> | <files, noting hunk splits> |
- Lossless check: <"patch-id matches" or "leftover changes match the exclusion list">
- Excluded files: <list and reason, "none" if none>
- Undo: `git reset --mixed <starting HEAD>` (unwinds the commits back to the pre-split state)
使用方式
该技能以技能形式提供,通过上述六步流程驱动:读取状态、分析改动、提出方案、用户确认、执行、验证无损。执行阶段使用 git add 与 git commit,对需要按 hunk 拆分的文件使用随附的 scripts/stage_hunks.py 脚本。
适用场景
- 工作区积累了大量未提交改动,需要拆成多个便于审查的原子提交。
- 一个文件里混有多种上下文(如功能改动与配置调整),需要按 hunk 级别拆分。
- 希望拆分结果可验证,确保拆分前后代码内容零差异。
限制
- 只运行 add 和 commit,绝不 push、reset、stash 或 checkout。
- 遇到 merge/rebase 进行中或存在冲突文件时会停止。
- 无法保留已有的暂存状态,需先取消全部暂存再重新拆分。
- 中途出错不会自动恢复,只报告状态并停止。
- 撤销命令
git reset --mixed <startHEAD>不会由技能自行执行,仅在报告中给出。 - 若有意排除某些文件,patch-id 会不同,需改用排除列表比对来验证。