Hook理解为程序特定动作时回调的一个接口,Git中的Hook用于在特定的重要动作发生时,触发脚本。
在开发中,为了规范化提交信息,以便追溯修改和理解修改内容,通常会采用统一的提交信息模板来约束开发人员。因此可以通过配置git,来提升便利性。
进入工程的git hook目录
zengyu@MacBook-Pro universezy.github.io % cd .git/hooks
zengyu@MacBook-Pro hooks % ls -a
. post-update.sample pre-rebase.sample
.. pre-applypatch.sample pre-receive.sample
applypatch-msg.sample pre-commit.sample prepare-commit-msg.sample
commit-msg.sample pre-merge-commit.sample update.sample
fsmonitor-watchman.sample pre-push.sample
hooks目录是存放git运行时关键动作执行脚本的地方,sample是默认的示例文件,在init一个git仓库时生成,每一个sample文件对应一个git动作,我们可以参照sample写一个自己的脚本,以去掉sample后缀命名,便可以执行对应的功能。
通过配置一个提交信息的模板,避免每次提交时需要复制粘贴带来的不便。
我们定义一个如下模板:
[Module.SubModule.Tag][feature/bug][Id:000][Desc:example]
[分析]
[方案]
[影响范围]
[测试建议]
[适用范围]
[依赖链接]
满足开发过程中的主要场景:需求(feature)和修复(bug),有工程对应的模块、子模块、标签,id,描述,以及详细内容。
然后保存到hooks目录中(为了仅对当前工程生效,如果想对所有工程生效,需要保存在本地独立目录下,且git config时加上--global
,具体可以阅读参考文献):
zengyu@MacBook-Pro hooks % vim commit-template
zengyu@MacBook-Pro hooks % cat commit-template
[Module.SubModule.Tag][feature/bug][Id:000][Desc:example]
[分析]
[方案]
[影响范围]
[测试建议]
[适用范围]
[依赖链接]
然后在仓库根目录配置模板使其生效:
zengyu@MacBook-Pro hooks % cd ../../
zengyu@MacBook-Pro universezy.github.io % git config commit.template .git/hooks/commit-template
接下来修改任意内容后,提交修改:
zengyu@MacBook-Pro universezy.github.io % git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git restore --staged ..." to unstage)
modified: universezy/README.md
zengyu@MacBook-Pro universezy.github.io % git commit -a
提供模板之后,开发人员可能修改提交信息后不满足规范,因此还需要使用脚本强制约束。
刚才在hooks目录下看到的commit-msg.sample
文件便是该约束脚本的示例:
zengyu@MacBook-Pro universezy.github.io % cat .git/hooks/commit-msg.sample
#!/bin/sh
#
# An example hook script to check the commit log message.
# Called by "git commit" with one argument, the name of the file
# that has the commit message. The hook should exit with non-zero
# status after issuing an appropriate message if it wants to stop the
# commit. The hook is allowed to edit the commit message file.
#
# To enable this hook, rename this file to "commit-msg".
# Uncomment the below to add a Signed-off-by line to the message.
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
# hook is more suited to it.
#
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
# This example catches duplicate Signed-off-by lines.
test "" = "$(grep '^Signed-off-by: ' "$1" |
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
echo >&2 Duplicate Signed-off-by lines.
exit 1
}
修改默认脚本,使其要求必须符合我们定义的模板格式:
zengyu@MacBook-Pro universezy.github.io % cd .git/hooks
zengyu@MacBook-Pro hooks % cp commit-msg.sample commit-msg
zengyu@MacBook-Pro hooks % vim commit-msg
输入脚本:
MSG=$(awk '{
printf("%s",$0)}' "$1")
REGEX="^\[(.+\.)+.+](\[.+])(\[Id:.+])(\[.+]).*([\s\S]*)\[分析].*([\s\S]*)\[方案].*([\s\S]*)\[影响范围].*([\s\S]*)\[测试建议].*([\s\S]*)\[适用范围].*([\s\S]*)\[依赖链接].*([\s\S]*)"
if [[ ! $MSG =~ $REGEX ]]; then
echo "Commit msg does not comply with the specification!"
exit 1
fi
exit 0
下面进行讲解:
MSG=$(awk '{
printf("%s",$0)}' "$1")
“$1”
是调用该脚本的入参,也就是提交信息awk
是一种文本处理语言,将文本逐行执行指令$0
是域标记,$后面的数字表示该行第n个值,0表示全部MSG
,需要注意的是,shell下全局变量赋值时等号左右不能有空格REGEX="^\[(.+\.)+.+](\[.+])(\[Id:.+])(\[.+]).*([\s\S]*)\[分析].*([\s\S]*)\[方案].*([\s\S]*)\[影响范围].*([\s\S]*)\[测试建议].*([\s\S]*)\[适用范围].*([\s\S]*)\[依赖链接].*([\s\S]*)"
这里采用了正则表达式的形式进行校验,关于正则表达式可以参考《正则表达式 - 语法》学习,此处不进行详解。
按照定义的提交模板,要求提交信息遵循以下格式:
if [[ ! $MSG =~ $REGEX ]]; then
echo "Commit msg does not comply with the specification!"
exit 1
fi
exit 0
if-fi
是条件语句的首尾关键字,满足条件用then
关键字执行[[ ]]
搭配条件语句使用,中间是判断条件=~
为正则表达式匹配$
引用定义的变量echo
是输出命令,用来打印信息exit
返回0表示正常结束,1表示异常结束会执行失败,并输出提示信息:
zengyu@MacBook-Pro universezy.github.io % git commit -a
Commit msg does not comply with the specification!
执行成功:
zengyu@MacBook-Pro universezy.github.io % git commit -a
[master 9550586] [Module.SubModule.Demo][bug][Id:0001][Correct demo of commit msg]
1 file changed, 1 insertion(+)
博主的正则表达式部分还有待改进的地方,感兴趣的读者可以留言。