pre-commit: Git Hook Framework for Python
pre-commit is a framework for managing reusable Git hooks. A project declares hooks in .pre-commit-config.yaml; pre-commit installs each hook’s runtime environment, runs hooks at selected Git stages, and blocks the Git operation when a hook fails or modifies files.
In Python projects, pre-commit commonly runs Ruff, mypy, ty, whitespace checks, YAML checks, and repository-local scripts. For setup steps, see How to set up pre-commit hooks for a Python project.
Key Features
- Git hook installation: writes hook scripts for
pre-commit,pre-push,commit-msg, and other supported Git hook stages. - Shared configuration: reads
.pre-commit-config.yamlfrom the repository root. - Pinned hook revisions: clones remote hook repositories at the configured
rev:so teams run the same hook code. - Isolated hook environments: creates language-specific environments for Python, Node, Ruby, Go, Rust, Docker, and other hook backends.
- File filtering: runs hooks only on matching files by default, with options for all files, explicit files, or files changed between refs.
- Manual runs: supports
pre-commit run,pre-commit run --all-files, andpre-commit run <hook-id>. - Hook updates: updates configured hook repository revisions with
pre-commit autoupdate.
Configuration
The main configuration file is .pre-commit-config.yaml. Each entry names a hook repository, a pinned revision, and one or more hook IDs:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yamlHook entries can override file patterns, arguments, language versions, stages, and additional dependencies. Hook authors publish metadata in .pre-commit-hooks.yaml, which tells pre-commit the hook ID, name, entry point, language backend, and file matching rules.
Hook Stages
Despite its name, pre-commit is not limited to the pre-commit Git stage. It supports client-side Git hooks such as pre-push, commit-msg, prepare-commit-msg, post-checkout, post-commit, post-merge, post-rewrite, pre-rebase, and pre-merge-commit, plus the special manual stage for hooks run on demand with pre-commit run --hook-stage manual.
Projects can install more than one stage with repeated --hook-type flags or by setting default_install_hook_types in .pre-commit-config.yaml.
Python Project Fit
pre-commit fits projects that want the same checks to run before every commit without asking each contributor to remember separate commands. It is especially common for formatting and linting gates, because those tools can fix files before review.
pre-commit does not replace pytest, a type checker, or CI. It runs configured tools at a Git boundary. CI still needs to run merge-blocking checks because contributors can skip local hooks with git commit --no-verify or by not installing hooks.
pre-commit and prek
prek is a compatible reimplementation of pre-commit. It reads the same .pre-commit-config.yaml format and is designed as a drop-in replacement for the original CLI.
Use pre-commit when the project wants the original implementation, the broadest compatibility with documented pre-commit behavior, or a conservative default already known by contributors. Use prek when the project wants faster hook installation and execution while keeping the same configuration file.
Pros
- Standard hook format used across many Python repositories
- Reusable hook repositories instead of copied local shell scripts
- Per-hook isolated environments for language-specific tools
- Pinned hook revisions for consistent local and CI behavior
- Works with Python and non-Python tooling in the same repository
- CI-friendly
run --all-filesworkflow
Cons
- Hooks must be installed locally before
git commitruns them - Contributors can bypass hooks with
--no-verify - First runs can be slower while hook repositories and environments install
- Python-based implementation is slower than compatible tools such as prek
- Hook isolation can surprise tools that need project dependencies unless the hook config supplies them