Appearance
客户端钩子
客户端钩子在开发者本地执行,用于自动化日常开发工作流。
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 0prepare-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
ficommit-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 0post-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 0pre-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 0post-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
fipost-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-commit | git commit 前 | 代码检查、格式化 |
prepare-commit-msg | 编辑器打开前 | 自动填充模板 |
commit-msg | 信息写入后 | 验证信息格式 |
post-commit | 提交完成后 | 通知、日志 |
pre-push | 推送前 | 运行测试 |
pre-rebase | 变基前 | 防止误操作 |
post-merge | 合并后 | 更新依赖 |
post-checkout | 切换后 | 环境同步 |
合理配置客户端钩子,可以自动化大量重复性的质量检查工作,减少因遗忘检查而引入的问题。