在了解husky之前,先了解一下Git hooks。

GitHooks

官方文档:https://git-scm.com/docs/githooks

是什么

git hooks 是git提供的hooks,类似Vue的生命周期钩子函数一样,Git也会在它运行周期里面的某些时间点,提供一些让用户添加和执行自定义的函数。

默认情况下,hooks 目录是 $GIT_DIR/hooks ,但可以通过 core.hooksPath 配置变量进行更改。打开之后能看到很多Hookssample

https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f80e778aa94147f2acbaefa8b5fb5bce~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp

Simple文件的内容如下:

https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/55484a649af74601b493eeffa8a31bd0~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp

可以发现,Githooks文件其实是一个shell脚本,可以通过修改shell脚本来实现自定义功能

常用的hooks

pre-commit 预提交

该钩子由 git-commit调用,可以使用 --no-verify 选项绕过。它不带任何参数,在提交之前调用。从此脚本中以非零状态退出会导致 git commit 命令在创建提交前中止。

prepare-commit-msg 准备-提交-消息

在准备好默认日志消息之后、启动编辑器之前,git-commit[1] 会立即调用此挂钩。

它需要一到三个参数。

  • 第一个是包含提交日志消息的文件的名称。
  • 第二个是提交消息的来源,可以是: message (如果给出了 -m 或 -F 选项); template (如果给出了 -t 选项或设置了配置选项 commit.template ); merge (如果提交是合并或存在 .git/MERGE_MSG 文件); squash (如果 .git/SQUASH_MSG 文件存在);或 commit ,后跟提交对象名称(如果给出了 -c 、 -C 或 --amend 选项)。

如果退出状态非零, git commit 将中止。

Githooks能解决什么

回到项目中的实际问题

  • 问题:远程仓库的代码中存在语法错误
  • 原因:有成员没有仔细检查,误把有语法错误的代码上传了
  • 解决方法:在commit之前检查在暂存区的代码,看是否存在语法错误 显然,如果只靠当前成员去检查自己添加到暂存区的代码有没有错误,是很不可靠且低效的行为,那么这里我们可以用到pre-commit这一个hook了,它可以在git commit之前执行我们自定义的语句,当执行错误或者返回非0状态的时候阻止commit行为。

除了这个Hook以外,还有哪些就不一一介绍了,一般的Git操作,例如commit、push等会提供操作前或操作后的Hook,有兴趣的可以查看Githooks去了解。

Husky

git hooks和husky有什么关系呢?用一句话概括,那就是husky能够简化上述创建或者修改Githooks的操作流程。

husky工作原理

  • 初始化:先检查该项目是否通过Git来托管代码的
1
2
3
4
if (spawnSync('git', ['rev-parse']).status !== 0) {
l('not a Git repository, skipping hooks installation');
return;
}
  • 创建.husky文件夹:创建.husky文件夹用来存放Githookshusky的配置
1
2
3
4
5
6
7
8
9
10
11
// 创建 .husky/_
mkdirSync(join(dir, '_'), { recursive: true });

// 创建 .husky/.gitignore
writeFileSync(join(dir, '.gitignore'), '_\n');

// 将husky.sh 复制到 .husky/_/husky.sh
copyFileSync(
fileURLToPath(new URL('./husky.sh', import.meta.url)),
join(dir, '_/husky.sh')
);
  • 修改git hooks路径:通过core.hooksPath使项目Githooks的路径,指向新创建的.husky文件夹
1
2
3
4
const { error } = spawnSync('git', ['config', 'core.hooksPath', dir]);
if (error) {
throw error;
}
  • 初始化之后,可以通过add命令来进行创建或者往文件后面增加语句
1
2
3
4
5
6
if (existsSync(file)) {
appendFileSync(file, `${cmd}\n`);
l(`updated ${file}`);
} else {
set(file, cmd);
}

如何使用Husky

为了更好的演示这个功能,我们来做一个小Demo。

需求如下:

在代码commit之前,检查暂存区的代码的语法错误。

解决方案:

pre-commit hook+husky。它可以在git commit之前执行我们自定义的语句,当执行错误或者返回非0状态的时候阻止commit行为。

  • npm初始化:安装eslintlint-stagedhusky
1
npm install --save-dev eslint lint-staged husky
  • eslint初始化
1
npm init @eslint/config
  • husky初始化
1
npx husky install

这时候我们就看到了.husky的文件夹

  • 添加lintstagedrc.js:用来配置暂存区文件相对应的lint
1
2
3
module.exports = {
'*.js': ['eslint']
};
  • 使用husky add添加一条检查命令:

创建一个pre-commit hook,用来对暂存区的文件进行检查

1
npx husky add .husky/pre-commit "npx lint-staged -c .husky/lintstagedrc.js"
  • 在项目中新增一个js,故意写上语法错误
1
2
const testA = {
console.log(testA);
  • 尝试提交代码,得到以下报错

https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3facc870d17141838aec9bbd2a55cc7d~tplv-k3u1fbpfcp-zoom-in-crop-mark:1512:0:0:0.awebp

  • 把语法错误修复之后再commit,这时候就可以顺利通过检测且commit了,这样我们的目的就达成了