代码质量从来不只是代码审查阶段的事。很多问题——格式不一致、调试语句残留、提交信息混乱——如果在提交那一刻就被拦截,团队能省下大量返工和沟通成本。Git Hooks 正是实现这一目标的核心机制,而 pre-commit 和 commit-msg 是其中使用频率最高、收益最直接的两个钩子。
为什么选择 Git Hooks 而非 CI
CI 流水线当然也能做检查,但它有两个天然短板:反馈慢和修复成本高。开发者推送代码后等几分钟才看到 lint 失败,此时上下文已经切换,修复动力大打折扣。Git Hooks 在本地提交时立即执行,问题在离开开发者机器之前就被解决。
另一个现实因素是 CI 资源。把格式检查、调试语句扫描这类轻量级校验放在本地钩子中完成,CI 只负责集成测试和构建验证,流水线会跑得更快、更稳定。
pre-commit:提交前的质量闸门
pre-commit 钩子在 git commit 执行时、提交信息编辑器打开之前触发。如果脚本以非零状态退出,提交会被中止。这意味着它是拦截问题代码的最后一道本地防线。
常见的 pre-commit 检查项
一个实用的 pre-commit 通常包含以下几类检查:
- 代码格式:运行 Prettier、Black、gofmt 等工具,确保提交的代码符合团队规范
- 静态分析:ESLint、Flake8、RuboCop 等 linter 快速扫描潜在错误
- 调试残留:搜索
console.log、debugger、binding.pry等调试语句 - 敏感信息:检测是否误提交了
.env文件、API Key 或私钥 - 大文件:阻止超过阈值的大文件进入仓库历史
手写一个 pre-commit 脚本
在 .git/hooks/ 目录下创建 pre-commit 文件(去掉 .sample 后缀),写入以下内容:
#!/bin/sh
# 获取暂存区的文件列表
staged_files=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts|jsx|tsx)$')
if [ -z "$staged_files" ]; then
exit 0
fi
# 检查调试语句
if echo "$staged_files" | xargs grep -n 'console\.log\|debugger' 2>/dev/null; then
echo "❌ 检测到调试语句,请移除后再提交。"
exit 1
fi
# 运行 ESLint
echo "$staged_files" | xargs npx eslint --quiet
if [ $? -ne 0 ]; then
echo "❌ ESLint 检查未通过,请修复后重新提交。"
exit 1
fi
echo "✅ pre-commit 检查通过"
exit 0
别忘了赋予执行权限:
chmod +x .git/hooks/pre-commit
这个脚本只检查暂存区的文件,而不是整个项目,这样在大型仓库中也能保持快速响应。
commit-msg:规范提交信息
commit-msg 钩子在开发者编写完提交信息后触发,接收一个参数——包含提交信息的临时文件路径。它最常见的用途是强制团队遵循统一的提交信息格式,比如 Conventional Commits。
为什么提交信息值得强制规范
- 自动化 changelog:
feat:、fix:、BREAKING CHANGE:等前缀可以被工具解析,自动生成版本变更日志 - 语义化版本:结合工具可以根据提交类型自动决定版本号递增策略
- 可追溯性:规范的提交信息让
git log真正成为可读的项目历史
实现 commit-msg 校验
创建 .git/hooks/commit-msg:
#!/bin/sh
commit_msg_file=$1
commit_msg=$(head -1 "$commit_msg_file")
# 允许 Merge 和 Revert 提交跳过检查
if echo "$commit_msg" | grep -qE '^(Merge|Revert)'; then
exit 0
fi
# 定义允许的类型
pattern='^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?: .{1,72}$'
if ! echo "$commit_msg" | grep -qE "$pattern"; then
echo "❌ 提交信息格式不符合规范。"
echo ""
echo "格式:<type>(<scope>): <subject>"
echo "示例:feat(auth): 添加 OAuth2 登录支持"
echo ""
echo "允许的类型:feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert"
exit 1
fi
exit 0
同样需要执行权限:
chmod +x .git/hooks/commit-msg
这个正则还限制了主题行不超过 72 个字符,这是 Git 社区的广泛共识,能保证 git log --oneline 的显示效果。
团队协作中的关键问题:钩子如何共享
.git/hooks/ 目录不会被 Git 跟踪,这意味着你写的钩子无法通过 git clone 分发给团队成员。解决这个问题有三种主流方案:
方案一:Husky(Node.js 生态)
Husky 是目前前端项目中最流行的方案。安装后,它会把钩子配置写入 package.json,随项目一起版本控制:
npx husky init
echo "npx lint-staged" > .husky/pre-commit
配合 lint-staged,可以只对暂存文件运行检查,速度极快。
方案二:pre-commit 框架(Python 生态)
pre-commit 这个工具本身与 Git 的 pre-commit 钩子同名但不同概念。它通过 .pre-commit-config.yaml 管理钩子,支持多语言,安装简单:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-added-large-files
方案三:自定义 hooks 目录
通过 git config core.hooksPath .githooks 将钩子目录指向项目内的 .githooks/ 文件夹,然后把这个目录提交到仓库。这是最轻量的方案,不依赖任何额外工具。
性能与体验的平衡
钩子太慢会严重影响开发体验。几个实用建议:
- 只检查暂存文件,不要全量扫描
- 并行执行独立检查项
- 提供跳过机制:允许通过
git commit --no-verify绕过(紧急情况用),但要在团队规范中明确使用条件 - 失败信息要具体:告诉开发者哪个文件、哪一行出了问题,以及如何修复
总结
pre-commit 和 commit-msg 两个钩子覆盖了代码质量的两个关键维度:代码本身的质量和提交历史的质量。前者通过自动化检查拦截低级错误,后者通过规范约束让项目历史保持可读和可追溯。
落地时建议循序渐进:先加一个最简单的格式检查钩子,让团队适应这种工作方式,再逐步增加检查项。工具的选择上,Node.js 项目用 Husky + lint-staged,Python 项目用 pre-commit 框架,其他场景用 core.hooksPath 自定义方案即可。关键是让钩子成为团队工作流的一部分,而不是一个被 --no-verify 频繁绕过的摆设。
未经允许不得转载:任鹏个人博客 » Git Hooks 实战:用 pre-commit 和 commit-msg 提升代码质量


朋友圈点赞图在线生成源码