摘要
Conventional Commits 规范是建立在提交信息之上的一种轻量级约定。它提供了一套简单的规则用于创建明确的提交历史,这使得自动化工具的构建变得更加容易。该约定与 SemVer(语义化版本)相呼应,通过在提交信息中描述新功能、修复和破坏性变更来传达版本级别的含义。
提交信息的结构应如下所示:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
提交信息包含以下结构化元素,用于向你的库的使用者传达意图:
- fix: 类型为
fix的提交用于修复代码库中的缺陷(对应语义化版本中的PATCH)。 - feat: 类型为
feat的提交用于引入新功能(对应语义化版本中的MINOR)。 - BREAKING CHANGE: 包含
BREAKING CHANGE:页脚的提交,或在类型/作用域后追加!的提交,表示引入了破坏性 API 变更(对应语义化版本中的MAJOR)。BREAKING CHANGE 可以是任何类型的提交的一部分。 - 除
fix:和feat:之外,也允许使用其他类型,例如@commitlint/config-conventional(基于 Angular 约定)推荐使用build:、chore:、ci:、docs:、style:、refactor:、perf:、test:等。 - 除
BREAKING CHANGE: <description>之外,也可以提供其他页脚,并遵循类似于 git trailer 格式的约定。
Conventional Commits 规范并未强制要求其他类型,这些类型在语义化版本中也没有隐含的效果(除非它们包含 BREAKING CHANGE)。可以为提交的类型提供一个作用域(scope),以提供额外的上下文信息,并用圆括号括起来,例如 feat(parser): add ability to parse arrays。
示例
包含描述和破坏性变更页脚的提交信息
feat: allow provided config object to extend other configs
BREAKING CHANGE: `extends` key in config file is now used for extending other config files
使用 ! 标记破坏性变更的提交信息
feat!: send an email to the customer when a product is shipped
带作用域且使用 ! 标记破坏性变更的提交信息
feat(api)!: send an email to the customer when a product is shipped
同时使用 ! 和 BREAKING CHANGE 页脚的提交信息
chore!: drop support for Node 6
BREAKING CHANGE: use JavaScript features not available in Node 6.
不包含正文的提交信息
docs: correct spelling of CHANGELOG
带作用域的提交信息
feat(lang): add Polish language
包含多段正文和多个页脚的提交信息
fix: prevent racing of requests
Introduce a request id and a reference to latest request. Dismiss
incoming responses other than from latest request.
Remove timeouts which were used to mitigate the racing issue but are
obsolete now.
Reviewed-by: Z
Refs: #123
规范
- 提交 MUST 以一个类型(type)作为前缀,该类型由一个名词组成,如
feat、fix等,其后跟随可选的作用域(scope)、可选的!,以及必需的冒号和空格。 - 当提交为应用程序或库添加新功能时,MUST 使用
feat类型。 - 当提交表示对应用程序的缺陷修复时,MUST 使用
fix类型。 - 在类型之后 MAY 提供作用域。作用域 MUST 由一个描述代码库某个部分的名词组成,并用圆括号括起来,例如
fix(parser):。 - 描述 MUST 紧跟在类型/作用域前缀的冒号和空格之后。描述是对代码变更的简短总结,例如:fix: array parsing issue when multiple spaces were contained in string。
- 在简短描述之后 MAY 提供较长的提交正文,以提供关于代码变更的额外上下文信息。正文 MUST 在描述之后空一行开始。
- 提交正文是自由格式的,MAY 由任意数量的以换行符分隔的段落组成。
- 在正文之后空一行,MAY 提供一个或多个页脚(footer)。每个页脚 MUST 由一个单词标记(token)组成,其后跟随
:<空格>或<空格>#分隔符,再跟随一个字符串值(这受到 git trailer 约定的启发)。 - 页脚的标记 MUST 使用
-代替空格字符,例如Acked-by(这有助于将页脚部分与多段正文区分开来)。BREAKING CHANGE是一个例外,它 MAY 也可以作为标记使用。 - 页脚的值 MAY 包含空格和换行符,当观察到下一个有效的页脚标记/分隔符对时,解析 MUST 终止。
- 破坏性变更 MUST 在提交的类型/作用域前缀中指示,或在页脚中作为条目指示。
- 如果作为页脚包含,破坏性变更 MUST 由大写文本
BREAKING CHANGE组成,后跟冒号、空格和描述,例如:BREAKING CHANGE: environment variables now take precedence over config files。 - 如果在类型/作用域前缀中包含,破坏性变更 MUST 通过紧挨着冒号前的
!来指示。如果使用了!,则 MAY 在页脚部分省略BREAKING CHANGE:,此时提交描述 SHALL 用于描述破坏性变更。 - 在提交信息中 MAY 使用除
feat和fix之外的其他类型,例如:docs: update ref docs. - 实现者 MUST NOT 将构成 Conventional Commits 的信息单元视为区分大小写的,
BREAKING CHANGE除外,它 MUST 为大写。 - 当作为页脚中的标记使用时,
BREAKING-CHANGEMUST 与BREAKING CHANGE同义。
为什么使用 Conventional Commits?
- 自动生成 CHANGELOG。
- 自动确定语义化版本的升级幅度(基于落地提交的类型)。
- 向团队成员、公众和其他利益相关者传达变更的性质。
- 触发构建和发布流程。
- 让更多人更容易为你的项目做出贡献,通过让他们能够探索一个更有结构的提交历史。
常见问题
在初始开发阶段,我应该如何处理提交信息?
我们建议你按照产品已经发布的方式来进行。通常某些人——即使是你身边的软件开发者——正在使用你的软件。他们会想知道什么被修复了、什么被破坏了等等。
提交标题中的类型应该大写还是小写?
可以使用任何大小写形式,但最好保持一致。
如果提交符合多个提交类型,我该怎么办?
尽可能回退并拆分为多个提交。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 规范?
不需要!如果你使用基于 squash 的工作流,首席维护者可以在合并时清理提交信息——这不会给临时贡献者增加额外的工作量。一种常见的工作流是让你的 Git 系统自动将 PR 中的提交压缩,并为首席维护者提供一个表单来输入正确的 Git 提交信息。
Conventional Commits 如何处理 revert(回退)提交?
回退代码可能很复杂:你是在回退多个提交吗?如果你回退了一个功能,下一个版本应该是补丁版本吗?
Conventional Commits 并未明确定义回退行为。相反,我们将这一点留给工具开发者,利用类型和页脚的灵活性来开发他们自己的回退处理逻辑。
一个建议是使用 revert 类型,并使用一个引用被回退的提交 SHA 的页脚:
revert: let us never again speak of the noodle incident
Refs: 676104e, a215868