1. 为什么需要 Commit 规范?
在深入格式之前,先理解“为什么”能让你坚持遵守:
- 代码审查 (Review) 更高效:清晰的标题能让 Reviewer 快速判断修改的范围和性质。
- 自动化生成 Changelog:规范的格式可以直接脚本提取生成发布日志。
- 版本回溯与定位 (Bisect):当出现 Bug 需要回滚时,规范的提交记录能帮你快速定位是哪个
feat或fix引入的问题。 - 语义化版本控制:提交类型(如
fixvsfeat)直接对应版本号的 Patch 或 Minor 更新。
2. 标准格式结构
一个标准的 Commit Message 包含三个部分:Header、Body 和 Footer。
<Type>(<Scope>): <Subject>
// 空一行
<Body>
// 空一行
<Footer>
第一部分:Header (必填)
这是最关键的一行,必须简洁明了(建议不超过 50 或 72 个字符)。
Type(类型):用于说明 commit 的类别。常见的类型有:feat:新功能 (Feature) -> 对应语义化版本的 MINOR。fix:修补 Bug -> 对应语义化版本的 PATCH。docs:仅文档更改 (Documentation)。style:不影响代码含义的更改 (空格、格式化、缺少分号等)。refactor:重构代码 (既不是新增功能,也不是修改 Bug)。perf:优化性能的更改。test:增加测试或更新现有测试。chore:构建过程或辅助工具的变动 (如文档生成、依赖升级),不影响源码。ci:CI 配置文件或脚本的修改 (如 GitHub Actions, Travis)。revert:回滚之前的提交。
Scope(范围):(可选) 用于说明 commit 影响的范围。- 例如:
feat(user-auth): add login logic,括号里的user-auth就是 scope,指明了修改的是用户认证模块。
- 例如:
Subject(简述):commit 的简短描述。- 使用祈使句(如 “change” 而不是 “changed” 或 “changes”)。
- 不要以句号结尾。
- 中文示例:
feat(ui): 新增暗黑模式切换按钮
第二部分:Body (选填)
对本次 commit 的详细描述。
- 解释“为什么”:修改的动机是什么?解决了什么问题?
- 解释“怎么做”:与之前的代码相比,主要区别是什么?(如果逻辑复杂)
- 换行:每行建议不超过 72 个字符,避免在终端查看时自动换行错乱。
第三部分:Footer (选填)
用于追踪 issue 或重大变更。
- Breaking Changes:如果不兼容旧版本,必须在此处(或 Header)注明
BREAKING CHANGE:,并说明迁移方案。这将触发语义化版本的 MAJOR 更新。 - 关联 Issue:例如
Closes #123,Fixes #456。
3. 深度剖析:实战中的常见误区与技巧
误区 1:万物皆 update
很多人的提交记录全是 update code、fix bug。
- 深度分析:这等于没有写。
fix bug并没有告诉队友修复了哪个 bug。 - 改进:
fix(login): 修复登录接口在 500 错误时未提示的问题
误区 2:一次提交包含过多修改
在一个 commit 里既修了 A 模块的 Bug,又加了 B 模块的新功能,还顺手改了 C 文件的缩进。
- 深度分析:这破坏了提交的原子性。如果你需要回滚 B 功能,由于它们都在一个 commit 里,你会被迫回滚掉 A 的 Bug 修复。
- 原则:一个 Commit 只做一件事。
误区 3:Scope 滥用或缺失
- 深度分析:对于大型单体仓库(Monorepo),
scope至关重要。如果没有 scope,在看几千条 log 时很难过滤出“只看订单模块”的变更。
技巧:Emoji 的使用 (Gitmoji)
有些团队喜欢在 Type 前加 Emoji,虽然不在官方规范内,但能增加可视性:
- ✨
:sparkles:-> New Feature - 🐛
:bug:-> Bug Fix - ♻️
:recycle:-> Refactor - (注意:正式的企业级开发或开源项目需谨慎使用,可能会干扰某些 changelog 工具)
4. 优秀的 Commit 示例
示例 1:新增功能
feat(blog): 添加文章点赞功能
为文章详情页增加了点赞按钮。
后端 API 已经更新,前端通过调用 /api/like 接口实现。
用户点赞后,按钮状态会即时更新并持久化。
示例 2:修复 Bug 并关闭 Issue
fix(utils): 修复 formatDate 在 Safari 下的兼容性问题
Safari 浏览器不支持 'YYYY-MM-DD' 格式的直接解析,会导致 Date 对象返回 Invalid Date。
改为使用 regex 手动分割字符串进行解析。
Closes #42
示例 3:破坏性更新 (Breaking Change)
feat(api)!: 重构用户数据结构,移除 age 字段
BREAKING CHANGE: api v2 返回的用户对象中不再包含 `age` 字段,请改用 `birthDate` 计算。
(注意:Header 中的 ! 也是标识 Breaking Change 的一种简写方式)
5. 如何强制落地?(工具链)
光靠口头约定很难坚持,通常需要工具辅助:
- Commitizen (
cz-cli):一个撰写合格 Commit message 的工具。它会像向导一样一步步问你:类型是什么?范围是什么?描述是什么?然后自动生成符合规范的提交信息。 - Commitlint:结合 Husky 使用。在
git commit时触发钩子,自动检查你的 message 是否符合规范。如果不符合,直接报错拒绝提交。 - Standard Version:自动根据 commit 记录升级版本号并生成
CHANGELOG.md。