作为一名程序员,代码托管大多数用的都是git。在个人的学习或工作中,都会提交代码至git远程仓库中。随着项目增大,开发人员增多,每个人在提交代码时携带的message也是层出不穷,五花八门。
有这样的:

这样的:

随着时间推移,项目历史久远,开发人员回溯历史的时候,看到张三\李四在之前提交的代码记录全部都是abcd 1234。心中瞬间一万只草泥马奔腾~ 。由此可见,规范正式的commit提交对开发者而言是多么重要
那优雅规范的commit提交是什么样子的呢?

从图中可以清晰知道何人何时做了什么事情。 如果我们也想使用这种格式来规范自己或团队的commit提交,该如何做呢? 我们前期需要准备以下三样东西:
- husky (Github传送门)
- commitizen/cz-cli (Github传送门)
- commitlint (Github传送门)
1. husky v8(8.0.2)
一个简单易用的Hooks工具,可以让我们快速的使用Git Hooks。
1.1 🤔Git Hooks是什么?
Git Hook是Git在工作流的生命周期内调用外部函数的Hook函数。以Git commit 举例,在commit过程中,git为开发者准备了4个Hooks。按照生命周期从前往后依次是:
- pre-commit
提交之前 (我们可以在这里做一些代码格式化的操作)
- prepare-commit-msg
准备提交 (我们提交信息要用到的一些规范化日志的工具准备工作)
- commit-msg
开始提交 (验证我们提交的信息是否符合规范)
- post-commit
提交成功 (已经将代码提交了。即 git commit之后调用的Hooks,这个我们一般用的比较少)
1.2 😀安装 Husky(单个项目安装)
npm install husky -d
or
yarn add husky --dev
安装好husky后,在package.json script 中添加一条prepare的husky安装指令。 (prepare是npm的生命周期执行命令,详情可见:Life Cycle Scripts,这里不做详细说明)
安装好husky后,在package.json script 中添加一条prepare的husky安装指令。(prepare是npm的生命周期执行命令,详情可见:Life Cycle Scripts,这里不做详细说明)
添加husky安装指令如下:
"scripts": {
"prepare": "husky install"
}
添加完上述命令之后,运行npm install 或 yarn install,npm会在当前install执行完之后,运行prepare脚本,会把husky安装一次,安装成功之后,控制台会输出husky - Git hooks installed的字样,同时项目根目录里会出现.husky的文件夹。

1.3 😀添加Git Hooks
通过husky我们可以添加Git hooks,我们先尝试添加pre-commit的hook。在scripts中,我们添加两条命令:
"scripts":{
"husky": "node node_modules/husky/lib/bin",//快速执行husky
"add-pre-commit": "npm run husky add .husky/pre-commit \"npm run pre-commit\"",
"pre-commit": "console.log('pre-commit')"
}
add-pre-commit命令是通过运行husky,添加一条git 出于pre-commit周期时执行的命令。
pre-commit命令是我们模拟开始格式化文件的相关操作
在上面代码中,我们告诉husky,当git处于pre-commit周期时,会同步运行我的pre-commit命令去做一些“格式化代码”的事情。

