Community编程与开发github.com

原子化 Git 提交

将工作区中的更改拆分为原子化、单一用途的 Git 提交——审视变更内容,将其组织为逻辑单元,排序使每个提交都能独立构建,并编写与仓库现有风格匹配的提交信息。当用户要求提交时,特别是当工作区中同时存在多个逻辑变更时,以及当用户要求拆分、分离、重新排序、重构或清理提交时使用。当一条提交信息需要使用“and”才能准确描述,或只有部分文件需要暂存时也应使用。

原子化 Git 提交 是什么?

该技能指导开发者将工作区的混乱更改转化为干净、原子化的 Git 提交序列。核心价值在于:每个提交只包含一个逻辑变更,从而使代码审查更聚焦(审阅者可一次只评估一个变更)、回退更精确(可安全地撤销有害提交而不牵连正常代码)、二分查找更可靠(`git bisect` 能准确指向引入 bug 的提交)。技能首先要求通过 `git status` 和 `git diff` 全面审视工作树,然后通过“and”语言测试和依赖关系分析将变更分组为独立逻辑单元,并严格按照依赖顺序排列提交以确保每个提交都能构建。暂存策略分三个层级:全文件暂存(首选)、通过 `git stash` 暂时搁置干扰文件、以及通过参考文件处理单文件内的跨段拆分(强调交互式 `git add -p` 在非交互环境中静默失败,需避免使用)。提交信息编写要求严格匹配仓库现有风格(如 Conventional Commits、项目编号格式等),但严禁凭空猜测缺失的引用编号。最后通过 `git log` 和 `git status` 验证结果。技能还明确禁止丢弃未提交的工作、重写已推送的历史、以及为追求“全面”而过度拆分。适用于 Claude Code、Cursor、Codex 等支持 Git 操作的 AI 编码助手。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/roselletabuena/skills/tree/HEAD/plugins/git-workflows/skills/git-atomic-commit

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

原子化提交

原子化提交只包含一个逻辑变更。其价值并非为了整洁本身——而是团队在提交混杂时会失去的三种具体能力:

  • 审查:审阅者可以在脑海中只关注一个概念并对其进行判断。
  • 回退:可以撤销一个糟糕的变更,而不带走好的变更。
  • 二分查找:git bisect 可以定位引入 bug 的提交,但这要求每个提交只包含一个变更并且每个提交都能独立构建。

最后一条是最常被忽略的。原子化并不意味着“尽可能小”——而是“一个完整的变更”。将一个函数与其所需的调用点分开的提交虽然更小,但并非原子化,因为它会使代码树处于损坏状态。

工作流程

1. 在暂存任何内容之前先审视全局

在决定如何划分变更之前,先了解整个工作树:

git status --porcelain      # 每条路径,包括未跟踪文件
git diff                    # 未暂存的变更
git diff --cached           # 已暂存的任何内容
git log --oneline -15       # HEAD 的位置以及本地风格

不要将 git add -A 和 git commit -a 作为第一步操作。它们是将混合工作树变成混合提交的方式,而一旦提交,拆分它要比之前拆分困难得多。同时检查是否有根本不应提交的变更——构建输出、.env 文件、调试打印语句、无关的编辑器配置。

2. 将变更分组为逻辑单元

最可靠的检验标准是语言层面:起草提交主题,如果它需要一个“and”才能准确描述,那么你实际上需要多个提交。“修复登录重定向并升级 lodash”就是两个提交。审阅者的问题则是第二个检验标准:他们是否必须理解变更 X 才能判断变更 Y?如果不是,那么这些变更是独立的,应该分开。

分开的情况:

保持分离原因
格式/空白 vs. 逻辑格式化会使得那三行真正的代码被埋没在400行噪音中。这是最有价值的拆分。
重命名 vs. 行为变更同样原因——重命名会触及所有地方并掩盖真正重要的一行。
依赖升级 vs. 代码每个都需要各自的回退故事。
无关的错误修复独立的回退,独立的二分查找。
重构 vs. 使用重构的特性让审阅者能够确认重构是保持行为不变的。

放在一起的情况:

保持在一个提交中原因
变更及其测试它们是一个逻辑单元;为未发布的代码编写测试,或为没有测试的代码编写测试,都只是变更的一半。
重命名及其所有调用点拆分会使代码树损坏——与原子化相反。
签名变更及其调用者同上。
变更及其描述性文档/更新日志它们描述同一个决策,应该一起回退。

3. 对所有提交进行排序,确保每个都能构建

