Git Hooks的使用
有时我们想要在git操作时候进行一些定制化操作,比如在git commit时候检查一下提交内容是否合规、git push时候检查一下资源文件大小等等,这些功能需要我们可以在git命令执行前后进行拦截,git hooks提供了这样的能力。
1.git默认提供
我们每个通过git管理的项目,在.git/hooks/
文件夹中,会提供一些默认的git hooks文件,比如pre-commit.sample
pre-push.sample
等。
当我们执行git命令时,git会执行相应命令的相关文件,比如:
pre-commit
在我们新增一个commit前prepare-commit-msg
在我们编辑一个commit的消息前调用commit-msg
在我们编辑完一个commit的消息后调用pre-push
在我们执行一次push操作前调用
由于git默认并不会提供具体的hook操作,所以这些文件都是.sample
类型的文件,不会自动执行,所以我们需要自己定义操作。
2.自定义hook操作
(1)直接修改
最简单的办法,我们可以直接将默认文件的.sample后缀去掉,然后在脚本里开发即可,不过因为文件是在.git/hooks/文件夹内,不会跟随着项目被git提交,也就无法同步给其他开发者,每次更改都需要手动修改,很不友好。
(2)链接自定义文件
那如何可以让git hooks文件,像普通项目文件一样,被git操作,同步给其他人呢?因为hook文件其实就是个脚本文件,我们可以写一个脚本文件到项目被git管理的任意目录里即可,那如何执行呢?只要在.git/hooks/里的默认脚本文件中,执行我们外部的这个文件即可。
- 自定义脚本文件:
项目根目录/githooks/commit-msg-impl.py
#!/usr/bin/env python
import sys, os, re
from subprocess import check_output
# 收集参数,第一个参数是commit的信息的文件
commit_msg_filepath = sys.argv[1]
# 打开commit提交消息的文件,检测消息是否以指定格式开头的,不是则异常退出,终止这次commit消息的提交
with open(commit_msg_filepath, 'r') as f:
content = f.read()
if not content.startswith("xxxx"):
print "commit-msg: ERROR! The commit message must start with xxxx"
sys.exit(1)
- 默认hooks文件:
.git/hooks/commit-msg
#!/bin/bash
GIT_ROOT="$(git rev-parse --show-toplevel)"
FILE_NAME=$GIT_ROOT/githooks/commit-msg-impl.py
if [ -f "$FILE_NAME" ]; then
source $FILE_NAME
fi
这样一来,我们只需第一次
修改一遍.git/hooks/里的默认文件,后续修改hook实现时候,只需要修改外部的脚本文件即可。
那么第一次需要修改.git/hooks里文件的行为,可不可以也做成自动化的呢,总不能每个人都去操作一遍吧,答案是可以的,可以写一个脚本来完成这些事情。
inithook.sh
:
#!/bin/sh
# 获取项目根目录
GIT_ROOT="$(git rev-parse --show-toplevel)"
# 获取.git/hooks/根目录
HOOKS_DIR="$GIT_ROOT/.git/hooks"
# 没有时创建该目录
if [ ! -d "$HOOKS_DIR" ]; then
mkdir "$HOOKS_DIR"
fi
# 所有需要hook的git hooks文件名
hook_names=(
pre-commit
prepare-commit-msg
commit-msg
...)
# 在.git/hooks/下生成这些执行文件
for hook_name in ${hook_names[@]}
do
# 生成hook文件
REAL_HOOK_FILE=$HOOKS_DIR/$hook_name
# 将模板文件内容copy进去
cp $GIT_ROOT/githooks/hook-tmp $REAL_HOOK_FILE
# 替换模板文件中的hook-tmp为正确的hook文件名
sed -i "" "s/hook-tmp/$hook_name/g" $REAL_HOOK_FILE
# 修改hook文件权限
chmod a+x $REAL_HOOK_FILE
done
那模板文件
是什么呢?就是默认文件链接到外部执行文件的代码,因为每个hook文件都差不多,只是执行文件的名字不一样,所以可以搞一个模板。
hook-tmp.sh
:
#!/bin/bash
GIT_ROOT="$(git rev-parse --show-toplevel)"
FILE_NAME=$GIT_ROOT/githooks/hook-tmp-impl.py
if [ -f "$FILE_NAME" ]; then
source $FILE_NAME
fi
这样一来,每次更新git配置时,只需要执行一遍inithook.sh
就可以了~
参考资料:自定义git hook