1. Git Hooks 简介

Git hooks 是 Git 提供的一种机制,允许我们在执行某些 Git 操作(如 commit、push)前后自动运行脚本。它是一种轻量级的自动化手段,虽然不如 Jenkins、Concourse 这类 CI/CD 工具强大,但在本地或服务端做一些简单校验、自动处理时非常实用。

本文将介绍 Git hooks 的基本概念、分类、管理方式,并通过两个示例演示如何创建本地和远程 Git hooks。

2. Git Hooks 类型与作用

Git hooks 分为两类:客户端钩子(client-side)服务端钩子(server-side)

  • 客户端钩子:在本地仓库操作时触发,例如 commit、merge、rebase、checkout 等。
  • 服务端钩子:在远程仓库收到 push 请求时触发,例如 pre-receive、update、post-receive 等。

2.1 客户端钩子

以下是一些常见的客户端钩子及其触发时机:

钩子名称 触发时机
pre-commit 提交前,用于代码检查、格式化等
prepare-commit-msg 提交信息生成前
commit-msg 提交信息生成后
post-commit 提交完成后
pre-push push 操作前
post-checkout checkout 或 clone 后

还有一些较少用到的钩子,如 pre-rebase、post-rewrite、pre-auto-gc 等。

2.2 服务端钩子

服务端钩子主要在远程仓库收到 push 操作时触发:

钩子名称 触发时机
pre-receive 接收推送前,用于拒绝某些提交
update 每个 ref 更新前调用一次
post-receive 所有 ref 更新完成后,常用于部署
post-update 推送完成后触发

这些钩子可以用来做权限控制、部署、通知等操作。

3. Git Hooks 的管理

Git hooks 存放在每个仓库的 .git/hooks/ 目录下,如下是默认的几个 .sample 示例文件:

$ ls -1 .git/hooks/
applypatch-msg.sample
commit-msg.sample
fsmonitor-watchman.sample
post-update.sample
pre-applypatch.sample
pre-commit.sample
pre-merge-commit.sample
prepare-commit-msg.sample
pre-push.sample
pre-rebase.sample
pre-receive.sample
push-to-checkout.sample
update.sample

3.1 钩子的参数与环境变量

部分钩子可以通过标准输入(stdin)接收参数,例如 post-rewrite 可以根据输入判断是 commit --amend 还是 rebase

另外,一些钩子会设置环境变量,如 GIT_EDITORGIT_COMMIT 等,用于传递上下文信息。

3.2 钩子的执行机制

  • 钩子文件必须可执行chmod +x .git/hooks/hook-name
  • 返回值决定 Git 操作是否继续
    • 返回 0 表示成功,继续执行 Git 操作
    • 返回非 0 表示失败,Git 操作中止

因此,钩子文件应作为主入口,调用其他脚本并统一返回状态码。

3.3 创建或启用钩子

步骤如下:

  1. .git/hooks/ 下创建文件,文件名即为钩子名称(如 pre-commit
  2. 添加 shebang 行(如 #!/usr/bin/env bash
  3. 编写脚本逻辑
  4. 执行 chmod +x 使其可执行

也可以复制 .sample 文件并重命名为钩子名。

4. 示例钩子

4.1 客户端钩子:pre-commit

这是一个 pre-commit 钩子示例,用于在提交前运行测试,若测试失败则询问是否继续提交:

#!/usr/bin/env bash
exec < /dev/tty
npm test
failed=$?
if [ $failed ]; then
  while true; do
    read -p 'Continue with failing tests [Yn]: ' yn
    case $yn in
      [Yy]* ) exit 0;;
      [Nn]* ) exit 1;;
          * ) echo 'Y or N';;
    esac
  done
fi

✅ 要点:

  • 使用 exec < /dev/tty 保证能读取用户输入
  • npm test 执行测试
  • 若失败,进入交互循环,用户选择 Y/N 决定是否继续提交

4.2 服务端钩子:post-receive

这是一个 post-receive 钩子示例,在收到超过 4 个 refs 的 push 后触发部署任务:

#!/usr/bin/env bash
refs=$(cat | wc --lines)
if [ $refs -gt 4 ]; then
  fly --target=node trigger-job --job nodetasks/deploy 2>&1 >/dev/null &
  disown
  echo 'deployed'
else
  echo 'not deployed'
fi

✅ 要点:

  • cat | wc --lines 统计收到的 refs 数量
  • 使用 & 后台执行,避免阻塞客户端
  • 使用 disown 防止子进程被挂起
  • 使用重定向 2>&1 >/dev/null 隐藏输出

4.3 触发钩子测试

在本地仓库中模拟提交和推送:

# 创建文件
$ echo $(date) | tee --append file{1..5} >/dev/null
$ git add file{1..5}

# 提交触发 pre-commit
$ git commit --all --message 'upDate'

# 添加远程仓库
$ git remote add origin https://gerganov.com/repository

# 推送触发 post-receive
$ git push --all

输出结果中会显示 deployed,说明钩子成功执行。

5. 总结

Git hooks 是一种轻量但功能强大的机制,适用于在 Git 操作前后执行自动化任务。

✅ 优势:

  • 无需复杂配置,脚本即可生效
  • 支持客户端与服务端双侧操作
  • 可用于代码检查、测试、部署、权限控制等场景

⚠️ 注意:

  • 钩子不会随 Git 仓库一同提交,需要手动部署
  • 服务端钩子可能影响推送性能,建议异步执行
  • 不要将敏感信息写入钩子文件中

合理使用 Git hooks,可以有效提升开发效率与代码质量。


原始标题:Understanding and Creating Git Hooks