"又有人把调试代码提交上来了""提交信息乱写一气""CI 在合并前才发现格式错误"——根源都是质量检查全靠自觉。Git Hooks 能在 commitpush 等动作发生的瞬间自动执行脚本,把"事后补救"变成"事前拦截"。

1. 什么是 Git Hooks

Git Hooks 是 Git 在特定事件发生时自动执行的脚本,放在 .git/hooks/ 目录。自带的 *.sample 示例,去掉后缀并加执行权限即可启用。

ls .git/hooks/
# pre-commit.sample  commit-msg.sample  pre-push.sample ...

# 启用钩子:去掉 .sample 后缀并加执行权限
mv .git/hooks/pre-commit.sample .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit

2. 客户端钩子与执行时机

钩子触发时机典型用途
pre-commitcommit 前格式化、lint、检查调试代码
commit-msg提交信息输入后校验提交信息格式
pre-pushpush 前跑测试、检查大文件

钩子退出码为 0 表示通过,非 0 则中断操作,如 pre-commit 返回 1,commit 即被拒绝。

3. 实战:pre-commit 检查调试代码

写一个钩子,阻止包含 console.logdebugger 的 JS 文件被提交:

#!/bin/sh
# .git/hooks/pre-commit
files=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts)$')
for f in $files; do
  if git show :"$f" | grep -nE 'console\.log|debugger'; then
    echo "错误: $f 包含调试代码,禁止提交!"
    exit 1
  fi
done
exit 0

脚本遍历暂存区里的 JS/TS 文件,发现调试代码就拒绝提交。注意钩子必须可执行:chmod +x .git/hooks/pre-commit

4. 实战:commit-msg 校验提交规范

团队约定提交格式 type(scope): subject(如 feat(login): 增加验证码),用钩子强制:

#!/bin/sh
# .git/hooks/commit-msg
msg=$(cat "$1")
if ! echo "$msg" | grep -qE '^(feat|fix|docs|refactor|chore|test)\([a-z-]+\): .+'; then
  echo "提交信息不符合规范: type(scope): subject"
  echo "示例: feat(login): 增加验证码登录"
  exit 1
fi

$1 是提交信息文件路径。从此不规范提交在本地就被拦下,CI 和评审都轻松很多。

5. 实战:pre-push 跑测试

#!/bin/sh
# .git/hooks/pre-push
echo "=== 推送前运行测试 ==="
npm test
if [ $? -ne 0 ]; then
  echo "测试失败,已阻止推送!"
  exit 1
fi

测试比较耗时,建议只跑核心用例;超过 30 秒的钩子,同事会想办法绕过它。

6. 团队如何共享钩子

.git/hooks/ 不随仓库提交,新成员 clone 后没有钩子。常见做法:把脚本放到 scripts/hooks/ 提交进仓库,用安装脚本软链过去:

# scripts/install-hooks.sh
#!/bin/sh
for hook in pre-commit commit-msg pre-push; do
  ln -sf ../../scripts/hooks/$hook .git/hooks/$hook
done
echo "钩子安装完成"

进阶可用 Husky(Node)或 pre-commit 框架(Python),自动安装钩子、按文件类型分流,接入成本更低。

💡 学习建议:从"最痛的点"入手——比如团队最近总有人提交调试代码,就先写一个 pre-commit 钩子;跑通后加上提交信息校验,再考虑 pre-push 测试。一次只加一道闸,别想着第一天就搭起完整的质量流水线。