提交信息(Commit message),就是在项目的版本管理中,每次提交(commit)时要编写的那段提交信息。提交信息的主要用途是,告诉这个项目的其他人这次修改做了些什么。
- 一般小步提交,防止一次提交修改过多的文件,导致后期的修改,维护和撤销困难;
- 提交信息应该清晰明了,说明本次提交的目的。
为了标准化提交信息,便出现了一些写法规范。其中,使用较为广泛的是 AngularJS 的规范:
格式
包括三个部分:Header,Body 和 Footer
<type>(<scope>): <subject>
<空行>
<body>
<空行>
<footer>
Header
包括三个字段:type(必需)、scope(可选)和subject(必需)
type
用于说明 commit 的类别,只允许使用下面7个标识:
- feat:新功能(feature)
- fix:修补bug
- docs:文档(documentation)
- style: 格式(不影响代码运行的变动)
- refactor:重构(即不是新增功能,也不是修改bug的代码变动)
- test:增加测试
- chore:构建过程或辅助工具的变动
scope
用于说明 commit 影响的范围,可选值。通常是文件、路径、功能等。 Angular 项目中,scope 也指定了范围。
Subject
commit 目的的简短描述。不超过50个字符。
Body
commit 的详细描述。
Footer
- Break Changes: 不兼容变动;BREAKING CHANGE开头,后面是对变动的描述、以及变动理由和迁移方法。
- Closes: 关闭 Issue;如果当前 commit 针对某个issue,那么可以在 Footer 部分关闭这个 issue;如:
Closes #123, #245, #992
Revert
特殊情况,在当前 commit 是用来撤销以前某个 commit 的时候。须以 revert:
开头,后面跟着被撤销 commit 的 Header。
另外
规范的 Commit message 除了手写外,也可以借助一些工具,帮助我们生成规范的提交信息。比如,Commitizen。
Commitizen
本着简单易操作的原则,我们参照 Commitizen 的手册来搞。
安装: 可以全局安装也可以本地安装,我更倾向用本地安装:
npm install --save-dev commitizen
本地安装,要使用 npx 命令初始化:
npx commitizen init cz-conventional-changelog --save-dev --save-exact
这行代码干了:
- 安装 cz-conventional-changelog;
- 更新 package.json 里的依赖项;
- package.json 里添加
config.commitizen
项:
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
}
全局安装的话,当需要 git commit
命令时,就可以使用 git cz
了。
对于局部安装,每次使用 npx 不够优雅,一般会定义一个 npm 运行脚本,官方的例子:
"scripts": {
"commit": "git-cz"
}
这时候,运行 npm run commit
,会有如下提示:
然后,按着提示一步一步搞就行了。
Vue
因为最近在看 Vue 的源码,下意识的去瞅了瞅 Vue 的 package.json:
- 有 cz-conventional-changelog 依赖项;
- 添加了
config.commitizen
项; - 有 commit 运行脚本。
所以,Vue 也是采用了上面的方式搞的。
changelog
规范提交信息还有一个目的,就是能够自动生成 changelog 文件。 Angular 使用了 git-release-notes 生成 changelog.md 的。
而 Vue 项目中没有 changelog.md 文件,其 changelog 在 release note 中。 Vue 也会生成 changelog 文件,只是没提交,作者把内容复制到了 release note 了。
Vue 中的 release:note
命令:
"release:note": "node scripts/gen-release-note.js",
gen-release-note.js
很简单,利用库 conventional-changelog 生成 changelog 文件:
const version = process.argv[2] || process.env.VERSION
const cc = require('conventional-changelog')
const file = `./RELEASE_NOTE${version ? `_${version}` : ``}.md`
const fileStream = require('fs').createWriteStream(file)
cc({
preset: 'angular',
pkg: {
transform (pkg) {
pkg.version = `v${version}`
return pkg
}
}
})
.pipe(fileStream)
.on('close', () => {
console.log(`Generated release note at ${file}`)
})
在我们自己的项目中,可以像 Vue 一样写个脚本进行配置,但是,对于没有什么幺蛾子的小项目,用 conventional-changelog 提供的 CLI 版 更适合一些:
npm i -D conventional-changelog-cli
然后执行以下命令,可以生成 changelog 文件:
npx conventional-changelog -p angular -i CHANGELOG.md -s
命令太长记不住,搞个 npm 运行脚本:
"scripts": {
"release:note": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md"
}
关于 conventional-changelog-cli 的其他玩法和工作流,移步这里 。
如此,便生成了一个看似很正经的 changelog 文件。在 GitLab 中预览一下:
检查提交信息
我们不能保证项目中每个人都严格遵守规则,所以同其他的规则的落地一样,需要通过检查工具来强制执行。
基本原理是在 git 的 commit-msg
钩子中去判断提交的信息是否合法。
除了自己在 .git/hooks 目录下手写 shell 脚本这种原始的方法外,市面上还有多种解决方案,大概看了一下后,我觉得去看看大牛是咋搞的。 依然是参考 Vue 项目:
原来(V2.5.14前)的 Vue 是这么干的:
-
build/git-hooks/commit-msg 检查脚本内容如下:
commit_regex='^Merge.+|(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert|types)(\(.+\))?: .{1,50}' if ! grep -iqE "$commit_regex" "$1"; then echo echo " Error: proper commit message format is required for automated changelog generation." echo echo " - Use \`npm run commit\` to interactively generate a commit message." echo " - See .github/COMMIT_CONVENTION.md for more details." echo exit 1 fi
-
克隆仓库后,需要运行
npm install & npm run setup
,在 .git/hooks 下创建脚本的链接:"scripts": { "setup": "node build/setup.js" }
setup.js:
const { test, ln, chmod } = require('shelljs') function installHooks () { if (test('-e', '.git/hooks')) { ln('-sf', '../../build/git-hooks/commit-msg', '.git/hooks/commit-msg') chmod('+x', '.git/hooks/commit-msg') } } installHooks()
在 2.5.14之后的版本中,尤大用了自己的 yorkie,可以在 package.json
中很方便的定义git的各种钩子,并且也不需要 npm run setup
了。
"gitHooks": {
"pre-commit": "lint-staged",
"commit-msg": "node scripts/verify-commit-msg.js"
}
verify-commit-msg.js 的内容为:
const chalk = require('chalk')
const msgPath = process.env.GIT_PARAMS
const msg = require('fs').readFileSync(msgPath, 'utf-8').trim()
const commitRE = /^(revert: )?(feat|fix|polish|docs|style|refactor|perf|test|workflow|ci|chore|types|build)(\(.+\))?: .{1,50}/
if (!commitRE.test(msg)) {
console.log()
console.error(
` ${chalk.bgRed.white(' ERROR ')} ${chalk.red(`invalid commit message format.`)}\n\n` +
chalk.red(` Proper commit message format is required for automated changelog generation. Examples:\n\n`) +
` ${chalk.green(`feat(compiler): add 'comments' option`)}\n` +
` ${chalk.green(`fix(v-model): handle events on blur (close #28)`)}\n\n` +
chalk.red(` See .github/COMMIT_CONVENTION.md for more details.\n`) +
chalk.red(` You can also use ${chalk.cyan(`npm run commit`)} to interactively generate a commit message.\n`)
)
process.exit(1)
}
实操
安装 yorkie 和 chalk
npm i -D yorkie chalk
在 package.json
中添加:
"gitHooks": {
"commit-msg": "node build/verify-commit-msg.js"
}
在 build 目录下创建 verify-commit-msg.js 文件:
const chalk = require('chalk')
const msgPath = process.env.GIT_PARAMS
const msg = require('fs')
.readFileSync(msgPath, 'utf-8')
.trim()
const commitRE = /^(revert: )?(feat|fix|polish|docs|style|refactor|perf|test|workflow|ci|chore|types|build)(\(.+\))?: .{1,50}/
if (!commitRE.test(msg)) {
console.log()
console.error(
` ${chalk.bgRed.white(' 错误 ')} ${chalk.red(`提交信息的格式不正确。`)}\n\n` +
chalk.red(`使用 npm run commit 或者使用格式正确的提交信息`)
)
process.exit(1)
}
更新
使用上面的方法,在 win10 系统中,无论在 powershell,cmd 还是 Windows Terminal 上,打印的文字只有白色(lint-staged 的打印也没有其他颜色),也没有 chalk 模块的啥报错。
另外:
- yorkie 本身就 fork 自 husky;
- yorkie 关闭了 Issue,社区支持差,使用者少。
于是,决定改用更通用的方案:husky:
npm uninstall yorkie
npm i -D husky
package.json
将原来的 gitHooks
字段删掉,添加 husky
字段:
{
"husky": {
"hooks": {
"commit-msg": "env FORCE_COLOR=1 node script/verify-commit-msg.js"
}
}
}
verify-commit-msg.js 更新:
// const msgPath = process.env.GIT_PARAMS // 删掉这句
const msgPath = process.env.HUSKY_GIT_PARAMS;