git commit规范 、CHANGELOG生成 和版本发布的标准自动化

长期以来,大家是不是受限于这种情况:团队中每位成员提交代码时填写的信息随意,没有一定的规范,在出问题后想要定位到某次提交记录时更是难上加难,或者是加上了 commitlint之类的规范,也没有添加change log来记录每次发布版本的主要内容,以及版本发布的自动化。本篇文章就从 git commit入手,到自动生成 CHANGELOG,再到版本发布的标准自动化工具三个方面,一站式解决这些问题。

前言

Git 每次提交代码,都要写 Commit message(提交说明),否则就不允许提交。

$ git commit -m "hello world"

上面代码的-m参数,就是用来指定 commit mesage 的。

如果一行不够,可以只执行git commit,就会跳出文本编辑器,让你写多行。

$ git commit

基本上,你写什么都行(这里这里这里)。

git commit规范 、CHANGELOG生成 和版本发布的标准自动化

但是,一般来说,commit message 应该清晰明了,说明本次提交的目的。

git commit规范 、CHANGELOG生成 和版本发布的标准自动化\

目前,社区有多种 Commit message 的写法规范。本文介绍Angular 规范(见上图),这是目前使用最广的写法,比较合理和系统化,并且有配套的工具。

一、git commit 规范

1. Commit message 的格式

格式化的Commit message,有几个好处。

(1)提供更多的历史信息,方便快速浏览。

比如,下面的命令显示上次发布后的变动,每个commit占据一行。你只看行首,就知道某次 commit 的目的。

$ git log <last tag> HEAD --pretty=format:%s

git commit规范 、CHANGELOG生成 和版本发布的标准自动化

(2)可以过滤某些commit(比如文档改动),便于快速查找信息。

比如,下面的命令仅仅显示本次发布新增加的功能。

$ git log <last release> HEAD --grep feature

(3)可以直接从commit生成Change log。

Change Log 是发布新版本时,用来说明与上一个版本差异的文档,详见后文。

git commit规范 、CHANGELOG生成 和版本发布的标准自动化

2、Commit message 的格式

每次提交,Commit message 都包括三个部分:Header,Body 和 Footer。

<type>(<scope>): <subject> 
// 空一行 
<body> 
// 空一行 
<footer>

其中,Header 是必需的,Body 和 Footer 可以省略。

不管是哪一个部分,任何一行都不得超过72个字符(或100个字符)。这是为了避免自动换行影响美观。

2.1 Header

Header部分只有一行,包括三个字段:type(必需)、scope(可选)和subject(必需)。

(1)type

type用于说明 commit 的类别,只允许使用下面7个标识。

  • feat:新功能(feature)
  • fix:修补bug
  • docs:文档(documentation)
  • style: 格式(不影响代码运行的变动)
  • refactor:重构(即不是新增功能,也不是修改bug的代码变动)
  • test:增加测试
  • chore:构建过程或辅助工具的变动
  • perf: 提升性能\

如果type为 feat 和 fix,则该 commit 将肯定出现在 Change log 之中。其他情况(docs、chore、style、refactor、test)由你决定,要不要放入 Change log,建议是不要。

(2)scope

scope用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。

(3)subject

subject是 commit 目的的简短描述,不超过50个字符。

  • 以动词开头,使用第一人称现在时,比如change,而不是changed或changes
  • 第一个字母小写
  • 结尾不加句号(.)
<type>(<scope>): <short summary>
  │       │             │
  │       │             └─⫸ Summary in present tense. Not capitalized. No period at the end.
  │       │
  │       └─⫸ Commit Scope: The scope should be the name of the component affected
  │                           (as perceived by the person reading the 
  │                          changelog generated from commit messages).
  │
  └─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|test

2.2 Body

Body 部分是对本次 commit 的详细描述,可以分成多行。下面是一个范例。

More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. Further paragraphs come after blank lines. - Bullet points are okay, too - Use a hanging indent

有两个注意点。

(1)使用第一人称现在时,比如使用change而不是changed或changes。

(2)应该说明代码变动的动机,以及与以前行为的对比。

2.3 Footer

Footer 部分只用于两种情况。

(1)不兼容变动

如果当前代码与上一个版本不兼容,则 Footer 部分以BREAKING CHANGE开头,后面是对变动的描述、以及变动理由和迁移方法。

