约定式提交(conventional commits)是一种针对 commit message 的约定,用于创建清晰的提交历史,有助于自动生成CHANGELOG,同时与语义化版本的格式保持一致。

本文分三部分:1. 为何使用约定式提交;2. 约定式提交的规范什么;3. 有哪些工具可以用于约束和利用约定式提交。

为何使用约定式提交

为了自动生成CHANGELOG

  • 无论是在发布版本时、还是在每次提交commit时,手动编写CHANGELOG总是令人心烦的。自动化该流程能提高幸福指数。
  • 在不强调commit规范的前提下,去强调CHANGELOG的准确性,我认为是靠不住的。在记录时、迭代编辑时、合并分支时都有可能丢失文档内的信息。而基于commit生成CHANGELOG则大大降低了这个可能性,毕竟commit可不能随随便便就搞丢。
  • 自动生成CHANGELOG应该作为CI/CD的一部分。

基于提交的类型,自动决定语义化的版本变更

语义化版本 2.0.0 的规范如下:

版本格式:主版本号.次版本号.修订号,版本号递增规则如下:

  1. 主版本号:当你做了不兼容的 API 修改,
  2. 次版本号:当你做了向下兼容的功能性新增,
  3. 修订号:当你做了向下兼容的问题修正。

先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。

理论上,我们只要在commit信息上标注当前提交类型为 feature、bugFix、BREAKING CHANGE等,在发布版本时,就能依据这段提交历史中的提交类型来确定要升的是MAJOR、MINOR还是PATCH。

约定式提交的规范

commit message 的结构

1
2
3
4
5
<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

commit 内的 type等元素用于表示当前提交的意图:

  • type =fix,表示修复bug,跟语义化版本的修订号(PATCH)对应。
  • type =feat,表示新增feature,跟语义化版本的次版本号(MINOR)对应。
  • footer 内包含BREAKING CHANGE,或者在<type>(scope)后存在 !,表示出现了破坏性的变更,跟语义化版本的主版本号(MAJOR)对应。
  • 除了 fix、feat外,还可以按需定义如 build、ci、docs、refactor 等 commit type。

Examples

  • 存在 BREAKING CHANGE表示破坏性变更:

    1
    2
    3
    feat: allow provided config object to extend other configs

    BREAKING CHANGE: `extends` key in config file is now used for extending other config files
  • 存在 ! 表示破坏性变更:

    1
    feat!: send an email to the customer when a product is shipped
  • 包含影响范围的提交:

    1
    feat(lang): add polish language

更多详细的规范可以阅读:Conventional Commits

面向约定式提交的自动化工具

commitizen 替换 git commit

Commitizen 在创建提交时,可以提示你填写符合 conventional commits 约定的必要字段。

下面介绍全局安装使用的方法,项目级安装的方式可以看README文档

cz-conventional-changelog 是符合约定式提交的一种规范,你也可以参考它制定自己的规范,比如可以指定有哪些提交类型。

Install

1
2
3
npm install -g commitizen cz-conventional-changelog

echo '{ "path": "cz-conventional-changelog" }' > ~/.czrc

Usage

git commit 替换为 cz或者git cz,工具会展示交互式的界面,按照提示填写commit message。

commitizen


commitlint 对commit message进行lint

使用commitlint 对 commit message进行lint,判断其是否符合 conventional commits 的约定,对于不满足的拒绝提交。

  • commitlint也需要对应conventional commits的配置项,这里使用的是 @commitlint/config-conventional
  • commitlint需要使用 commit-msg hook,官方使用的是husky。

Install

1
2
3
4
5
6
7
8
9
10
11
12
# 全局安装 @commitlint
npm install -g @commitlint/cli @commitlint/config-conventional

# commitlint.config.js 放在项目目录下
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js

# Install husky
npm install husky --save-dev
# Active hooks
npx husky install
# Add hook
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit $1'

Usage

commintlint

创建一个提交进行测试

1
2
3
4
5
6
7
8
9
10
git commit -m "foo: this will fail"
husky > commit-msg (node v10.1.0)
No staged files match any of provided globs.
⧗ input: foo: this will fail
✖ type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] [type-enum]

✖ found 1 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint

husky > commit-msg hook failed (add --no-verify to bypass)

standard-version 自动生成CHANGELOG

执行 standard-version 命令即可生成 CHANGELOG,或者向CHANGELOG增加这段提交历史对应的记录。

同时自动确定语义化版本号,并打上tag。

Install

1
2
# 全局安装
npm i -g standard-version

Usage

1
2
3
4
5
6
standard-version

✔ outputting changes to CHANGELOG.md
✔ committing CHANGELOG.md
✔ tagging release v1.0.1
ℹ Run `git push --follow-tags origin develop` to publish

CHANGELOG 效果如下:

standard-version


一套组合拳下来,正经的commit规范+自动生成CHANGELOG的机制就形成了。
此时再针对各个配置按需进行调整,就能run起来了。