使用 Git Pre-Commit Hook 自动化代码检查

每个开发者都经历过这样的挫折:推送代码后,CI/CD 流水线因为格式问题或 linting 错误而失败。如果你能在这些问题到达代码仓库之前就发现它们会怎样?Git pre-commit hook 提供了一个强大的解决方案,它在本地运行自动化检查,既节省时间又保持团队代码质量的一致性。
本文将指导你使用 pre-commit 框架设置 git pre-commit hook,配置 ESLint、Prettier 和 Black 等重要工具,并实施最佳实践来保持开发工作流程的顺畅和高效。
核心要点
- Git pre-commit hook 在提交前运行自动化检查,防止常见问题进入代码仓库
- pre-commit 框架通过 YAML 配置和跨语言支持简化了 hook 管理
- 速度优化和渐进式采用对团队接受度至关重要
- Husky 和 Lefthook 等替代工具为特定需求提供了不同的方法
什么是 Git Pre-Commit Hook?
Git hook 是 Git 在提交、推送和接收等事件之前或之后执行的脚本。git pre-commit hook 特指在你暂存更改之后但在 Git 创建提交之前运行的脚本。如果 hook 以非零状态退出,Git 会中止提交,让你有机会先修复问题。
虽然你可以在 .git/hooks/
中编写简单的 shell 脚本作为 hook,但随着项目的增长,管理它们会变得复杂。这就是 pre-commit 框架 的优势所在——它提供了一种标准化的方式来管理和在团队间共享 hook。
为什么使用 Pre-Commit 框架?
pre-commit 框架解决了原生 Git hook 的几个痛点:
- 简化安装和更新:Hook 有版本控制,通过简单的 YAML 配置安装
- 跨语言支持:从一个配置文件运行 Python linter、JavaScript 格式化工具和 shell 脚本
- 选择性执行:Hook 只在更改的文件上运行,保持提交速度
- 团队一致性:在整个团队中共享相同的
.pre-commit-config.yaml
文件
在项目中设置 Pre-Commit
首先,安装 pre-commit 包:
pip install pre-commit
在项目根目录创建 .pre-commit-config.yaml
文件。以下是一个涵盖常见需求的实用配置:
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-added-large-files
# 使用 Black 进行 Python 格式化
- repo: https://github.com/psf/black
rev: 24.2.0
hooks:
- id: black
files: \.py$
# 使用 Mypy 进行 Python 类型检查
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
additional_dependencies: [types-all]
files: \.py$
# 使用 Prettier 处理 JavaScript/TypeScript
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.1.0
hooks:
- id: prettier
files: \.(js|jsx|ts|tsx|json|css|md)$
# 使用 ESLint 进行 JavaScript/TypeScript 代码检查
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.56.0
hooks:
- id: eslint
files: \.(js|jsx|ts|tsx)$
additional_dependencies:
- eslint-config-standard
- eslint-plugin-react
# 运行项目测试
- repo: local
hooks:
- id: pytest
name: pytest
entry: pytest
language: system
pass_filenames: false
always_run: true
安装 git hook:
pre-commit install
现在你的 hook 将在每次提交时自动运行。要在所有文件上手动运行它们:
pre-commit run --all-files
Discover how at OpenReplay.com.
优化 Hook 性能
速度很重要——缓慢的 hook 会阻碍开发者频繁提交。遵循以下实践:
仅在暂存文件上运行 hook:pre-commit 框架默认这样做,但要确保你的自定义 hook 遵循这个模式。
使用快速工具:在速度至关重要时,选择 Ruff 而不是 Flake8 处理 Python,或选择 Biome 而不是 ESLint 处理 JavaScript。
在本地跳过昂贵的检查:将全面的测试套件保留给 CI/CD。本地 hook 应专注于格式化和基础 linting 等快速收益。
尽可能并行化:一些工具支持并行执行。例如,pytest 可以使用 pytest-xdist
并行运行测试。
团队采用最佳实践
让团队采用 pre-commit hook 需要周到的实施:
从小处开始:从去除尾随空格等非侵入性 hook 开始。逐步添加更严格的检查。
记录设置过程:在 README 中包含安装说明。将其作为入职流程的一部分。
处理环境差异:尽可能使用与语言无关的 hook。对于特定语言的工具,确保你的 .pre-commit-config.yaml
指定兼容版本。
提供逃生通道:有时开发者需要绕过 hook。--no-verify
标志跳过所有 hook:
git commit --no-verify -m "Emergency fix"
谨慎使用这个功能,并记录何时适合使用。
替代方案
虽然 pre-commit 是最受欢迎的框架,但也存在替代方案:
Husky:在 JavaScript 项目中很受欢迎,与 npm 脚本集成良好。
原生 Git hook:.git/hooks/
中的直接 shell 脚本提供最大控制权,但缺乏可移植性。
Lefthook:快速、跨平台的替代方案,支持并行执行。
对于大多数团队,pre-commit 在功能、性能和生态系统支持方面提供了最佳平衡。
常见陷阱和解决方案
CI 上的 Hook 失败:确保 CI 环境安装了所有必要的依赖项。考虑将 pre-commit run --all-files
作为 CI 步骤运行。
平台特定问题:在团队使用的所有平台上测试 hook。如果需要,使用 Docker 创建一致的环境。
配置文件中的合并冲突:保持 .pre-commit-config.yaml
更改最小化和原子化。在单独的提交中更新 hook 版本。
结论
Git pre-commit hook 将代码质量从被动过程转变为主动过程。通过在问题进入代码仓库之前捕获它们,你节省了时间,减少了上下文切换,并在代码库中保持了更高的标准。从基础格式化 hook 开始,逐步添加 linter 和类型检查器,并始终优先考虑速度以保持团队工作流程的顺畅。
成功采用的关键在于平衡彻底性与开发者体验。快速、专注的 hook 提供即时价值,将成为团队工作流程中不可或缺的一部分。
常见问题
可以,pre-commit 在 monorepo 中表现出色。你可以为不同的文件模式和目录配置不同的 hook。在配置中使用 files 和 exclude 键来针对仓库中的特定语言或文件夹。
将 nvm 或 pyenv 等语言版本管理器与 pre-commit 一起使用。或者,在 hook 配置中指定 language_version,或使用基于 Docker 的 hook 来确保所有开发者机器上的环境一致。
没有安装 pre-commit 时,hook 不会在本地运行,但你的代码仍会提交。为了强制执行标准,在 CI 流水线中运行 pre-commit 检查作为安全网,并将 pre-commit 安装作为项目设置文档的一部分。
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.