Skip to content

客户端钩子

客户端钩子在开发者本地执行,用于自动化日常开发工作流。

pre-commit:提交前检查

pre-commit 在执行 git commit 时最先触发,用于检查即将提交的快照:

bash
#!/bin/sh
# .git/hooks/pre-commit

echo "运行代码检查..."

# 运行 ESLint(Node.js 项目)
npx eslint --ext .js,.ts src/
if [ $? -ne 0 ]; then
  echo "ESLint 检查失败,请修复后再提交"
  exit 1
fi

# 运行 Prettier 格式检查
npx prettier --check "src/**/*.{js,ts,json}"
if [ $? -ne 0 ]; then
  echo "代码格式不符合规范,请运行 prettier --write 修复"
  exit 1
fi

# 运行单元测试(如果测试很快)
npm test -- --watchAll=false
if [ $? -ne 0 ]; then
  echo "测试失败,请修复后再提交"
  exit 1
fi

echo "检查通过!"
exit 0

只检查已暂存的文件:

bash
#!/bin/sh
# 只对已暂存的 JS 文件运行 lint
STAGED_JS_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts)$')

if [ -n "$STAGED_JS_FILES" ]; then
  echo "$STAGED_JS_FILES" | xargs npx eslint
  if [ $? -ne 0 ]; then
    exit 1
  fi
fi
exit 0

prepare-commit-msg:自动填充提交信息

在提交信息编辑器打开前执行,可以自动填充提交信息模板:

bash
#!/bin/sh
# .git/hooks/prepare-commit-msg
# 参数:$1=提交信息文件路径,$2=提交来源,$3=提交 SHA-1

COMMIT_MSG_FILE="$1"
COMMIT_SOURCE="$2"

# 如果是普通提交(非合并、非 amend)
if [ -z "$COMMIT_SOURCE" ]; then
  # 获取当前分支名
  BRANCH=$(git branch --show-current)
  
  # 从分支名提取 Issue 编号(如 feature/123-user-login)
  ISSUE=$(echo "$BRANCH" | grep -oE '^[a-z]+/([0-9]+)' | grep -oE '[0-9]+')
  
  if [ -n "$ISSUE" ]; then
    # 在提交信息开头添加 Issue 引用
    CURRENT_MSG=$(cat "$COMMIT_MSG_FILE")
    echo "Refs #$ISSUE" > "$COMMIT_MSG_FILE"
    echo "" >> "$COMMIT_MSG_FILE"
    echo "$CURRENT_MSG" >> "$COMMIT_MSG_FILE"
  fi
fi

commit-msg:验证提交信息格式

在提交信息写入后执行,用于验证提交信息是否符合规范:

bash
#!/bin/sh
# .git/hooks/commit-msg
# 参数:$1=提交信息文件路径

COMMIT_MSG_FILE="$1"
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")

# 验证 Conventional Commits 格式
# 格式:<type>(<scope>): <description>
CONVENTIONAL_PATTERN="^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?: .{1,100}$"

if ! echo "$COMMIT_MSG" | grep -qE "$CONVENTIONAL_PATTERN"; then
  echo "错误:提交信息不符合 Conventional Commits 规范!"
  echo ""
  echo "正确格式:<type>(<scope>): <description>"
  echo "示例:"
  echo "  feat(auth): 添加用户登录功能"
  echo "  fix(api): 修复分页查询问题"
  echo "  docs: 更新 README"
  echo ""
  echo "允许的 type:feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert"
  exit 1
fi

# 检查标题行长度
FIRST_LINE=$(head -1 "$COMMIT_MSG_FILE")
if [ ${#FIRST_LINE} -gt 72 ]; then
  echo "错误:提交信息标题行不能超过 72 个字符(当前 ${#FIRST_LINE} 个字符)"
  exit 1
fi

exit 0

post-commit:提交后通知

在提交完成后执行,常用于通知或日志记录(退出码不影响提交):

bash
#!/bin/sh
# .git/hooks/post-commit

# 显示提交摘要
echo "✅ 提交成功!"
git log HEAD -1 --format="提交: %h - %s"

# 可以在这里发送通知、更新日志等
# 注意:此钩子的退出码不影响提交结果

pre-push:推送前检查

git push 实际传输数据前执行,可以运行更全面的测试:

bash
#!/bin/sh
# .git/hooks/pre-push
# 通过 stdin 接收要推送的引用信息

REMOTE="$1"
URL="$2"

# 读取推送的分支信息
while read local_ref local_sha remote_ref remote_sha; do
  # 检查是否要推送到 main 分支
  if [[ "$remote_ref" == refs/heads/main ]]; then
    echo "推送到 main 分支,运行完整测试..."
    
    npm test
    if [ $? -ne 0 ]; then
      echo "❌ 测试失败,推送已中止"
      exit 1
    fi
    
    npm run build
    if [ $? -ne 0 ]; then
      echo "❌ 构建失败,推送已中止"
      exit 1
    fi
  fi
done

echo "✅ 推送前检查通过"
exit 0

pre-rebase:变基前检查

在变基操作开始前执行:

bash
#!/bin/sh
# .git/hooks/pre-rebase
# $1=rebase 的上游分支,$2=要变基的分支

UPSTREAM="$1"
BRANCH="$2"

# 阻止对已推送的主分支执行变基
if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
  echo "错误:不允许对主分支 $BRANCH 执行变基操作!"
  exit 1
fi
exit 0

post-merge:合并后操作

git merge 完成后执行,常用于自动更新依赖:

bash
#!/bin/sh
# .git/hooks/post-merge

# 检查 package.json 是否变更(依赖更新)
CHANGED=$(git diff HEAD@{1} HEAD --name-only | grep "package.json")

if [ -n "$CHANGED" ]; then
  echo "package.json 已更新,正在重新安装依赖..."
  npm install
fi

post-checkout:切换后操作

git checkout/git switch 完成后执行:

bash
#!/bin/sh
# .git/hooks/post-checkout
# $1=旧 HEAD,$2=新 HEAD,$3=1(切换分支)或 0(文件检出)

OLD_HEAD="$1"
NEW_HEAD="$2"
IS_BRANCH_CHECKOUT="$3"

# 只在切换分支时执行(不是文件检出)
if [ "$IS_BRANCH_CHECKOUT" = "1" ]; then
  BRANCH=$(git branch --show-current)
  echo "已切换到分支:$BRANCH"
  
  # 检查 package.json 是否有变更
  CHANGED=$(git diff "$OLD_HEAD" "$NEW_HEAD" --name-only | grep "package.json")
  if [ -n "$CHANGED" ]; then
    echo "依赖可能已更新,建议运行:npm install"
  fi
fi

总结

钩子触发时机常见用途
pre-commitgit commit 前代码检查、格式化
prepare-commit-msg编辑器打开前自动填充模板
commit-msg信息写入后验证信息格式
post-commit提交完成后通知、日志
pre-push推送前运行测试
pre-rebase变基前防止误操作
post-merge合并后更新依赖
post-checkout切换后环境同步

合理配置客户端钩子,可以自动化大量重复性的质量检查工作,减少因遗忘检查而引入的问题。