顺序很重要,因为每个提交都应该使工作树保持正常工作状态。将基础部分放在使用它们的内容之前:在调用代码之前添加辅助函数,在基于重构构建特性之前进行重构,在编写查询之前确定模式。如果两个组相互依赖,这说明它们实际上应该是一个提交。

4. 暂存每个分组

使用最简洁的可行技术。只有在更简单的方法无法表达拆分意图时才升级:

级别 1 - 暂存整个文件。 这涵盖了绝大多数实际的拆分场景,因为逻辑变更通常位于不同的文件中:

git add src/auth.py tests/test_auth.py
git commit -m "..."

级别 2 - 暂时搁置整个文件,当工作树中某些内容可能被意外提交时使用:

git stash push -- requirements.txt    # 暂时搁置
# ... 提交其余部分 ...
git stash pop                         # 取回

级别 3 - 拆分单个文件内的变更。 仅当单个文件确实包含两个逻辑变更时才需要。具体操作比较繁琐且容易出错,因此在尝试之前请先阅读 references/splitting-one-file.md。

git add -p 在非交互式环境中无法工作,并且会静默失败。 它会打印第一个代码块,在 stdin 上遇到 EOF,什么也不暂存,并以退出码 0 结束。这里的成功退出码意味着什么事都没发生。git add -i、git commit -p 和 git checkout -p 也是如此。因为失败看起来像成功,通常的后果是得到一个空提交或缺少预期变更的提交——所以请改用参考文件中的技术,并在提交前使用 git diff --cached 验证索引状态。

5. 为每一条信息使用仓库自己的语言风格

匹配周围的历史记录,而不是引入一种新约定。在一个全是纯文本主题的日志中插入一个 feat: 前缀就是噪音,它会让历史看起来像是两个项目。请先阅读本地风格:

git log --no-merges --format='%s' -n 30               # 主题风格
git log --no-merges -n 10 --format='%n---%n%s%n%b'    # 是否使用正文

查找:type: 或 type(scope): 前缀(约定式提交)、类似 [PROJ-123] 的工单前缀、大小写、祈使句 vs. 过去时(“Add” vs. “Added”)、典型主题长度,以及正文是否正常使用还是主题单独存在。采用你找到的风格。约定式提交是一个好的默认选择,仅当日志中已经使用了它,或者仓库还没有历史记录且用户没有偏好。

无论本地风格如何,主题应该说明更改了什么,而正文——当历史记录使用正文时——应该说明为什么,因为差异本身已经展示了更改了什么。如果仓库有 CONTRIBUTING.md 或 commit-msg 钩子,它们优先于从日志中推断出的规则。

匹配约定绝不意味着凭空捏造它所携带的数据。 如果本地风格嵌入了工单或问题引用——[PROJ-123]、Fixes #456——而你没有获得真实的标识符,请不要猜测一个,也不要继续你从日志中看到的编号。一个看似合理的 [PROJ-124] 比根本没有前缀更糟糕:它是永久性的,看起来像是真正的可追溯性,而且它可能指向别人无关的工单。省略引用,匹配风格的其余部分,并在你的回复中说明缺少了 ID,以便用户可以补充——提交尚未推送,修改成本很低。如果用户在线,可以向他询问编号。

6. 在交还给用户之前进行验证

git log --oneline -n <计数>     # 你构建的提交结构
git status --porcelain           # 是否还有剩余内容

确认每个提交只包含其自身的变更(git show --stat <sha>),并且没有任何内容被遗漏。如果某些变更出于设计原因而未提交,请明确说明——用户认为工作树是干净的而实际上不是,这是最糟糕的结果,因为他们稍后才会发现,并且不知道这是否是有意为之。

护栏

  • 指导如何提交,而不是决定是否提交。 如果用户没有要求提交,本技能不会使创建提交变得合适,也从不暗示推送。
  • 切勿为了简化拆分而丢弃工作。 git checkout -- <file>、git reset --hard 和 git stash drop 会销毁尚未提交且没有被任何提交保存的工作——这是不可恢复的。如果某项技术需要这些命令之一,请先备份内容并在运行前确认备份存在。
  • 不要为了整理而重写已发布的历史记录。拆分已经推送的提交意味着强制推送,这会重写其他人可能已经拉取的历史;请先询问。
  • 不要为了看起来全面而过度拆分。 三个每个都破坏构建的提交比一个能工作的提交更糟糕。如有疑问,优先选择能保持每个提交功能完整的分组。
  • 如果工作树实际上只包含一个逻辑变更,就将其作为一个提交。 并非每个工作树都需要拆分,而这样说本身就是一个不错的结果。

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

相关技能