Workflows
提交前检查工作流
利用 pre-commit hook 在代码进入仓库前自动运行检查,守住代码质量第一道关。
- 要把命令组合成稳定流程的团队成员
- 需要处理协作顺序和分支边界的人
- 知道 fetch / pull / push / branch 的基本作用
- 能理解一条分支为什么会分叉
- 照抄流程却没确认当前分支关系
- 在共享分支上用错整合方式
一句话理解
pre-commit hook 在 git commit 执行前触发,让你可以在代码正式进入 Git 历史之前自动运行 lint、测试、格式化等检查,把住质量的第一道关。
git commit编辑器保存文件暂存区
代码格式lint 结果测试通过敏感信息扫描
hook 是最便宜的防御线,在本地阻止问题比在 CI 中发现要快得多。
为什么需要 pre-commit hook
# 没有 hook 的场景
git commit -m "feat: add feature"
git push
# CI 失败!代码格式不对、有 lint 错误...
# 回到本地修复,产生修复提交
# 有 hook 的场景
git commit -m "feat: add feature"
# pre-commit hook 自动运行
# ❌ ESLint 错误,提交被阻止
# 本地修复后再提交
# ✅ 检查通过,提交成功
基本 hook 配置
手动配置
# 进入仓库的 hooks 目录
cd .git/hooks
# 创建 pre-commit 脚本
cat > pre-commit << 'EOF'
#!/bin/sh
# 运行 lint
echo "Running linter..."
npm run lint
if [ $? -ne 0 ]; then
echo "Lint failed. Commit aborted."
exit 1
fi
# 运行格式化检查
echo "Checking formatting..."
npm run format:check
if [ $? -ne 0 ]; then
echo "Formatting check failed. Run 'npm run format' first."
exit 1
fi
# 运行单元测试
echo "Running tests..."
npm test
if [ $? -ne 0 ]; then
echo "Tests failed. Commit aborted."
exit 1
fi
EOF
chmod +x pre-commit
使用 pre-commit 框架(推荐)
# 安装 pre-commit 工具
pip install pre-commit
# 或 brew install pre-commit
# 在项目根目录创建配置文件
cat > .pre-commit-config.yaml << 'EOF'
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-json
- id: check-added-large-files
args: ['--maxkb=500']
- repo: https://github.com/psf/black
rev: 23.12.1
hooks:
- id: black
- repo: https://github.com/eslint/eslint
rev: v8.56.0
hooks:
- id: eslint
additional_dependencies: ['eslint@8.56.0']
- repo: local
hooks:
- id: custom-check
name: Custom Project Check
entry: ./scripts/check.sh
language: script
files: \\.py$
EOF
# 安装 hooks
cd /path/to/your/repo
pre-commit install
# 手动运行所有 hooks
cd /path/to/your/repo
pre-commit run --all-files
渐进式引入策略
阶段 1:非阻塞检查(预警模式)
# 初期先让 hook 只输出警告,不阻止提交
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
if ! npm run lint; then
echo "⚠️ Warning: Lint errors found. Please fix before pushing."
# 不退出非零,允许提交
fi
EOF
阶段 2:选择性阻塞
# 只检查本次要提交的变更(staged files)
staged_files=$(git diff --cached --name-only --diff-filter=ACM | grep '\.js$')
if [ -n "$staged_files" ]; then
echo "$staged_files" | xargs npx eslint
if [ $? -ne 0 ]; then
exit 1
fi
fi
阶段 3:全面强制
# 全量检查,阻塞提交
npm run lint
npm run test:unit
只检查变更文件
# 获取本次要提交的文件列表
staged_files=$(git diff --cached --name-only --diff-filter=ACM)
# 只检查相关文件
if echo "$staged_files" | grep -q '\.py$'; then
echo "$staged_files" | grep '\.py$' | xargs python -m flake8
fi
跳过 hook 的应急方案
# 紧急修复时临时跳过 hook
git commit -m "hotfix: critical bug" --no-verify
# 但团队应该对 --no-verify 的使用有审计和约束
团队同步 hook
问题:hooks 在 .git/hooks 中,不会被提交到仓库
解决方案 1:使用 pre-commit 框架
.pre-commit-config.yaml 可以提交到仓库,团队成员只需运行 pre-commit install。
解决方案 2:自定义初始化脚本
# scripts/setup-hooks.sh
#!/bin/sh
cp scripts/git-hooks/pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
echo "Hooks installed."
# package.json
# "postinstall": "./scripts/setup-hooks.sh"
解决方案 3:core.hooksPath
# 把 hooks 放在仓库可提交的目录中
mkdir -p scripts/git-hooks
cp .git/hooks/pre-commit scripts/git-hooks/
# 配置 Git 使用该目录
git config core.hooksPath scripts/git-hooks
# 把 scripts/git-hooks/ 提交到仓库
性能优化
快速检查优先
#!/bin/sh
# 先运行最快的检查,失败立即退出
# 1. 检查大文件(最快)
if ! check-large-files; then exit 1; fi
# 2. 检查尾随空格
if ! check-trailing-whitespace; then exit 1; fi
# 3. 运行 lint(中等)
if ! run-lint-on-staged; then exit 1; fi
# 4. 运行测试(最慢)
if ! run-tests-related-to-staged; then exit 1; fi
并行运行
#!/bin/sh
# 并行运行独立的检查
npm run lint &
lint_pid=$!
npm run typecheck &
type_pid=$!
wait $lint_pid
lint_result=$?
wait $type_pid
type_result=$?
if [ $lint_result -ne 0 ] || [ $type_result -ne 0 ]; then
exit 1
fi
最佳实践总结
- 先快后慢:最快的检查放在前面,失败立即退出
- 只查变更:不要全量检查,只检查 staged 文件
- 渐进启用:初期预警不阻塞,团队适应后再强制
- 提供绕过:保留
--no-verify应急通道,但要有审计 - 统一配置:用
.pre-commit-config.yaml或core.hooksPath确保团队一致 - 本地与 CI 互补:hook 守住第一道关,CI 守住最后一道关
注意事项
- hook 检查不要太慢,否则会严重影响开发体验
- 不同操作系统(macOS/Linux/Windows)的 hook 脚本要注意兼容性
- 使用 pre-commit 框架可以跨平台工作,是更推荐的方式
- hook 只在本地生效,不能替代 CI/CD 中的检查
- 团队成员需要培训,理解 hook 失败时的处理流程