不细说GitHook

GitHook小记

最近在研究Hook相关的,这里小记一下

Git Hooks 是自动化任务、执行编码标准、执行持续部署和运行测试的绝佳工具,通常与自动化搭配比较多。

什么是Hook?

Hooks 是可以放置在 .git/hooks 目录中的程序,以便在 git 执行某些流程时触发生效(即当仓库中特定事件发生时执行的脚本)。

Git 在提交、推送或合并代码等特定操作之前或之后执行称为“钩子”的脚本。它们允许您在开发过程中自动执行任务、执行策略以及与代码库交互。

Git hooks 通常存储在Git 存储库的.git/hooks目录中, 另外用户还可以通过客户端使用core.hooksPath配置变量来配置它。

目前主要分两种类型, 客户端hook服务端hook

  • 客户端hook: 通常存储在本地,仅针对个人生效,默认没配置,需要自行配置,且没法通过git同步
  • 服务端hook: 通常由Git服务维护,通常不建议修改或没权限修改,例如Gitlab付费版本才支持。另外通常这种由Git服务提供附加功能支持,如Webhook或者Rule来支持类似功能

怎么写Hook脚本

Hook目前对语言没有什么要求,条件允许的情况下,你想用啥就用啥。目前主要比较常见的是bash或者bash+go

如果对Hook脚本不知道怎么写,那么内置的样例脚本是非常有参考价值的,因为他们标注了每个hook需要传入的参数(每个hook有所不同)

git init --bare
cd hooks

进入hooks目录,你会看到如下文件

19:38 ➜  hooks git:(master) tree
.
├── applypatch-msg.sample
├── commit-msg.sample
├── fsmonitor-watchman.sample
├── post-update.sample
├── pre-applypatch.sample
├── pre-commit.sample
├── pre-merge-commit.sample
├── pre-push.sample
├── pre-rebase.sample
├── pre-receive.sample
├── prepare-commit-msg.sample
├── push-to-checkout.sample
├── sendemail-validate.sample
└── update.sample

1 directory, 14 files

大多数可用的hooks就是这些,.sample后缀默认不能执行。为了测试一个hook,你仅仅需要移除.sample后缀。或者,如果你写了一个新脚本,你只需要使用上面这些文件名,去除.sample后缀创建一个文件即可。

客户端常用Hooks

Hooks作用于当前仓库,当你执行git clone时他们不会被复制到新仓库。

如果你的团队需要通过hooks做些什么事,例如提交代码前先进行代码扫描、提交信息是否关联了禅道的需求或者Bug等,这里可能就需要一点技巧来保证你整个团队的hooks是一致的,最新的。

接下来我来简单介绍一下,本地常用的3种hooks

提交前pre-commit

在提交代码之前执行,这种也是用的最多的。

pre-commit中,可以执行各种操作,例如:

  • 代码风格检查:使用工具(如 linters)检查代码是否符合预定义的代码风格规范。
  • 静态代码分析:运行静态代码分析工具,检查代码中的潜在问题和错误。
  • 单元测试:运行单元测试以验证代码的正确性。
  • 格式化代码:自动格式化代码,以确保代码风格的一致性。
  • 检查敏感信息:检查提交的代码中是否包含敏感信息,例如密码、API 密钥等

没有参数传递给pre-commit脚本,并且非零状态时候退出会拒绝本次提交。
可以看看默认的.git/hooks/pre-commit.sample
这个脚本会在遇到空白错误时终止提交,就像git diff-index命令定义的那样(末尾空格、只有空格的行、起始行使用tab产的空格都被默认认为是错误的)

默认情况下,你可能没法感知这个行为,可以给这个脚本添加上Debug即可(set -x), 截取部分如下

#!/bin/bash
set -x
echo "pre commit"

准备提交信息 prepare-commit-msg

用于在创建提交消息(commit message)之前执行操作,

#!/bin/bash

set -x

echo "prepare-commit-msg $@"

