首页 / Git 入门教程 / Git 钩子(Hooks)

Git 入门教程

Git 钩子(Hooks)

本教程共 26 篇 · 第 23 篇 · 更新于 2026-07-29 · 约 7 分钟阅读

GitGit 入门教程hooks钩子pre-commit自动化工作流

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 后缀表示这些是示例脚本,不会自动执行。把后缀去掉就激活了。

钩子的类型

按运行位置分两类:

  • 客户端钩子:在你本地执行。由 commitpushmerge 等操作触发。
  • 服务端钩子:在远程仓库执行。由 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 initgit clone 都会自动复制这些钩子。

办法三:用专门的工具

比如 Husky(Node.js 项目常用)或 pre-commit(Python 项目常用)。它们通过配置文件管理钩子,团队只需安装一次就都能用。

实用建议

  1. 钩子要跑得快。pre-commit 钩子太慢会让人抓狂,然后大家开始用 --no-verify 跳过。

  2. 别在钩子里做危险操作。钩子是自动执行的,如果脚本有 bug 可能破坏工作区。

  3. 钩子是建议,不是强制。客户端钩子可以被 --no-verify 跳过。想强制规范,用服务端钩子。

  4. 善用 exit 状态码0 表示通过,非零表示失败。脚本的最后一个命令的退出状态就是钩子的退出状态。

常见用途速查

场景用哪个钩子
提交前跑 lintpre-commit
强制提交信息格式commit-msg
提交后发送通知post-commit
推送前跑完整测试pre-push
阻止对已推送提交 rebasepre-rebase
服务端拒绝不合规推送pre-receive
服务端触发 CI/CDpost-receive

一句话总结:钩子让 Git 在你需要的时候自动执行脚本。把它当成你工作流里的”自动助手”就好。

这章学到了什么

  • 钩子是 Git 在特定事件发生时自动运行的脚本,存放在 .git/hooks 目录
  • 客户端钩子(pre-commit、commit-msg、post-commit、pre-push、pre-rebase)在你本地执行
  • 服务端钩子(pre-receive、update、post-receive)在远程仓库执行,用于强制团队规范
  • 钩子不会随 clone 复制,团队共享需要用模板目录或专门工具(如 Husky)
  • 钩子要跑得快、别做危险操作,善用 exit 状态码控制流程

下一章,我们学习如何给 Git 命令起别名,提升日常操作效率。