BREAKING CHANGE: isolate scope bindings definition has changed.
To migrate the code follow the example below:
Before:
scope: {
myAttr: ‘attribute’,
}
After:
scope: {
myAttr: ‘@’,
}
The removed inject wasn’t generaly useful for directives so there should be no code using it.

(2)关闭 Issue

如果当前 commit 针对某个issue,那么可以在 Footer 部分关闭这个 issue 。

Closes #234

也可以一次关闭多个 issue 。

Closes #123, #245, #992

2.4 Revert

还有一种特殊情况,如果当前 commit 用于撤销以前的 commit,则必须以revert:开头,后面跟着被撤销 Commit 的 Header。

revert: feat(pencil): add ‘graphiteWidth’ option This reverts commit 667ecc1654a317a13331b17617d973392f415f02.

Body部分的格式是固定的,必须写成This reverts commit <hash>.,其中的hash是被撤销 commit 的 SHA 标识符。

如果当前 commit 与被撤销的 commit,在同一个发布(release)里面,那么它们都不会出现在 Change log 里面。如果两者在不同的发布,那么当前 commit,会出现在 Change log 的Reverts小标题下面。

3. 交互式 commit message 生成工具:commitizen

commitizen 是一款可以交互式建立提交信息的工具。它帮助我们从 type 开始一步步建立提交信息。

安装命令如下。

$ npm install -g commitizen

git commit规范 、CHANGELOG生成 和版本发布的标准自动化

3.1 强制校验 commit message 格式

在日常开发中,为保证小伙伴们都能按照规范书写 commit message,我们可以使用 commitlint + husky 的方式强制推行规范。原理是在实际的 git commit 提交到远程仓库之前使用 git 钩子来验证信息,将阻止不符合规则的信息提交到远程仓库。

  • commitlint 可以帮助检查我们的 commit message 是否符合格式规范。

git commit规范 、CHANGELOG生成 和版本发布的标准自动化

  • huskey:Git hooks made easy.
  • You can use it to lint your commit messages, run tests, lint code, etc… when you commit or push. Husky supports all Git hooks.

二、生成 Change log

conventional-changelog-cli

conventional-changelog-cli 默认推荐的 commit 标准是来自angular项目,除了 angular 标准以外,目前集成了包括 atom, codemirror, ember, eslint, express, jquery 等项目的标准,具体可以根据自己口味来选用。

安装

# Help conventional-changelog --help
$ npm install -g conventional-changelog-cli

基本使用

$ conventional-changelog -p angular -i CHANGELOG.md -s

以上命令中参数-p angular用来指定使用的 commit message 标准,假如想使用atom的标准,则是:

$ conventional-changelog -p atom -i CHANGELOG.md -s

参数-i CHANGELOG.md表示从 CHANGELOG.md 读取 changelog, -s 表示读写 changelog 为同一文件。需要注意的是,上面这条命令产生的 changelog 是基于上次 tag 版本之后的变更(Feature、Fix、Breaking Changes等等)所产生的,所以如果你想生成之前所有 commit 信息产生的 changelog 则需要使用这条命令:

$ conventional-changelog -p angular -i CHANGELOG.md -s -r 0

其中 -r 表示生成 changelog 所需要使用的 release 版本数量,默认为1,全部则是0。

自定义参数

生成的 changlog 中有些常用内容可以通过自定义参数来根据需求更改,例如版本号、commit 地址等等。 changelog 中生成的版本号即是从 package.json 中获取 version 字段来的。commit 连接的仓库地址我们需要修改 package.json 中的repository地址,changelog 中 issuse 默认的连接地址也是根据 repository 来生成的。如果你使用了第三方的协作系统(例如 bitbucket), 那么你可以使用这个标准conventional-changelog-angular-bitbucket。或者像我们使用 redmine 来管理 isssue ,那么在生成 changelog 后可以使用 replace 工具来处理文本中的原有地址:

$ replace 'https://github.com/myproject/issues/' 'https://redmine.example.com' CHANGELOG.md

最后看看大致生成的效果:

git commit规范 、CHANGELOG生成 和版本发布的标准自动化

conventional-changelog 更多的选项配置可以看这里。