1到3个参数需要传递给prepare-commit-msg的脚本

  • 包含提交信息的临时文件名称, 通常是.git/COMMIT_EDITMSG
  • 提交类型。可能是消息提交(使用了-m or -F选型),模板提交(-t选项),合并提交(该提交是一个merge合并提交),或者压缩(squash)提交(该提交是压缩其他提交)。
  • 相关提交的SHA1哈希值,仅仅当被提供-c, -C, 或者 –amend选项时候需要加入这个参数

和pre-commit一样,当非零状态退出时中止提交。

这个hook我用的不多

提交日志 commit-msg

commit-msg跟prepare-commit-msg类似, 但是他用的更多些。
用于在提交消息(commit message)被创建后执行操作。它允许您在提交消息创建完成后运行自定义的脚本或命令,以对提交消息进行验证、格式化或其他处理。

#!/bin/bash

set -x

echo "commit-msg $@"

唯一需要传入的参数是包含这个日志的文件的名称(.git/COMMIT_EDITMSG)

和pre-commit一样,当非零状态退出时中止提交。

#!/bin/bash

set -x

echo "commit-msg $@"

exit 1

服务端hooks

服务端hooks跟本地的基本差不多一样,只不过放在服务端仓库(远端仓库)而已。当hooks放在正式仓库中,那他们就可以用来作为拒绝某些提交的规定的方法了,另外通常情况下这些hooks由服务端维护,个人没有权限编辑或者变更。

接下来我也简单介绍一下,服务端常用的3种hooks

  • 推送接受前(pre-receive
  • 推送更新中(update
  • 推送接受后(post-receive

推送接受前 pre-receive

当使用git push命令推送提交到仓库时,pre-receive就会触发执行

pre-receive钩子脚本中,可以执行各种操作,例如:

  • 验证提交:对即将被推送到远程仓库的提交进行验证,例如检查提交消息、代码规范、权限等。
  • 拒绝推送:如果提交不符合要求,您可以中止推送操作并给出相应的错误提示。
  • 更新参考(refs):在某些情况下,您可以通过修改参考(refs)来实现自定义操作,如更新特定分支或标签。

需要注意的是,pre-receive 是在服务器端执行的,它可以对所有提交进行集中检查和控制。如果脚本返回非零退出码,Git 将拒绝推送操作,并将错误信息返回给推送者。

应用场景:在这里添加任何你想要的开发规定。如果你只希望用公司邮箱用户推送,不喜欢某些格式写的提交信息,或者不喜欢提交里所做的修改,你可以拒绝这个推送。当然你不能限制开发者生成不合格的提交,但是你可以在pre-receive中阻止他们推送不合规的提交到正式仓库

这个脚本不需要入参,但是每个被推送的引用都会按照如下所示的标准输入格式,以一个独立的一行传递给脚本

<old-value> <new-value> <ref-name>

测试脚本

set -x

while read oldrev newrev refname; do
	echo "$refname"
done

尝试推送多个分支,只要任意一个被拒绝,所有推送都会被拒绝

更新推送 update

更新推送在pre-receive之后执行,即在推送接受前(实际更新前)被调用,但是相对pre-receive区别是,你推送几个分支,就会执行几次。

更新推送hook不需要读取标准输入,而是接受三个参数:

  • 正在更新的引用的名称refname
  • 存储在引用里的旧的对象名oldrev
  • 存储在引用里的新的对象名newrev

这里主要更多的是对引用的操作

  • 验证引用更新:对即将被推送到远程仓库的引用更新进行验证,例如检查提交消息、代码规范、权限等。
  • 拒绝推送:如果引用更新不符合要求,您可以中止推送操作并给出相应的错误提示。
  • 更新引用:在某些情况下,您可以通过修改引用来实现自定义操作,如更新特定分支或标签

接受推送后 post-receive

用的不多,更多的是推荐使用webhook

总结

本文只是简单科普githook相关适宜,给大家留个问题,如果你想通过hook实现代码评审,该怎么操作?
后面有空可以给大家细说这个。

Sponsor

Like this article? $1 reward

Comments