同理接下来我们再补全其他git提交工作流的hooks,并关联到我们在1.2中提到npm prepare环节中,从而实现clone项目到install安装依赖的环节就会把我们的husky以及对应的git hooks安装好:
"scripts": {
"prepare": "husky install & npm run add-pre-commit & npm run add-prepare-commit-msg & npm run add-commit-msg & npm run add-post-commit",
"husky": "node node_modules/husky/lib/bin",
"add-pre-commit": "npm run husky add .husky/pre-commit \"npm run pre-commit\"",
"add-prepare-commit-msg": "npm run husky add .husky/prepare-commit-msg \"npm run prepare-commit-msg\"",
"add-commit-msg": "npm run husky add .husky/commit-msg \"npm run commit-msg\"",
"add-post-commit": "npm run husky add .husky/post-commit \"npm run post-commit\"",
"pre-commit": "console.log('pre-commit')",
"prepare-commit-msg": "console.log('prepare-commit-msg')",
"commit-msg": "console.log('commit-msg')",
"post-commit": "console.log('post-commit')"
}
1.4 🤔hooks重复添加的问题
当在package.json新增加一个依赖,重新npm install 或yarn install。依旧会执行我们的“prepare” 指令,会导致我们的Hooks一直在新增,从而出现hooks重复的问题。所以在每次install前应该先删除本地的Hooks,重新添加。
先添加删除代码
var fs = require('fs'); // 引入fs模块
function deleteall(path) {
var files = [];
if (fs.existsSync(path)) {
files = fs.readdirSync(path);
files.forEach(function (file, index) {
var curPath = path + "/" + file;
if (fs.statSync(curPath).isDirectory()) { // recurse
deleteall(curPath);
} else { // delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
}
throw Error(`${path}文件夹不存在`)
};
const path = '.husky';
try {
console.log(`开始删除${path}文件夹`);
deleteall('.husky');
console.log(`删除${path}文件夹成功`);
} catch (error) {
console.log(`删除${path}文件夹失败,可能该文件夹不存在`);
}
再给“prepare” 指令中,每次运行前执行一次delete。husky就安装完成了。
完整package.json如下:
{
"scripts": {
"prepare": "npm run delete-husky ./husky && husky install & npm run add-pre-commit & npm run add-prepare-commit-msg & npm run add-commit-msg & npm run add-post-commit",
"husky": "node node_modules/husky/lib/bin",
"delete-husky": "node ./script/delete.js",
"add-pre-commit": "npm run husky add .husky/pre-commit \"npm run pre-commit\"",
"add-prepare-commit-msg": "npm run husky add .husky/prepare-commit-msg \"npm run prepare-commit-msg\"",
"add-commit-msg": "npm run husky add .husky/commit-msg \"npm run commit-msg\"",
"add-post-commit": "npm run husky add .husky/post-commit \"npm run post-commit\"",
"pre-commit": "console.log('pre-commit')",
"prepare-commit-msg": "console.log('prepare-commit-msg')",
"commit-msg": "console.log('commit-msg')",
"post-commit": "console.log('post-commit')"
},
"devDependencies": {
"husky": "^8.0.2"
}
}
2. 😋commitizen/cz-cli
commitizen/cz-cli是一个命令行工具,作用于提交代码时的一些规则选项,通过这个命令行我们可以按照期望规则自主有序的选择message内容。如下图:
2.1 😀安装和配置
先安装cz-cli
npm install commitizen -D
or
yarn add commitizen --dev
安装好cz-cli之后,我们还需要安装一个规则适配器,这个规则适配器会告诉cz-cli以哪种规则作为基准的一个规范。这里我们以AngularJS的提交规则作为规范,并且为了直观美化,我们使用国人修改的汉化版:cz-conventional-changelog-zh。也还可以选择其他规则适配器
npm install cz-conventional-changelog-zh -D
or
yarn add cz-conventional-changelog-zh --dev
再把cz-conventional-changelog-zh通过配置文件的方式和cz-cli关联起来。为了更加美观,我们尝试把emoji添加到commit type中(即上图中feat、fix这种类型)。配置方式有配置文件或package.json里配置两种,具体可参考官方配置说明。在这里我选择使用配置文件的方式来进行配置。在当前项目根目录里新建 .czrc 文件。文件内容完成如下:
{
"path": "cz-conventional-changelog-zh",
"disableScopeLowerCase": false,
"disableSubjectLowerCase": false,
"maxHeaderWidth": 100,
"maxLineWidth": 100,
"defaultType": "",
"defaultScope": "",
"defaultSubject": "",
"defaultBody": "",
"defaultIssues": "",
"types": {
"feat": {
"description": "✨ 开发了一个新功能或新模块",
"title": "Features | 新功能",
"emoji": "✨"
},
"fix": {
"description": "🐛 解决了一些bug",
"title": "Bug Fixes | Bug 修复",
"emoji": "🐛"
},
"docs": {
"description": "📚 文档相关的更改",
"title": "Documentation | 文档",
"emoji": "📚"
},
"style": {
"description": "🎨 不影响功能的代码格式化修改。例如: 删除空格",
"title": "Styles | 风格",
"emoji": "🎨"
},
"refactor": {
"description": "📦 不修复错误、不新加功能的代码重构",
"title": "Code Refactoring | 代码重构",
"emoji": "📦"
},
"perf": {
"description": "⚡️ 性能优化、调优",
"title": "Performance Improvements | 性能优化",
"emoji": "⚡️"
},
"test": {
"description": "✅ 添加测试或修改测试",
"title": "Tests | 测试",
"emoji": "✅"
},
"chore": {
"description": "♻️ 其他更新",
"title": "Chores | 其他更新",
"emoji": "♻️"
},
"revert": {
"description": "⏪ 回退到某个版本",
"title": "Reverts | 回退",
"emoji": "⏪"
},
"conflict": {
"description": "🔀 解决冲突",
"title": "Conflict | 冲突",
"emoji": "🔀"
},
"delete":{
"description": "🔥删除代码或者文件",
"title": "Delete | 删除",
"emoji": "🔥"
}
}
}
当前安装方式是当前项目安装,非全局安装。即官方的git-cz命令无法使用,我们可以在script里加一条命令commit:"git-cz"
,再使用npm或者yarn来运行commit命令查看效果:
可以看到已经配置成功,但对大多数开发者而言不是很友好。为什么呢?因为当要运行提交代码命令时,不再是传统的git commit
的命令,而是运行自己写的commit:"git-cz"
命令。🙁🙁🙁 那么如何关联到git commit
命令呢?
2.2 🧐 通过Husky关联Git Hooks
可以通过上文提及的Husky来和git commit
这类git工作代码进行关联。按照cz-cli的推荐方式,给我们的prepare-commit-msg
命令增加如下内容:
#!/bin/bash
exec < /dev/tty && node_modules/.bin/cz --hook || true
这是一段bash命令,用于告诉git来运行cz-cli。我使用的是windows环境,bash命令默认是用不了的。因此还需要添加两个windows可执行的脚本代码。(exec-git-cz.cmd和parepare-commit-msg.cmd)
参考链接:https://github.com/commitizen/cz-cli/issues/627)
@ECHO off
SETLOCAL
CALL :find_dp0
set "_prog=%dp0%node_modules\.bin\git-cz.cmd"
IF NOT EXIST "%_prog%" (
ECHO "%_prog% does not exist, please install commitizen"
ENDLOCAL
EXIT /b 1
)
call "%_prog%" %*
ENDLOCAL
EXIT %errorlevel%
:find_dp0
SET dp0=%~dp0
EXIT /b
@ECHO off
SETLOCAL
CALL :find_dp0
set "_prog=%dp0%exec-git-cz.cmd"
IF NOT EXIST "%_prog%" (
ECHO "%_prog% does not exist"
ENDLOCAL
EXIT /b 1
)
start /wait call "%_prog%" --hook %*)
:find_dp0
SET dp0=%~dp0
EXIT /b
然后将我们script-->prepare-commit-msg的内容替换为parepare-commit-msg.cmd
。接着尝试运行git commit
命令,windows会新弹出一个CMD窗口来选择此次提交的信息选项。
3.😋commitlint
和cz-cli一样也是一个命令行,只不过cz-cli是用于提交commit message,而commitlint用于检测commit message是否符合规范的。因为之前在配置commitizen/cz-cli阶段中是以AngularJS的提交规则作为规范。所以我们再配置commitlint也用此规范的适配器做为校验。
3.1 😀安装 @commitlint/config-conventional @commitlint/cli
npm install @commitlint/config-conventional @commitlint/cli -d
or
yarn add @commitlint/config-conventional @commitlint/cli --dev
3.2 👨💻配置
在当前项目根目录新建commitlint.config.js
,添加如下内容
module.exports = {
extends: ["@commitlint/config-conventional"],
};
配置内容是告诉commitlint,我们使用哪种适规则配置。(查看更多适配器可点击这里)添加完之后,当commit message为错误的信息的时候则会被commitlint打回,无法提交也更加无法push到仓库中。
3.3 💪关联husky
当我们输入完commit message时,需要告诉husky,给我们执行commit-msg
hook函数,从而在我们在执行git commit
时就验证我们的提交的内容是是否规范。因此,我们需要把commit-msg
内容修一下。官方推荐如下:
npx --no -- commitlint --edit ${1}
乍眼一看这段命令没有什么问题,通俗解释出来就是让npm
运行commitlint
去打开${1}
这份文件。问题来了:${1}
是shell变量,windows也不支持。所以我们需要将其修改为如下命令才可以适配,把${1}
修改为.git/COMMIT_EDITMSG
(COMMIT_EDITMSG是我们此次提交输入的信息暂存区)。
node_modules/.bin/commitlint --edit .git/COMMIT_EDITMSG
添加完之后,运行git commit
,就会自动校验内容是否合规了。