Git 钩子(Hooks)
本教程共 26 篇 · 第 23 篇 · 更新于 2026-07-29 · 约 7 分钟阅读
23. Git 钩子(Hooks)
本节目标:学会在 Git 操作的关键节点插入自定义脚本,实现代码检查、自动通知等个性化流程。
什么是钩子
钩子(hook)就是 Git 在特定事件发生时自动运行的脚本。
想象你在网上买东西,从下单到收货系统会发好几次通知。Git 钩子也一样——在 commit、push、merge 这些动作前后,自动执行你写好的脚本。
钩子存放在仓库的 .git/hooks 目录里:
ls .git/hooks/
applypatch-msg.sample pre-push.sample
commit-msg.sample pre-rebase.sample
post-update.sample prepare-commit-msg.sample
pre-applypatch.sample update.sample
pre-commit.sample
.sample 后缀表示这些是示例脚本,不会自动执行。把后缀去掉就激活了。
钩子的类型
按运行位置分两类:
- 客户端钩子:在你本地执行。由
commit、push、merge等操作触发。 - 服务端钩子:在远程仓库执行。由
push触发。用于强制团队规范。
Note钩子不会随
git clone复制。别人克隆你的仓库,拿不到你的钩子。如果想让团队共享钩子,得想别的办法(后面会讲)。
编写一个最简单的钩子
新建 .git/hooks/pre-commit 文件:
#!/bin/sh
echo "正在检查代码..."
exit 0
赋予执行权限:
chmod +x .git/hooks/pre-commit
现在每次 git commit 都会先执行这个脚本。如果 exit 0(正常退出),提交继续;如果非零退出,提交被中止。
常用客户端钩子
pre-commit:提交前检查
在提交创建之前触发。最常用的场景是代码检查。
检查代码风格(比如用 ESLint):
#!/bin/sh
# 对暂存区的 JS 文件运行 ESLint
files=$(git diff --cached --name-only --diff-filter=ACMR | grep '\.js$')
if [ -n "$files" ]; then
npx eslint $files
if [ $? -ne 0 ]; then
echo "ESLint 检查不通过,请修复后再提交"
exit 1
fi
fi
exit 0
如果 ESLint 报错,exit 1 会中止提交。
Note可以用
git commit --no-verify跳过 pre-commit 钩子。紧急情况不想跑检查时有用,但别养成习惯。
commit-msg:校验提交信息
在用户输入提交信息之后触发。可以强制提交信息的格式。
比如要求提交信息必须包含 issue 编号:
#!/bin/sh
commit_msg=$(cat "$1")
if ! echo "$commit_msg" | grep -qE "#[0-9]+"; then
echo "提交信息必须包含 issue 编号,例如:#123"
exit 1
fi
exit 0
$1 是存放提交信息的临时文件路径。
post-commit:提交完成后通知
在整个提交过程完成后触发。常用于发送通知。
比如提交后打印一条确认信息:
#!/bin/sh
echo "提交成功!SHA: $(git rev-parse --short HEAD)"
exit 0
pre-push:推送之前检查
在 git push 触发,但远程引用更新之前执行。适合在推送前跑测试套件。
#!/bin/sh
echo "正在运行测试..."
npm test
if [ $? -ne 0 ]; then
echo "测试不通过,取消推送"
exit 1
fi
exit 0
pre-rebase:变基之前检查
在执行 git rebase 之前触发。可以用来阻止对已推送的提交进行变基:
#!/bin/sh
# 禁止对已推送的分支 rebase
branch=$(git symbolic-ref --short HEAD)
if git merge-base --is-ancestor "origin/$branch" HEAD 2>/dev/null; then
echo "该分支已推送到远程,禁止 rebase"
exit 1
fi
exit 0
服务端钩子
服务端钩子运行在远程仓库上。它们接收信息的格式和客户端钩子不同。
pre-receive
在远程仓库收到 git push 之后、更新任何引用之前触发。可以检查所有推送的提交,决定接受还是拒绝。
适合做强制规范:比如”所有提交信息必须符合某种格式”。
update
和 pre-receive 类似,但对每个推送的分支分别触发。可以拒绝某个分支而接受其他分支。
post-receive
在推送成功完成后触发。适合触发持续集成(CI)或发送通知。
Note服务端钩子的输出会传送到客户端。你可以在脚本里
echo提示信息,推送者能看到。
钩子的编程语言
虽然示例脚本多是 shell 和 Perl,但你可以用任何语言。只需要在第一行声明解释器路径。
Python 示例(.git/hooks/pre-commit):
#!/usr/bin/env python3
import sys
print("执行 pre-commit 检查...")
# 你的检查逻辑
sys.exit(0)
Node.js 示例:
#!/usr/bin/env node
console.log("执行 pre-commit 检查...");
process.exit(0);
团队共享钩子
钩子在 .git/hooks 下,不会随仓库分发。想给团队共享钩子,有几种办法:
办法一:把钩子存在项目目录中
把钩子脚本放在项目根目录的 scripts/hooks/ 文件夹里,受版本控制。然后让组员手动或通过工具链接到 .git/hooks。
办法二:用 Git 模板目录
git config --global init.templateDir ~/.git-templates
mkdir -p ~/.git-templates/hooks
# 把你的钩子放进去
之后所有 git init 或 git clone 都会自动复制这些钩子。
办法三:用专门的工具
比如 Husky(Node.js 项目常用)或 pre-commit(Python 项目常用)。它们通过配置文件管理钩子,团队只需安装一次就都能用。
实用建议
-
钩子要跑得快。pre-commit 钩子太慢会让人抓狂,然后大家开始用
--no-verify跳过。 -
别在钩子里做危险操作。钩子是自动执行的,如果脚本有 bug 可能破坏工作区。
-
钩子是建议,不是强制。客户端钩子可以被
--no-verify跳过。想强制规范,用服务端钩子。 -
善用
exit状态码。0表示通过,非零表示失败。脚本的最后一个命令的退出状态就是钩子的退出状态。
常见用途速查
| 场景 | 用哪个钩子 |
|---|---|
| 提交前跑 lint | pre-commit |
| 强制提交信息格式 | commit-msg |
| 提交后发送通知 | post-commit |
| 推送前跑完整测试 | pre-push |
| 阻止对已推送提交 rebase | pre-rebase |
| 服务端拒绝不合规推送 | pre-receive |
| 服务端触发 CI/CD | post-receive |
一句话总结:钩子让 Git 在你需要的时候自动执行脚本。把它当成你工作流里的”自动助手”就好。
这章学到了什么
- 钩子是 Git 在特定事件发生时自动运行的脚本,存放在
.git/hooks目录 - 客户端钩子(pre-commit、commit-msg、post-commit、pre-push、pre-rebase)在你本地执行
- 服务端钩子(pre-receive、update、post-receive)在远程仓库执行,用于强制团队规范
- 钩子不会随 clone 复制,团队共享需要用模板目录或专门工具(如 Husky)
- 钩子要跑得快、别做危险操作,善用 exit 状态码控制流程
下一章,我们学习如何给 Git 命令起别名,提升日常操作效率。