总和示例

  • 安装依赖
    • @commitlint/cli、@commitlint/config-conventional
      • commit message 格式校验工具,验证 message 格式是否符合规范。
    • husky
      • git钩子,用于在 commit 的时候,能调用 commitlint 校验 commit message
    • conventional-changelog-cli、conventional-changelog
      • 根据 git 提交历史,自动生成 CHANGELOG 工具。
npm install --save-dev @commitlint/cli @commitlint/config-conventional
npm install --save-dev conventional-changelog conventional-changelog-cli
npm install --save-dev husky
  • 配置 commitlint(commitlint.config.js)
module.exports = { 
    extends: ['@commitlint/config-conventional'] 
};
  • 配置 git 钩子
{ 
    "husky": { 
        "hooks": { 
            "commit-msg": "npx commitlint -E HUSKY_GIT_PARAMS" 
        } 
    } 
}
  • 配置 CHANGELOG 自动生成
"scripts": { 
    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0"
},
  • 执行几次提交
chore(init): init npm & git repo 
fix(cli): exit CLI with 1 when received SIGINT 
docs(readme): specify environment in code blocks 
feat(core): use cz-conventional-changelog as default adapter
  • 生成CHANGELOG
npx conventional-changelog -p angular -i CHANGELOG.md -s

git commit规范 、CHANGELOG生成 和版本发布的标准自动化

三、版本发布标准自动化(tandard-version)

standard-version 是一款遵循语义化版本( semver)和 commit message 标准规范 的版本和 changlog 自动化工具。通常情况线下,我们会在 master 分支进行如下的版本发布操作:

  1. git pull origin master
  2. 根据 pacakage.json 中的 version 更新版本号,更新 changelog
  1. git add -A, 然后 git commit
  2. git tag 打版本操作
  1. push 版本 tag 和 master 分支到仓库

其中2,3,4则是 standard-version 工具会自动完成的工作,配合本地的 shell 脚本,则可以自动完成一系列版本发布的工作了。

安装 & 使用

在这里我仍然推荐的全局安装:

$ npm install -g standard-version

或者

$ npm install --save-dev standard-version

执行:

$ standard-version

执行 standard-version 命令,我们会在控制台看到整个执行流程的 log 信息,在这里几个常用的参数需要注意下:

–release-as, -r 指定版本号

默认情况下,工具会自动根据 主版本(major),次版本( minor) or 修订版(patch) 规则生成版本号,例如如果你 package.json 中的version 为 1.0.0, 那么执行后版本号则是:1.0.1。自定义可以通过:

$ standard-version -r minor
output 1.1.0
$ standard-version -r 2.0.0
output 2.0.0
$ standard-version -r 2.0.0-test
output 2.0.0-test

需要注意的是,这里的版本名称不是随便的字符,而是需要遵循 语义化版本( semver) 规范的

–prerelease, -p 预发版本命名

用来生成预发版本, 如果当期的版本号是 2.0.0,例如:

$ standard-version --prerelease alpha
output 2.0.0-alpha.0

–tag-prefix, -t 版本 tag 前缀

用来给生成 tag 标签添加前缀,例如如果前版本号为 2.0.0,则:

$ standard-version --tag-prefix "stable-"
output tag: stable-v2.0.0

以上这几个参数可能我们用的比较多,还有其他选项可以通过 standard-version --help查看。

集成 npm

最后记得把命令集成到 npm package.json 的 scripts 中, 并配合 shell 脚本使用, 如下:

"scripts": {
  "release": "./scripts/release.sh",
  "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md && npm run changeissueurl",
  "changeissueurl": "replace 'https://github.com/myproject/issues/' 'https://redmine.example.com/' CHANGELOG.md"
},

// 配置好后使用 npm run 执行发布

$ npm run release

添加 release.sh 脚本:

#!/bin/bash

while [[ "$#" > 0 ]]; 
do 
    case $1 in
    -r|--release) release="$2"; shift;;
    # -b|--branch) branch="$2"; shift;;
    -t|--tag-prefix) prefix="$2"; shift;;
    -p|--prerelease) prerelease="$2"; shift;;
    -n|--no-verify) no="true"; shift;;
    *) echo "❌❌❌ Unknown parameter passed: $1"; exit 1;;
    esac; shift; 
done

# Default as minor, the argument major, minor or patch: 
if [ -z "$release" ]; then
    release="patch";
fi

echo "
上一篇:git (七) 提交


下一篇:git 修改远端 commit 信息