Workflows

提交前检查工作流

利用 pre-commit hook 在代码进入仓库前自动运行检查,守住代码质量第一道关。

适合谁看
  • 要把命令组合成稳定流程的团队成员
  • 需要处理协作顺序和分支边界的人
前置知识
  • 知道 fetch / pull / push / branch 的基本作用
  • 能理解一条分支为什么会分叉
常见风险
  • 照抄流程却没确认当前分支关系
  • 在共享分支上用错整合方式

一句话理解

pre-commit hook 在 git commit 执行前触发,让你可以在代码正式进入 Git 历史之前自动运行 lint、测试、格式化等检查,把住质量的第一道关。

Pre-commit Hook 工作流pre-commit hook 在本地提交前执行检查,在问题进入仓库之前就捞出来。
触发时机
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

最佳实践总结

  1. 先快后慢:最快的检查放在前面,失败立即退出
  2. 只查变更:不要全量检查,只检查 staged 文件
  3. 渐进启用:初期预警不阻塞,团队适应后再强制
  4. 提供绕过:保留 --no-verify 应急通道,但要有审计
  5. 统一配置:用 .pre-commit-config.yamlcore.hooksPath 确保团队一致
  6. 本地与 CI 互补:hook 守住第一道关,CI 守住最后一道关

注意事项

  1. hook 检查不要太慢,否则会严重影响开发体验
  2. 不同操作系统(macOS/Linux/Windows)的 hook 脚本要注意兼容性
  3. 使用 pre-commit 框架可以跨平台工作,是更推荐的方式
  4. hook 只在本地生效,不能替代 CI/CD 中的检查
  5. 团队成员需要培训,理解 hook 失败时的处理流程