如何配置 Git 提交代碼檢查?
今天來看看如何使用 commitlint + Husky 在 git commit 時規(guī)范提交信息!
1. 基本概述
在多人協(xié)同的團隊中,規(guī)范化的提交信息更易讀,便于查找。每個人的 git commit 的信息不一樣,沒有一個機制就很難保證規(guī)范化。而 commitlint 就可以幫助我們解決這個問題。Commitlint 是一個自動化工具,它可以用來檢查提交約定,如果提交不符合規(guī)范,則會拒絕該提交。當(dāng)然,這些規(guī)則是可以配置的。
使用 commitlint 的優(yōu)點:
- 自動變更日志:由于遵循標(biāo)準約定的提交,可以使用standard-version 等工具自動生成變更日志;
- 更好的理解提交:具有特定類型和范圍的提交將幫助我們了解提交更改了哪些代碼;
- 遵守特定約定:多人協(xié)作的的大項目中,commitlint 可以阻止不規(guī)范的提交,以便提交遵守定義的約定。
Git 允許開發(fā)人員在觸發(fā)特定事件時執(zhí)行一些操作,它被稱為git hook。我們可以在git工作流程的很多階段執(zhí)行操作,例如:
- pre-commit
- pre-push
- pre-rebase
- post-update
然而,這需要在本地的 .git 文件中配置的,因此在默認情況下,每個開發(fā)人員都需要安裝需要的 hook,這樣才能執(zhí)行特定的檢查。幸運的是,Husky 可以幫助我們解決這個問題,husky 可以讓我們在項目中方便添加 git hooks。Husky 不僅可以用于強制執(zhí)行提交約定,還可以在提交時運行靜態(tài)代碼分析、測試、自動代碼格式化等。
因此我們可以使用Husky和commitlint來檢查提交信息是否符合提交規(guī)范。
2. 安裝配置
由于 Commitlint 使用了 Husky,我們需要先安裝它。Husky 是作為開發(fā)依賴項提供的,因此它僅在本地使用,不會與生產(chǎn)代碼捆綁在一起。在終端中執(zhí)行以下命令來安裝Husky:
// 使用npm安裝
npm install --save-dev husky
// 使用yarn安裝
yarn add husky --dev
現(xiàn)在我們需要定義一個 Hook 來檢查提交信息,這個 hook 叫做 commit-msg,需要在 package.json 中定義:
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
或者,我們也可以項目的根目錄定義一個.huskyrc配置文件,在這個配置文件中單獨定義 Husky 配置:
{
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
這段代碼就告訴git hooks,當(dāng)我們執(zhí)行 git commit -m 'message' 時將觸發(fā)commit-msg 事件鉤子并通知 husky,從而執(zhí)行 commitlint -e $GIT_PARAMS命令。該命令會讀取commitlint.config.js 配置的規(guī)則并對提交的信息進行校驗,若校驗不通過,就會在終端中輸出錯誤,commit 被終止。
有了 Husky,接下來就需要安裝 Commitlint CLI,它將由 Husky hook 執(zhí)行以驗證提交消息:
// 使用npm安裝
npm install --save-dev @commitlint/cli
// 使用yarn安裝
yarn add @commitlint/cli --dev
Commitlint 只是用于檢查提交消息是否符合規(guī)范的工具。所以,我們還需要一個提交規(guī)范。這個配置是完全可以自定義的,當(dāng)然,Commitlint 提供了當(dāng)前支持的配置之外的一些配置:
- @commitlint/config-conventional
- @commitlint/config-lerna-scopes
- @commitlint/config-patternplate
- @commitlint/config-angular
- @commitlint/config-angular-type-enum
Commitlint 推薦使用 config-conventional 配置去規(guī)范 commit,安裝命令如下:
// 使用npm安裝
npm install --save-dev @commitlint/config-conventional
// 使用yarn安裝
yarn add @commitlint/config-conventional --dev
最后一步,我們需要創(chuàng)建一個 Commitlint 配置文件,在文件中可以定義需要使用的配置,也就是上面剛安裝的配置規(guī)范。只需要在項目的根目錄創(chuàng)建一個名為commitlint.config.js的規(guī)范:
module.exports = {
extends: ['@commitlint/config-conventional'],
};
到這里就完成了 Husky 和 Commitlint 的安裝和配置。其執(zhí)行流程如下:
3. 提交規(guī)范
Commitlint 推薦使用 config-conventional 配置,它的提交格式如下:
git commit -m <type>[optional scope]: <description>
這里面有三個參數(shù):
- type:必須,表示提交的改動類型;
- optional scope:可選,表示提交的修改范圍,需要使用括號包裹;
- description:必須,表示提交的內(nèi)容描述。
常見的 type 類型如下:
- build: 影響構(gòu)建組件的更改,如構(gòu)建工具、依賴項、項目版本等;
- chore: 比如構(gòu)建流程、輔助工具等的更改。
- ci: 持續(xù)集成的更改;
- docs: 影響文檔的更改。
- feat: 引入新功能的更改。
- fix: 修補錯誤的更改。
- perf: 優(yōu)化相關(guān),比如提升性能、體驗。
- refactor: 代碼重構(gòu)的更改。
- revert: 還原先前提交的更改。
- style: 代碼格式的更改(空格、格式、缺少分號等)。
- test: 添加缺失測試或糾正現(xiàn)有測試的更改。
例如:
git commit -m 'feat: 增加 xxx 功能'
git commit -m 'feat(xx模塊): 增加 yyy 功能'
git commit -m 'fix: 修復(fù) xxx 問題'
下面是一個git提交信息的例子:
下面來看看config-conventional 配置是如何定義的:
module.exports = {
parserPreset: 'conventional-changelog-conventionalcommits',
rules: {
'body-leading-blank': [1, 'always'],
'body-max-line-length': [2, 'always', 100],
'footer-leading-blank': [1, 'always'],
'footer-max-line-length': [2, 'always', 100],
'header-max-length': [2, 'always', 100],
'scope-case': [2, 'always', 'lower-case'],
'subject-case': [
2,
'never',
['sentence-case', 'start-case', 'pascal-case', 'upper-case'],
],
'subject-empty': [2, 'never'],
'subject-full-stop': [2, 'never', '.'],
'type-case': [2, 'always', 'lower-case'],
'type-empty': [2, 'never'],
'type-enum': [
2,
'always',
[
'build',
'chore',
'ci',
'docs',
'feat',
'fix',
'perf',
'refactor',
'revert',
'style',
'test',
],
],
},
};
這里面包含了很多配置項,這些配置項由配置名和配置數(shù)組組成,數(shù)組的第一個元素是level,可選值0、1、2,0為disable,1為warning,2為error,第二個元素為是否應(yīng)用與否,可選值always、never,第三個元素是配置規(guī)則。
上面是config-conventional的默認規(guī)則,我們可以在 commitlint.config.js 配置文件中覆蓋這些規(guī)則:
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'scope-case': [0, 'never'],
'header-max-length': [0, 'always', 120]
}
};