本文作者:IMWeb 何璇 原文出处:IMWeb社区 未经同意,禁止转载
优秀的团队必不可缺少源代码的质量管理,比如eslint、sasslint等代码检测工具,借助git hook能力,我们可以将这些工具无缝地整合到git开发工作流中。
Like many other Version Control Systems, Git has a way to fire off custom scripts when certain important actions occur
Git hook能够在发生某特定行为的时机,触发执行自定义的脚本。
Git hook分为客户端hooks(Client-Side Hooks)和服务端hooks(Server-Side Hooks),下面列出了所有可以触发hook的时机,可以在官方文档中查询:
git commit
命令时触发,常用于检查代码风格commit message
编辑器呼起前default commit message
创建后触发,常用于生成默认的标准化的提交说明commit message
后触发,常用于校验提交说明是否标准git commit
完成后触发,常用于邮件通知、提醒git am
命令时触发,常用于检查命令提取出来的提交信息是否符合特定格式git am
提取出补丁并应用于当前分支后,准备提交前触发,常用于执行测试用例或检查缓冲区代码git am
提交后触发,常用于通知、或补丁邮件回复(此钩子不能停止git am
过程)git rebase
命令时触发commit
的命令时触发,比如git rebase
或git commit --amend
git checkout
命令成功后触发,可用于生成特定文档,处理大二进制文件等merge
行为后触发git push
命令时触发,可用于执行测试用例hook脚本会存放在仓库.git/hooks
文件夹中,git提供了一些shell样例脚本以作参考。
pre-push.sample
:
#!/bin/sh
# An example hook script to verify what is about to be pushed. Called by "git
# push" after it has checked the remote status, but before anything has been
# pushed. If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
# <local ref> <local sha1> <remote ref> <remote sha1>
#
# This sample shows how to prevent push of commits where the log message starts
# with "WIP" (work in progress).
remote="$1"
url="$2"
z40=0000000000000000000000000000000000000000
while read local_ref local_sha remote_ref remote_sha
do
if [ "$local_sha" = $z40 ]
then
# Handle delete
:
else
if [ "$remote_sha" = $z40 ]
then
# New branch, examine all commits
range="$local_sha"
else
# Update to existing branch, examine new commits
range="$remote_sha..$local_sha"
fi
# Check for WIP commit
commit=`git rev-list -n 1 --grep '^WIP' "$range"`
if [ -n "$commit" ]
then
echo >&2 "Found WIP commit in $local_ref, not pushing"
exit 1
fi
fi
done
exit 0
你只需要在.git/hooks
文件夹中新建以钩子名命令的脚本文件(比如pre-push
),这个脚本就会在适当的时机被触发。
husky
是用node实现的一个快速安装git hooks的工具,在项目中安装后,就可以在package.json
中指定相关钩子执行的npm scripts。
// package.json
{
"husky": {
"hooks": {
"pre-commit": "npm test",
"pre-push": "npm test",
"...": "..."
}
}
}
但它存在一些些限制,后续说明。
imlint
是团队正在使用的一款git hook工作流生成工具,可以快速方便地实现eslint
,sasslint
等校验能力。
近期想在husky
的基础上实现询问用户输入的hook,代码如下:
console.log(`\n确认执行命令\n${question}?(y/n)`));
process.stdin.on('data', (data) => {
// do something...
});
但是git hook进程完全不管你是否在等待用户输入,直接就退出了,继续执行了相关操作。Google后发现,原来git hook被设计为不可交互的行为。
非要询问用户输入,可以监听非标准输入/dev/tty
,代码如下:
# ...
exec < /dev/tty
# ...
if (process.platform === 'win32') {
// husky不支持配置.git/hooks/xx 中的sh脚本
// 需要手动加入 exec < /dev/tty 开启交互
stream = process.stdin;
} else {
stream = fs.createReadStream('/dev/tty');
}
stream.setEncoding('utf-8');
stream.on('error', () => {
// do something...
});
stream.on('data', (data) => {
// do something...
});
windows系统的兼容问题,请参考
https://nodejs.org/docs/latest-v8.x/api/tty.html