為什么需要
- 降低Review成本,可以明確知道本次提交的改變和影響
- 規(guī)范整個(gè)Team的提交習(xí)慣,對(duì)技術(shù)素養(yǎng)的養(yǎng)成有益
- 可以通過(guò)統(tǒng)一工具,抽取規(guī)范的message自動(dòng)形成change log

GitHub Angular Demo
目前Github的Angular項(xiàng)目,就是完全采用規(guī)范的Git Message來(lái)進(jìn)行日常的提交管理和發(fā)布管理的,下面是這個(gè)項(xiàng)目的Commit記錄,和自動(dòng)根據(jù)commit生成的change log

遵循什么規(guī)范
目前,使用較多的是AngularJS規(guī)范
# 包括三個(gè)部分:Header,Body 和 Footer
<type>(<scope>): <subject>
// 空一行
<body>
// 空一行
<footer>
Header
包括三個(gè)字段:type(必需)、scope(可選)和subject(必需)
任何一行都不能超過(guò)100個(gè)字符
type
用于說(shuō)明 commit 的類別,類型包含如下幾種
- feat: A new feature
- fix: A bug fix
- docs: Documentation only changes
- style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
- refactor: A code change that neither fixes a bug nor adds a feature
- perf: A code change that improves performance
- test: Adding missing or correcting existing tests
- chore: Changes to the build process or auxiliary tools and libraries such as documentation generation
- revert: Reverts a previous commit
- build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
- ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
如果type為feat和fix,則該 commit 將肯定出現(xiàn)在 Change log 之中。其他情況由你決定,要不要放入 Change log。
scope
用于說(shuō)明 commit 影響的范圍,比如數(shù)據(jù)層、控制層、視圖層等等,視項(xiàng)目不同而不同
subject
subject是 commit 目的的簡(jiǎn)短描述
Body
Body 部分是對(duì)本次 commit 的詳細(xì)描述,可以分成多行
Footer
Footer 部分只用于兩種情況
不兼容變動(dòng)
如果當(dāng)前代碼與上一個(gè)版本不兼容,則 Footer 部分以BREAKING CHANGE開(kāi)頭,后面是對(duì)變動(dòng)的描述、以及變動(dòng)理由和遷移方法
關(guān)閉問(wèn)題
如果當(dāng)前 commit 針對(duì)某個(gè)issue,那么可以在 Footer 部分關(guān)閉這個(gè) issue。
如:Closes #123, #245, #992
工具約束
我們的目標(biāo)還是要通過(guò)工具生成和約束
Commitizen
commitizen/cz-cli 代替git commit
我們需要借助它提供的 git cz 命令替代我們的 git commit 命令, 幫助我們生成符合規(guī)范的 commit message
# 如何安裝,在安裝之前請(qǐng)先安裝npm
# 全局安裝 commitizen
npm install commitizen -g
除此之外, 我們還需要為 commitizen 指定一個(gè) Adapter 比如: cz-conventional-changelog (一個(gè)符合 Angular團(tuán)隊(duì)規(guī)范的 preset). 使得 commitizen 按照我們指定的規(guī)范幫助我們生成 commit message
# 進(jìn)入到我們項(xiàng)目的根目錄
cd your_repo_root_path
# 初始化package.json
npm init --yes
# 為commitizen指定適配器
commitizen init cz-conventional-changelog --save-dev --save-exact
現(xiàn)在我們就可以用git cz去進(jìn)行提交了

但是問(wèn)題來(lái)了,如果我們此時(shí)還用git commit -m "" 去提交,也是允許的,但是這肯定不是我們想要的,因?yàn)槲覀冃枰獙?duì)message進(jìn)行格式限制,所以,我們需要下面的檢驗(yàn)插件commitlint + Husky
Commitlint + Husky
commitlint可以幫助我們檢查校驗(yàn)提交的message
如果我們提交的不符合指向的規(guī)范, 直接拒絕提交
校驗(yàn) commit message 的最佳方式是結(jié)合 git hook, 所以需要配合Husky
# 在我們的項(xiàng)目根目錄
npm install --save-dev @commitlint/{config-conventional,cli}
npm install husky --save-dev
# 在package.json尾部加入如下結(jié)構(gòu)
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
# 在項(xiàng)目根目錄新增文件commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', [
"feat", "fix", "docs", "style", "refactor", "perf", "test", "build", "ci", "chore", "revert"
]],
'scope-empty': [2, 'never'],
'subject-full-stop': [0, 'never'],
'subject-case': [0, 'never']
}
};
現(xiàn)在,我們可以在試試git commit -m "test"看看是否可以正常提交,應(yīng)該會(huì)得到下面的攔截記錄

Standard Version
通過(guò)以上工具的幫助, 我們的工程 commit message 應(yīng)該是符合Angular團(tuán)隊(duì)那套,這樣也便于我們借助standard-version這樣的工具, 自動(dòng)生成 CHANGELOG, 甚至是語(yǔ)義化的版本號(hào)(Semantic Version)
# 在項(xiàng)目根目錄
npm install --save-dev standard-version
# 在scripts結(jié)構(gòu)體中加入執(zhí)行腳本
{
"scripts": {
"release": "standard-version"
}
}
# 生成changelog
# 第一次生成
npm run release -- --first-release
# 后續(xù)生成
npm run release
會(huì)在我們項(xiàng)目根目錄生成一個(gè)CHANGELOG.md文件,如下所示

項(xiàng)目中如何使用
如果我們已經(jīng)完成了上述操作,會(huì)發(fā)現(xiàn)我們最終會(huì)得到一個(gè)package.json,我們只需要把package.json / commitlint.config.js提交版本庫(kù)即可
把node_modules 和 package-lock.json都加入git忽略文件
下次再新clone項(xiàng)目后,直接在項(xiàng)目根目錄運(yùn)行npm install即可完成上述所有步驟
PS:NPM有時(shí)候國(guó)外鏡像不穩(wěn)定,可以切換淘寶鏡像
npm config set registry https://registry.npm.taobao.org