ToolkitX
知识库工具箱

Git Hooks

pre-commit, commit-msg hooks 配置

20min·高级

01. Git Hooks 是干什么的

Git Hooks 是在 Git 操作(commit、push、merge 等)前后自动执行的脚本。就像自动化门检——你进门之前先过安检,不合格就不让你进。 常见用途: pre-commit——commit 之前检查代码格式、跑 lint、跑单元测试。不通过就不让 commit。 commit-msg——检查 commit message 是否符合规范(如 Conventional Commits)。 pre-push——push 之前跑完整的测试套件。 post-merge——merge 之后自动安装依赖(npm install)。 post-receive——远程仓库收到 push 后自动部署(CI/CD 的原始形态)。 Hooks 放 .git/hooks/ 目录下,默认有一些 .sample 文件。去掉 .sample 后缀并设为可执行就生效。但 .git 目录不纳入版本控制,所以团队共享 hook 需要额外工具。
bash
# 查看已有 hooks
ls .git/hooks/

# 创建一个简单的 pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
echo "Running pre-commit checks..."
npm run lint
if [ $? -ne 0 ]; then
  echo "Lint failed! Commit rejected."
  exit 1
fi
EOF
chmod +x .git/hooks/pre-commit
Husky 是 Node.js 生态里管理 Git Hooks 的主流工具。它把 hooks 存在项目里(可版本控制),install 时自动设置。

02. 常用的 Client-Side Hooks

客户端 hooks 在你本地执行,常用这几个: pre-commit——最常用的 hook。在 commit message 输入之前执行。跑 lint、格式化、检查 debug 代码、阻止提交 secrets。返回非 0 退出码就阻止 commit。 prepare-commit-msg——在 commit message 编辑器打开前执行。可以自动生成 commit message 模板(比如从分支名提取 issue 号)。 commit-msg——输入完 commit message 后验证。检查是否包含 ticket 号、是否符合格式规范。 pre-rebase——rebase 之前执行,可以阻止向某些分支 rebase。 post-checkout——切换分支后执行。可以自动安装新分支的依赖、切换数据库连接。
bash
# commit-msg hook 示例——检查 commit message 格式
#!/bin/bash
MSG_FILE=$1
MSG=$(cat $MSG_FILE)
PATTERN="^(feat|fix|docs|style|refactor|test|chore): "

if ! echo "$MSG" | grep -qE "$PATTERN"; then
  echo "Commit message must follow: type: description"
  echo "Allowed types: feat, fix, docs, style, refactor, test, chore"
  exit 1
fi

03. Server-Side Hooks

服务端 hooks 在远程仓库收到 push 时执行,用来做服务端校验和自动化: pre-receive——收到 push 之后、更新引用之前执行。可以做最后的验证——检查是否有 merge conflict、检查提交者是否有权限。如果拒绝,push 就失败。 update——类似 pre-receive,但对每个被推送的分支分别执行。可以只阻止向 main 分支直接 push。 post-receive——push 更新完引用后执行。最经典的使用场景:自动部署。服务器收到代码后把最新代码部署到生产环境(最早的 CI/CD 实现)。 GitHub/GitLab 等平台封装了这些 hooks 为 Webhooks、GitHub Actions、GitLab CI——属于高级版服务端 hooks,更灵活好用。
bash
# 服务端 post-receive hook——自动部署
#!/bin/bash
# 放在裸仓库 (bare repo) 的 hooks 目录
DEPLOY_DIR="/var/www/myapp"
GIT_DIR="/var/www/myapp.git"

while read old new ref; do
  if [ "$ref" = "refs/heads/main" ]; then
    git --work-tree=$DEPLOY_DIR --git-dir=$GIT_DIR checkout -f main
    cd $DEPLOY_DIR
    npm ci --production
    pm2 restart myapp
    echo "Deployed!"
  fi
done
服务端 hooks 在远程仓库上执行,脚本 bug 可能导致 push 失败或服务异常。务必在测试环境验证过再上生产。

04. 用 Husky 管理 Hooks(团队共享)

.git/hooks 目录不在版本控制里,团队成员没法共享 hook。Husky 解决了这个问题——它把 hook 配置存在项目代码里(如 .husky/pre-commit),npm install 时自动设置 hook。 安装 Husky:npx husky init。之后在 .husky/ 目录下创建 hook 文件。比如 .husky/pre-commit 内容为 npm run lint。 配合 lint-staged——只对 git add 后的文件跑 lint,不对所有文件跑(省时间)。即使项目有几千个文件,只检查你改了的几个。 这两个工具配合是前端项目的标准配置——husky 触发 hook,lint-staged 在 pre-commit hook 里只跑改动文件的检查。
bash
# 安装 husky
npm install husky --save-dev
npx husky init

# 创建 pre-commit hook
echo "npx lint-staged" > .husky/pre-commit

# package.json 里配置 lint-staged
{
  "lint-staged": {
    "*.{js,ts,jsx,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{css,scss}": ["stylelint --fix"]
  }
}
lint-staged 只检查暂存区的文件,不会对整个项目全量扫描。commit 速度不受项目大小影响。

05. Hooks 的坑与最佳实践

Git Hooks 强大但也容易出问题: 1. 别在 hook 里执行太慢的任务——pre-commit 每次提交都跑,如果跑了完整的测试套件(几分钟),commit 体验极差。pre-commit 适合轻量检查(lint),重任务放到 pre-push 或 CI。 2. 提供跳过方法——偶尔紧急情况需要跳过 hook。git commit --no-verify 跳过 pre-commit 和 commit-msg hook。但要慎用,别成为习惯。 3. 跨平台问题——hook 脚本可能用了 bash 特有的语法,Windows 上不能跑。Husky 对跨平台有处理。 4. Hook 脚本保持简单——hook 脚本越复杂越容易出 bug。复杂逻辑应该抽成独立的脚本文件,hook 文件里只调用它。
bash
# 跳过 hooks(紧急情况用)
git commit --no-verify -m "hotfix"
git push --no-verify

# hook 里调独立脚本
# .husky/pre-commit
#!/bin/sh
node scripts/pre-commit-check.js

# pre-push hook——推送前跑测试
#!/bin/sh
npm test
pre-commit hook 超 3 秒就该优化了。重复提交代码是很频繁的操作,hook 太慢会让人想用 --no-verify 跳过去。

知识测验

1/5正确 0

pre-commit hook 什么时候触发?

下一节

Git 工作流

下一节