从 Git Hooks 说开去
最近我在一个咨询项目上,为了帮助客户规范代码提交信息,我们决定建议客户使用 git hooks
来实现。
为此,我们大概了解了一下 Git Hooks的相关知识。
一、初识 Git Hooks
Git Hooks 是什么?
Git Hooks 是一系列脚本, Git 允许 Git 仓库在触发某些事件时,去执行这些脚本。 我们可以通过 Git Hooks 在开发生命周期的关键节点,触发自定义的操作。
这里的脚本可以是任何正确命名的可执行脚本。由此可见,Git Hooks 的灵活性还是很强的。
在一个 Git 仓库中,我们可以在.git/hooks
中发现一系列的文件:
applypatch-msg.sample pre-push.sample
commit-msg.sample pre-rebase.sample
fsmonitor-watchman.sample pre-receive.sample
post-update.sample prepare-commit-msg.sample
pre-applypatch.sample update.sample
pre-commit.sample
这些文件都是不同的 Git Hooks 脚本, 当然,他们现在并不会执行。
Git 能识别的文件名是不带sample
的,所以当我们把上述文件的sample
后缀去掉时,这些脚本才是有效的。
另外,因为上述文件都是可执行的脚本,所以在执行时,要注意给与其相应的权限。
二、Git Hooks的版本化
我们在上面看到 Git Hooks 的相关脚本存放在 .git/hooks
目录下。但该目录并不被版本化,提交到远端。
但是对于团队来说,保证其可版本化是非常重要的功能,否则配置新脚本,或者对已有的脚本进行变化,都是一项浩大的工程。
经过简单的调研发现,无论是Java相关的技术栈,还是JS相关的技术栈,都有比较成熟的方案。
Java 相关的技术栈,比如 Spring 项目,Android项目等,可以使用Gradle的相关配置功能,将配置脚本纳入版本管理中,在gradle build
时,再拷贝到 .git\hooks
目录中。 具体实现步骤,大家可以参见腾云的这个项目。
Js 相关的技术栈,可以直接在Npm
中使用使用 husky
来完成对应的配置。他依赖Npm的安装和配置,具体的使用方式可以参见他的项目主页。
以上两种方式,都需要依靠特定的依赖管理工具才能完成。那依赖管理工具不支持,或者没有依赖管理工具,还想完成版本化管理 Git hooks, 怎么办呢?
Git hooks 的脚本文件都存放在.git\hooks
目录下,但是该目录并不能被纳入版本管理中。经过查阅 git
的文档可知, 在17年 Git 提供了一项配置: core.hooksPath
,通过它,可以将指定某个目录存放 git hook 的相关脚本。同时,该目录也可以纳入版本管理。
执行命令如下:
git config --add core.hookspath .mygithooks
执行该命令后,在提交时,会自动触发.mygithooks
目录下的脚本。
三、Git Hooks 的分类
Git Hooks 分两种,客户端钩子 、服务端钩子。 客户端钩子会被 commit,merge,rebase 等操作触发,而服务端会被push等操作触发。
1. 客户端钩子
客户端钩子 分为三种,提交工作流,电子邮件流,和其他。我们这里只讨论提交工作流和其他部分。
在 Git Hooks 中,若脚本以 exit 0
退出应用,则流程会继续进行。若脚本以非0值退出,则流程会中断不会继续进行。
下图是提交工作流钩子的生命周期:
![](https://img.haomeiwen.com/i2251612/6cf126a633c164a4.png)
pre commit
不带任何参数,可以通过git diff 的形式获取变更的代码。一般用来检查即将提交的代码是否有效,比如是否能通过编译,是否能通过测试等。
prepare commit msg
带三个参数, 当前提交信息的文件路径,当前的提交类型,以及提交的SHA-1校验。 一般用来做提交信息的格式化,合并,整理等。
commit msg
带一个参数,即当前提交信息的文件路径。 一般用来检查提交信息格式等。
post commit
不带任何参数。 一般用于提交后发送通知等。
其他工作流包括pre-rebase
, post-rewrite
, post-merge
, pre-push
这些在我们日常开发过程中用的比较少, 这里就不做介绍了。
使用git commit --no-verify
可以越过和commit相关的钩子,直接提交。
2. 服务端钩子
服务端的钩子在持续集成中,应用是很广泛的。
pre-receive
会处理来自客户端的推送。 常见的就是提交后触发CI,由CI返回结果来判断能否合入。
update
也会处理来自客户端的推送,区别是可以按照分支进行处理。 如果客户端同时推送了多个分支,仅有失败的分支会被拒绝。成功的分支仍会被更新。update
带三个参数,分别是引用分支的名字,推送前的SHA-1值, 需要更新的SHA-1值。
post-receive
在整个过程完结以后运行。 大家最熟悉的就是提交后触发测试,打包,部署等流程。
四、 Git Hooks的使用场景
Git Hooks 的本质是触发器,可以在 Git 的生命周期的某些阶段触发开发人员预先编写好的脚本。 所以其使用场景并不受限。
下面仅介绍一些我们在开发过程中可能会用到的场景。
-
客户端钩子
因为客户端钩子仅在本地起作用,想要做出强制性的约束,还是只能依赖服务端的流程。
所以客户端的钩子一般多用来做提示性,或触发一些重复手动工作。
所以我们可以在客户端钩子中检查:
暂存区是否还有未提交的代码。
检查或者直接格式化提交信息。
代码是否能够编译,是否能通过lint,是否能通过单元测试,是否达到单元测试覆盖率等等指标。
在Push 时自动升级版本。
在将多个提交合并成一个提交时,按照格式整理提交信息等。
-
服务端钩子
服务端的钩子主要的作用有两个:一个是决定是否接受当前推送(
pre-receive
,update
),另外一个是在接受推送后(post-receive
), 触发其他操作等。服务端的钩子的使用场景,大家就很熟悉了,通过各种自动化的方式来对代码质量进行审查,来决定代码是否能够合入云端。如果不能合入,给出相应的提示。以及合入后,触发各种各种自动化的流程进行CICD流程。
推荐阅读: