Git提交规范
本文介绍Conventional Commits 1.0.0 (opens new window)规范,这是一套被广泛采用的Git提交消息约定。
# 摘要
Conventional Commits
规范是一种轻量级的提交消息约定,已成为现代软件开发的最佳实践之一。
它提供了一套简单而强大的规则,用于创建明确、可读性强的提交历史记录;这使得编写自动化工具更加容易,同时显著提升团队协作效率。
此规范与SemVer (opens new window)紧密结合,通过描述提交消息中的特性、修复和重大更改来实现自动化版本管理,广泛应用于开源项目和企业级开发中。
提交消息应按照以下格式构建:
<type>[可选范围]: <描述>
[可选正文]
[可选页脚]
提交包含以下结构元素,以向库的使用者传达意图:
- fix: 类型为
fix
的提交修复代码库中的错误(这对应于语义化版本控制中的PATCH (opens new window))。 - feat: 类型为
feat
的提交引入代码库中的新功能(这对应于语义化版本控制中的MINOR (opens new window))。 - BREAKING CHANGE: 如果提交的页脚包含
BREAKING CHANGE:
或在类型/范围后附加!
,则表示引入了破坏性API更改(这对应于语义化版本控制中的MAJOR (opens new window))。破坏性更改可以是任何类型的提交的一部分。 - 其他类型:除了
fix:
和feat:
之外,还可以使用其他类型,例如@commitlint/config-conventional (opens new window)(基于Angular约定 (opens new window))推荐的build:
、chore:
、ci:
、docs:
、style:
、refactor:
、perf:
、test:
等。 - 除
BREAKING CHANGE: <描述>
之外的其他页脚也可以提供,并遵循类似于git trailer format (opens new window)的约定。
其他类型不受Conventional Commits规范强制要求,也不会在语义化版本控制中产生隐式影响(除非它们包括破坏性更改)。可以通过括号提供范围,以提供更多上下文信息,例如feat(parser): 添加解析数组的能力
。
# 常见的类型(type)详解
# 核心类型
feat
: 新功能的添加(对应语义化版本的 MINOR)- 示例:
feat(auth): 添加微信登录功能
- 示例:
feat: 支持文件批量上传
- 示例:
fix
: 修复 Bug(对应语义化版本的 PATCH)- 示例:
fix(login): 修复用户名包含特殊字符时登录失败
- 示例:
fix: 解决内存泄漏问题
- 示例:
# 维护类型
build
: 构建系统或外部依赖的更改- 示例:
build: 升级webpack至5.0
- 示例:
build(deps): 更新所有依赖包
- 示例:
chore
: 日常维护任务,不影响代码功能- 示例:
chore: 更新.gitignore文件
- 示例:
chore(release): 发布1.2.0版本
- 示例:
ci
: 持续集成相关文件或配置的更改- 示例:
ci: 添加自动化测试流水线
- 示例:
ci(github): 优化GitHub Actions配置
- 示例:
# 代码改进类型
docs
: 文档更新或修改- 示例:
docs: 更新API文档
- 示例:
docs(readme): 添加安装说明
- 示例:
perf
: 性能优化- 示例:
perf(api): 优化数据库查询性能
- 示例:
perf: 减少首页加载时间
- 示例:
refactor
: 代码重构,不改变功能- 示例:
refactor(utils): 重构工具函数结构
- 示例:
refactor: 使用ES6语法重写模块
- 示例:
style
: 样式或格式调整,不影响代码逻辑- 示例:
style: 统一代码缩进格式
- 示例:
style(css): 优化样式文件结构
- 示例:
test
: 测试相关的更改或新增测试用例- 示例:
test(auth): 添加登录功能单元测试
- 示例:
test: 提高测试覆盖率至90%
- 示例:
revert
: 回滚之前的提交- 示例:
revert: 回滚"feat: 添加新支付方式"
- 示例:
# 常见的范围(scope)详解
# 功能模块范围
auth
: 认证相关模块(登录、注册、权限验证)user
: 用户管理模块(用户信息、个人设置)order
: 订单管理模块payment
: 支付相关功能notification
: 通知系统search
: 搜索功能upload
: 文件上传功能chat
: 聊天或即时通讯功能
# 技术层面范围
api
: API 相关更改(接口设计、路由)database
: 数据库相关(迁移、模型)cache
: 缓存相关功能security
: 安全相关功能config
: 配置文件或设置deps
: 依赖项更新core
: 核心逻辑或全局功能utils
: 工具函数
# 界面组件范围
ui
: 通用UI组件dashboard
: 仪表盘或控制台header
: 页面头部组件sidebar
: 侧边栏组件modal
: 弹窗组件form
: 表单组件table
: 表格组件
# 页面范围
home
: 首页login
: 登录页面profile
: 个人资料页面settings
: 设置页面admin
: 管理后台页面
# 示例
# 包含描述和破坏性更改页脚的提交消息
feat: 允许提供的配置对象扩展其他配置
BREAKING CHANGE: 配置文件中的 extends 键现在用于扩展其他配置文件
# 使用!
标记破坏性更改的提交消息
feat!: 在产品发货时向客户发送电子邮件
# 带有范围和!
标记破坏性更改的提交消息
feat(api)!: 在产品发货时向客户发送电子邮件
# 同时包含!
和破坏性更改页脚的提交消息
chore!: 丢弃对节点 6 的支持
BREAKING CHANGE: 使用 Node 6 中不可用的 JavaScript 功能。
# 不带正文的提交消息
docs: CHANGELOG 的正确拼写
# 带有范围的提交消息
feat(lang): 添加波兰语
# 包含多段正文和多个页脚的提交消息
fix: 防止请求争用
引入请求 ID 和对 latest 请求的引用。解雇来自 latest request 以外的传入响应。
删除了用于缓解赛车问题的超时,但现在已经过时了。
Reviewed-by: Z
Refs: #123
# 规范
本文档中的关键词“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY”和“OPTIONAL”应按RFC 2119 (opens new window)中的描述进行解释。
- 提交必须以前缀类型开头,该类型由名词组成,如
feat
、fix
等,后跟可选范围、可选!
以及必需的冒号和空格。 - 当提交添加新功能到应用程序或库时,必须使用类型
feat
。 - 当提交代表应用程序的错误修复时,必须使用类型
fix
。 - 可以为类型提供范围。范围必须由一个描述代码库部分的名词组成,并用括号包围,例如
fix(parser):
。 - 描述必须紧随类型/范围前缀后的冒号和空格。描述是对代码更改的简短总结,例如
fix: String 中包含多个空格时的数组解析问题
。 - 可以在简短描述后提供较长的提交正文,提供有关代码更改的更多上下文信息。正文必须从描述后的一行空白开始。
- 提交正文是自由形式的,可以包含任意数量的新行分隔段落。
- 在正文后的一行空白后可以提供一个或多个页脚。每个页脚必须由一个单词标记组成,后跟
:
或#
分隔符,再跟一个字符串值(这受到git trailer convention (opens new window)的启发)。 - 页脚标记必须用
-
替换空白字符,例如Acked-by
(这有助于区分页脚部分和多段正文)。BREAKING CHANGE
作为标记时例外。 - 页脚值可以包含空格和换行符,解析必须在观察到下一个有效的页脚标记/分隔符对时终止。
- 破坏性更改必须在提交的类型/范围前缀中指示,或者作为页脚条目。
- 如果作为页脚包含,破坏性更改必须由大写字母
BREAKING CHANGE
组成,后跟冒号、空格和描述,例如BREAKING CHANGE: 环境变量现在优先于配置文件
。 - 如果包含在类型/范围前缀中,破坏性更改必须由
!
立即在:
之前指示。如果使用!
,则可以从页脚部分省略BREAKING CHANGE:
,并且提交描述应用于描述破坏性更改。 - 提交消息中可以使用其他类型,例如
docs: update ref docs.
。 - 实现者不应将构成常规提交的信息单元视为大小写敏感,但
BREAKING CHANGE
必须大写。 - 当用作页脚标记时,
BREAKING-CHANGE
必须与BREAKING CHANGE
同义。
# 为什么使用Conventional Commits?
- 自动生成CHANGELOG。
- 自动确定语义版本提升(基于提交的类型)。
- 向团队成员、公众和其他利益相关者传达更改性质。
- 触发构建和发布过程。
- 通过允许人们探索更结构化的提交历史,使人们更容易贡献到您的项目。
# 工具和自动化
# 提交消息验证工具
1. Commitlint commitlint
是最常用的提交消息验证工具,可以确保提交消息符合规范。
安装和配置:
# 安装
npm install --save-dev @commitlint/cli @commitlint/config-conventional
# 创建配置文件 commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional']
}
# 配合 husky 使用
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
2. Conventional Changelog 自动生成 CHANGELOG.md 文件:
npm install --save-dev conventional-changelog-cli
# 生成 changelog
npx conventional-changelog -p angular -i CHANGELOG.md -s
# IDE 插件和扩展
VS Code 扩展
Conventional Commits
: 提供提交消息模板GitLens
: 增强Git功能,更好地查看提交历史Commitizen
: 交互式提交消息生成
JetBrains IDE 插件
Git Commit Template
: 提交消息模板Conventional Commit
: 规范化提交辅助
# 自动化发布工具
Semantic Release
npm install --save-dev semantic-release
# .releaserc.json 配置
{
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",
"@semantic-release/github"
]
}
# Git Hook 配置示例
使用 husky
和 lint-staged
创建完整的工作流:
// package.json
{
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"lint-staged": {
"*.{js,ts,vue}": ["eslint --fix", "prettier --write"],
"*.md": ["prettier --write"]
}
}
# 最佳实践和常见陷阱
# 最佳实践
1. 保持提交原子性
- 每个提交只包含一个逻辑变更
- 避免将多个不相关的修改合并到一个提交中
2. 编写清晰的描述
# 好的示例
feat(auth): 添加JWT令牌刷新机制
# 不好的示例
fix: 修复bug
3. 合理使用破坏性更改标记
# 当API发生不兼容变更时
feat(api)!: 重构用户认证接口返回格式
BREAKING CHANGE: API响应格式从 {user: {...}} 改为 {data: {user: {...}}}
4. 团队规范统一
- 制定团队内的scope命名规范
- 定期Review提交质量
- 使用工具自动化检查
# 常见陷阱
1. 过度细化scope
# 避免过度细化
fix(user-login-form-validation): 修复邮箱格式验证
# 推荐
fix(auth): 修复邮箱格式验证
2. 描述过于技术化
# 避免过于技术化
fix: 修复UserService.validateEmail()方法的正则表达式
# 推荐
fix(auth): 修复邮箱格式验证错误
3. 忽略BREAKING CHANGE 当修改会影响现有功能时,必须标记为破坏性更改。
4. 提交消息过长或过短
- 描述应该在50字符以内
- 正文可以详细说明,但要简洁明了
# 团队采用建议
# 渐进式采用策略
第1阶段:基础规范
- 只要求使用
feat
、fix
、docs
三种类型 - 暂时不强制使用scope
第2阶段:完整规范
- 引入完整的type分类
- 开始使用scope
- 添加commitlint验证
第3阶段:自动化
- 集成自动化工具
- 自动生成CHANGELOG
- 配置自动化发布流程
# 培训和文档
团队培训要点
- 规范的意义和价值
- 工具使用方法
- 常见场景的处理方式
- Code Review中的注意事项
内部文档建议
- 团队特定的scope定义
- 常用提交消息模板
- 工具配置说明
- 问题排查指南
# 常见问题解答
# 初始开发阶段如何处理提交消息?
我们建议您像已经发布产品一样继续进行。通常有人,即使是您的软件开发人员同事,也在使用您的软件。他们会想知道哪些问题已修复,哪些会破坏等。
# 提交标题中的类型是大写还是小写?
可以使用任何大小写,但最好保持一致。
# 如果提交符合多个提交类型怎么办?
尽可能回退并制作多个提交。Conventional Commits的部分好处在于其驱动我们制作更有组织的提交和PR的能力。
# 这是否阻碍了快速开发和快速迭代?
它阻止了无序的快速移动。它帮助您能够在多个项目中长期快速移动,即使有不同贡献者也是如此。
# Conventional Commits是否会限制开发者制作的提交类型,因为他们只会考虑提供的类型?
Conventional Commits鼓励我们制作某些类型的提交,例如修复。除此之外,Conventional Commits的灵活性允许您的团队制定自己的类型,并随着时间改变这些类型。
# 这与SemVer有何关系?
fix
类型的提交应转换为PATCH
发布。feat
类型的提交应转换为MINOR
发布。无论类型如何,包含BREAKING CHANGE
的提交应转换为MAJOR
发布。
# 如何为Conventional Commits规范的扩展版本?例如@jameswomack/conventional-commit-spec
?
我们建议使用SemVer发布对此规范的扩展(并鼓励您制作这些扩展!)
# 如果不小心使用了错误的提交类型怎么办?
# 当使用了属于规范但不是正确类型的类型时,例如fix
而不是feat
在合并或发布错误之前,我们建议使用git rebase -i
编辑提交历史。发布后,清理方式将根据您使用的工具和流程而异。
# 当使用了不属于规范的类型时,例如feet
而不是feat
最坏的情况下,如果提交不符合Conventional Commits规范,世界末日不会到来。这只是意味着该提交将被基于规范的工具忽略。
# 所有贡献者都需要使用Conventional Commits规范吗?
不需要!如果您使用Git上的squash工作流,主维护者可以在合并时清理提交消息——不会增加临时提交者的负担。常见的工作流是让您的Git系统自动压缩来自拉取请求的提交,并为主维护者提供输入正确的Git提交消息的表单。
# Conventional Commits如何处理revert提交?
回滚代码可能很复杂:您是在回滚多个提交吗?如果回滚了一个特性,下一个发布应该是补丁吗?
Conventional Commits没有明确努力定义回滚行为。而是留给工具作者使用类型和页脚的灵活性来开发他们处理回滚的逻辑。
一个建议是使用revert
类型,并在页脚中引用正在回滚的提交SHA:
revert: 让我们永远不要再谈论 Noodle 事件
Refs: 676104e, a215868
祝你变得更强!