最近我在一个咨询项目上,为了帮助客户规范代码提交信息,我们决定建议客户使用 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值退出,则流程会中断不会继续进行。
下图是提交工作流钩子的生命周期:
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流程。
推荐阅读: