8.1 pre-commit 框架概览
pre-commit 是一个用 Python 写的 Git hooks 管理框架——你只写一份 YAML,它帮你拉取、安装、运行各种检查工具。核心概念:
- hook(钩子)
- 一段要在 Git 事件(commit、push)发生时自动运行的程序。本身是 Git 原生功能,但 pre-commit 框架在此之上提供了统一的声明式管理。
- .pre-commit-config.yaml
- 项目根目录的配置文件,声明了要启用哪些钩子、从哪个仓库拉取、固定到哪个版本。
- pre-commit install
- 一条命令把
.git/hooks/pre-commit指向 pre-commit 框架,之后git commit会自动触发。
8.2 安装 pre-commit
$ uv tool install pre-commit
# 或:pipx install pre-commit / pip install pre-commit
$ pre-commit install
# pre-commit installed at .git/hooks/pre-commit
8.3 最小 .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.3 # 务必固定到具体 tag
hooks:
- id: ruff # 跑 ruff check(带自动修复)
args: [--fix]
- id: ruff-format # 跑 ruff format
把这个文件提交进仓库,队友 clone 下来后跑一次 pre-commit install 就能享受同样的钩子——配置跟代码一起版本化。
8.4 钩子的三种 ID
| id | 等价命令 | 用途 |
|---|---|---|
ruff | ruff check --force-exclude | 检查(可加 --fix) |
ruff-check | 同上的别名(新版本推荐) | 检查 |
ruff-format | ruff format --force-exclude | 格式化 |
--force-exclude 的作用
pre-commit 默认会把"暂存的文件列表"显式传给钩子。但 Ruff 的 exclude 配置是在"遍历文件"时生效的——当文件被显式传入时默认会跳过 exclude 检查。--force-exclude 强制让 exclude 仍然生效,避免生成目录里的文件被误检查。
8.5 常见搭配
repos:
# 通用检查(pre-commit 官方 hooks)
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
# Ruff(lint + format)
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.3
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
# mypy 类型检查(可选)
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.0
hooks:
- id: mypy
additional_dependencies: ["types-requests"]
8.6 stages:什么时候跑
默认钩子在 pre-commit 阶段运行。可指定其他阶段:
- id: ruff
args: [--fix]
stages: [pre-commit, pre-push] # 同时在 push 前跑一次
- id: ruff-format
stages: [manual] # 只在手动 pre-commit run --hook-stage manual 时跑
8.7 限定文件范围
- id: ruff
args: [--fix]
files: "^src/" # 只扫 src/
exclude: "^tests/fixtures/"
8.8 常用命令
$ pre-commit install # 安装钩子(首次)
$ pre-commit run # 对暂存文件跑所有钩子
$ pre-commit run --all-files # 对所有文件跑一次(迁移时用)
$ pre-commit run ruff # 只跑 ruff 钩子
$ pre-commit autoupdate # 自动升级到最新 tag
$ pre-commit uninstall # 卸载
8.9 绕过钩子:--no-verify(请慎用)
$ git commit --no-verify -m "emergency fix"
不推荐。CI 端应同样跑 Ruff,否则绕过等于没跑。强烈建议 CI 里加 ruff check . 作为兜底。
钩子失败后文件可能被改
ruff --fix 或 ruff-format 成功修改文件后,该钩子会失败(因为"有未暂存的修改")——这是设计。解决:git add -u 再 git commit。想让 pre-commit 自动 stage 修改后的文件,可在 CI/个人脚本里用 pre-commit run --files ... && git add -u 这类流程,但普通提交场景还是手动 add 更安全。
8.10 小结
- pre-commit 让提交前自动跑 Ruff + 其他检查,防止脏代码进历史。
- 仓库地址:astral-sh/ruff-pre-commit,两条
id: ruff+id: ruff-format搞定。 - rev 要锁具体 tag,
pre-commit autoupdate再升级。 - CI 端必须补一道
ruff check,堵住--no-verify绕过路径——下一章详解。