10. 提交代码自动检查:Git Hooks 实战,质量不再靠人工
「Git」系列已发布: 00. 改崩代码不敢回退?Git 版本控制 3 分钟救你狗命 | 01. Git 装完不会配?Windows/macOS/Linux 三端配置一次到位 | 02. Git 配置总踩坑?全局/仓库/SSH 密钥一次搞懂 | 03. add/commit/push 别再死记:Git 工作流核心概念实操 | 04. Git 分支管理:5 步彻底搞懂 branch 与 merge | 05. clone/fetch/pull/push 分不清?Git 远程协作实战 | 06. 给版本打上永久印记:Git 标签管理与发布实战 | 07. 切分支怕丢改动?Git stash 暂存恢复保护现场 | 08. rebase 不是 merge 的替代品:Git 故事线重构进阶 | 09. Git 日志与调试:log/blame/diff 让历史无所遁形 | 10. 提交代码自动检查:Git Hooks 实战,质量不再靠人工(本文) | 11. 多仓库依赖怎么管?Git 子模块统一版本一键更新
pre-commit 拦截坏代码、commit-msg 规范信息、pre-push 跑测试。Git Hooks 把质量门禁前移到提交那一刻,人工 review 省一半力。
Git 基础操作(第 03 篇)要熟练。下面这些 Hooks 脚本在任意 Git 仓库都能测试,建议拿个测试仓库练手。
一、Hooks 是什么
Git Hooks 就是在特定 Git 事件发生时自动触发的脚本。它们躺在.git/hooks/目录里,是 Git 内置的自动化扩展点。
# 看看仓库里有哪些示例 Hooks
ls -la .git/hooks/
# applypatch-msg.sample pre-applypatch.sample pre-rebase.sample
# commit-msg.sample pre-commit.sample update.sample
# post-update.sample pre-push.sample
这些
.sample文件只是示例模板,默认不会执行。激活一个 Hook 很简单:去掉.sample后缀,赋予可执行权限。
cp .git/hooks/pre-commit.sample .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
1.1 Hooks 分类与触发时机
按作用域分成客户端和服务端两类:
1.1.1 客户端 Hooks
| Hook | 什么时候触发 | 能干什么 |
|---|---|---|
pre-commit |
git commit执行前 |
代码风格检查、单元测试、拦截调试代码 |
prepare-commit-msg |
提交信息编辑器启动前 | 动态生成提交信息模板 |
commit-msg |
提交信息编辑完成后 | 校验提交信息格式 |
post-commit |
提交完成后 | 通知、日志 |
post-checkout |
git checkout后 |
环境初始化、依赖安装 |
post-merge |
git merge后 |
依赖更新、通知 |
pre-rebase |
git rebase前 |
禁止在主分支上变基 |
pre-push |
git push前 |
跑完整测试套件 |
1.1.2 服务端 Hooks
| Hook | 什么时候触发 | 能干什么 |
|---|---|---|
pre-receive |
接收推送前 | 权限检查、提交信息校验 |
update |
每个分支推送时 | 分支权限控制 |
post-receive |
接收推送后 | 触发 CI/CD、发通知 |
1.2 实战 Hooks
1.2.1 pre-commit:提交前自动检查
#!/bin/bash
# .git/hooks/pre-commit
# 提交前自动跑代码检查
set -e
echo "=== 执行 pre-commit 检查 ==="
# 1. 拦截调试代码
if git diff --cached --name-only | xargs grep -n "console\.log\|debugger\|print(" 2>/dev/null; then
echo "错误:提交里有调试代码(console.log / debugger / print),去掉再提交。"
exit 1
fi
# 2. 拦截敏感文件
SENSITIVE_PATTERNS="\.env$|\.pem$|\.key$|credentials\.json$"
if git diff --cached --name-only | grep -E "$SENSITIVE_PATTERNS" 2>/dev/null; then
echo "错误:检测到敏感文件,确认是否该加到 .gitignore。"
exit 1
fi
# 3. Python 代码格式检查
if git diff --cached --name-only | grep "\.py$" > /dev/null; then
echo "检查 Python 代码格式..."
if ! command -v ruff &> /dev/null; then
echo "警告:没装 ruff,跳过格式检查。"
else
git diff --cached --name-only | grep "\.py$" | xargs ruff check
fi
fi
echo "pre-commit 检查通过 ✓"
1.2.2 commit-msg:强制提交信息格式
#!/bin/bash
# .git/hooks/commit-msg
# 校验提交信息是否符合约定式提交
COMMIT_MSG_FILE=$1
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
CONVENTIONAL_PATTERN="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\)?: .{1,50}"
if ! echo "$COMMIT_MSG" | head -1 | grep -qE "$CONVENTIONAL_PATTERN"; then
echo "========================================="
echo "提交信息格式不对!"
echo ""
echo "请用约定式提交格式:"
echo " <type>(<scope>): <简短描述>"
echo ""
echo "type 取值:feat / fix / docs / style / refactor / test / chore / perf / ci / build / revert"
echo ""
echo "示例:"
echo " feat(auth): 添加双因素认证"
echo " fix(api): 修复超时重试逻辑"
echo "========================================="
exit 1
fi
1.2.3 pre-push:推送前跑测试
#!/bin/bash
# .git/hooks/pre-push
# 推送前自动跑测试
echo "=== 执行 pre-push 检查 ==="
BRANCH=$(git rev-parse --abbrev-ref HEAD)
# main/master 分支:跑完整测试
if [[ "$BRANCH" == "main" || "$BRANCH" == "master" ]]; then
echo "检测到 main 分支推送,运行完整测试套件..."
make test-full
if [ $? -ne 0 ]; then
echo "测试没过,推送已取消。"
exit 1
fi
fi
# feature 分支:跑快速测试
if [[ "$BRANCH" == feature/* ]]; then
echo "检测到 feature 分支推送,运行快速测试..."
make test-quick
if [ $? -ne 0 ]; then
echo "测试没过,推送已取消。"
exit 1
fi
fi
echo "pre-push 检查通过 ✓"
1.2.4 post-merge:自动装依赖
#!/bin/bash
# .git/hooks/post-merge
# 合并后发现依赖文件变了就自动装
if git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD | grep -qE "package\.json|yarn\.lock|requirements\.txt|Pipfile"; then
echo "检测到依赖文件变更,自动安装..."
if [ -f "package.json" ]; then
npm install
fi
if [ -f "requirements.txt" ]; then
pip install -r requirements.txt
fi
echo "依赖安装完成 ✓"
fi
1.3 团队共享 Hooks
Hooks 存在.git/hooks/里,不会被 push 到远程。要让团队用同一套 Hooks,有几种办法:
1.3.1 方案一:项目级脚本 + 安装脚本
在项目根目录建.githooks/放 Hooks,再给个安装脚本:
#!/bin/bash
# scripts/install-hooks.sh
HOOKS_DIR="$(git rev-parse --git-dir)/hooks"
PROJECT_HOOKS="$(git rev-parse --show-toplevel)/.githooks"
for hook in "$PROJECT_HOOKS"/*; do
hook_name=$(basename "$hook")
cp "$hook" "$HOOKS_DIR/$hook_name"
chmod +x "$HOOKS_DIR/$hook_name"
echo "已安装 Hook: $hook_name"
done
然后在
README 或贡献指南里注明克隆后要跑bash scripts/install-hooks.sh。
1.4 方案二:core.hooksPath
Git 2.9+ 支持自定义 Hooks 路径:
git config core.hooksPath .githooks
这样团队成员直接指向项目中版本控制的
.githooks/目录就行。
1.4.1 方案三
Husky(Node.js 生态)
npm install --save-dev husky
npx husky init
echo "npm test" > .husky/pre-commit
1.5 Hooks 调试与小贴士
1.5.1 调试
# 手动跑 Hook 脚本
GIT_DIR=.git .git/hooks/pre-commit
# 开详细输出
#!/bin/bash
set -x # 每条命令都打印
set -e # 遇错就停
# 记录执行日志
exec 1> >(tee -a /tmp/git-hook.log) 2>&1
echo "$(date): pre-commit hook triggered"
1.5.2 跳过 Hooks
紧急情况需要临时跳过:
# 跳过 pre-commit 和 commit-msg
git commit --no-verify -m "紧急修复"
# 跳过 pre-push
git push --no-verify
1.5.3 性能注意
- pre-commit Hook 要快(2 秒内),耗时的检查放 pre-push。
- 用
git diff --cached --name-only只检查暂存区,别扫整个工作目录。 - 能缓存的结果就缓存,别每次都来一遍。
1.6 文章引用
📌 本文首发于微信公众号 【运维知识点滴】——专注运维实战干货,微信搜索“运维知识点滴”或扫下方二维码关注,更新不迷路。
| 微信公众号【运维知识点滴】 | 微信小程序【A5单据合并&大试卷拆分转A4打印】 |
|---|---|
![]() |
![]() |
| 运维实战干货 · 关注追更 | Word 转 PDF · 试卷/发票打印神器 · 免费 |
💡 小程序【A5单据合并&大试卷拆分转A4打印】是干嘛的? 一个 Word 转 PDF 的打印神器,专治“文档版式和打印机对不上”:
- A3 试卷打不出来? 学校发的电子版试卷多是 A3,家用打印机只吃 A4——上传 Word 一键转换:可缩到一页 A4(页数不变),也可对半拆成两张 A4(1:1 原大、字更清晰),两种模式自选;
- 版式不乱:真实渲染引擎原样转换、绝不重排内容,转出的 PDF 和原 Word 排版一致,公式/表格/图片不跑版;
- 两张 A5 单据拼一张 A4:出租车票、火车票等报销凭证拼版后,一次打印、整齐粘贴;
- 隐私省心:无需注册,转换结果留存 5 天、最多下载 5 次即自动删除,不留任何历史记录;
- 微信扫码即用,免费、免安装,支持 doc / docx(≤50MB)。
上一篇:09. Git 日志与调试:log/blame/diff 让历史无所遁形 | 下一篇:11. 多仓库依赖怎么管?Git 子模块统一版本一键更新

