Pre-commit Hooks Setup Reference
Pre-commit hooks setup reference
TL;DR
- Bottom line: Use the
pre-commitframework with.pre-commit-config.yamlto automatically run linters, formatters, and checks before every git commit; pin hook versions by tag. - Key tool/command:
pre-commit install && pre-commit run --all-files - Watch out for: Forgetting to run
pre-commit installafter cloning — hooks are git-local. - Works with: Any git repository, Python 3.9+, hooks for every major language.
Constraints
- pre-commit requires Python 3.9+ (v4.x)
.pre-commit-config.yamlmust be at repository root- Hooks run on staged files only — unstaged changes are stashed
pre-commit installrequired once per clone- Pin hook repos by
rev(tag/SHA) — never use branch names
Quick Reference
| Command | Purpose |
|---|---|
pre-commit install | Install git hooks (once per clone) |
pre-commit run --all-files | Run all hooks on entire repo |
pre-commit run <hook-id> | Run a single hook |
pre-commit autoupdate | Update all hook versions |
SKIP=hook-id git commit | Skip specific hook |
git commit --no-verify | Skip ALL hooks (emergency) |
pre-commit clean | Clear cached environments |
pre-commit validate-config | Validate YAML syntax |
pre-commit try-repo <url> | Test a hook repo |
Decision Tree
START
├── Python? → ruff + mypy + pre-commit-hooks
├── JS/TS? → eslint + prettier via mirrors
├── Full-stack? → ruff + mypy + eslint + prettier
├── Go? → golangci-lint + gofmt
├── Need secrets detection? → gitleaks
└── DEFAULT → pre-commit-hooks (basics)
Step-by-Step Guide
1. Install pre-commit
[src1]
pip install pre-commit
# or: pipx install pre-commit
# or: brew install pre-commit
2. Create .pre-commit-config.yaml
[src1]
repos:
- 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-json
- id: check-added-large-files
args: ['--maxkb=500']
- id: check-merge-conflict
- id: detect-private-key
3. Install git hooks
pre-commit install
4. Run on all files
pre-commit run --all-files
5. Add language-specific hooks
[src4]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.6
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
6. Keep hooks updated
pre-commit autoupdate
Code Examples
Python project: Ruff + MyPy
repos:
- 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
- id: detect-private-key
- id: debug-statements
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.6
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.14.1
hooks:
- id: mypy
additional_dependencies: [types-requests, types-PyYAML]
JavaScript/TypeScript: ESLint + Prettier
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-json
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v9.20.0
hooks:
- id: eslint
files: \.(js|jsx|ts|tsx)$
additional_dependencies:
- [email protected]
- [email protected]
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8
hooks:
- id: prettier
types_or: [javascript, jsx, ts, tsx, css, json, yaml, markdown]
Full-stack + security
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-merge-conflict
- id: no-commit-to-branch
args: ['--branch', 'main']
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.6
hooks:
- id: ruff
files: ^backend/
- id: ruff-format
files: ^backend/
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8
hooks:
- id: prettier
files: ^frontend/
- repo: https://github.com/gitleaks/gitleaks
rev: v8.23.3
hooks:
- id: gitleaks
Anti-Patterns
Wrong: Using branch names for rev
# ❌ BAD — non-reproducible
rev: main
Correct: Pin to version tags
# ✅ GOOD — pinned, reproducible
rev: v0.9.6
Wrong: Running heavy checks on all files
# ❌ BAD — 30+ seconds per commit
- id: mypy
pass_filenames: false # Checks ALL files
Correct: Check only staged files
# ✅ GOOD — only staged files (default)
- id: mypy
# pass_filenames: true is the default
Wrong: Not documenting installation
# ❌ BAD — new contributors don't know hooks exist
Correct: Add to setup script
# ✅ GOOD — in Makefile
setup:
pip install pre-commit
pre-commit install
Common Pitfalls
- Hooks not running: Fix:
pre-commit installin setup script. [src1] - Slow commits: Fix: use
pass_filenames: true(default). [src6] - Stale hook env: Fix:
pre-commit clean && pre-commit install. [src1] - Missing type stubs: Fix: list in
additional_dependencies. [src2] - Wrong file matching: Fix: use
files,exclude,types_or. [src1] - CI and local disagree: Fix:
pre-commit autoupdateand commit. [src7]
Diagnostic Commands
# Version
pre-commit --version
# Validate config
pre-commit validate-config
# Run all hooks
pre-commit run --all-files
# Run specific hook
pre-commit run ruff --all-files
# Update hooks
pre-commit autoupdate
# Clear cache
pre-commit clean
# Test a repo
pre-commit try-repo https://github.com/astral-sh/ruff-pre-commit --all-files
Version History & Compatibility
| Version | Status | Breaking Changes | Migration Notes |
|---|---|---|---|
| pre-commit 4.x | Current | Python 3.8 dropped | Ensure Python 3.9+ |
| pre-commit 3.x | Maintenance | — | Last for Python 3.8 |
| Ruff | Current | Replaces black, isort, flake8 | ruff + ruff-format |
When to Use / When Not to Use
| Use When | Don't Use When | Use Instead |
|---|---|---|
| Any git repo with 2+ contributors | Solo prototype | Manual checks |
| Multi-language project | Node.js-only (JS ecosystem) | Husky + lint-staged |
| Need consistent quality | CI-only checks sufficient | GitHub Actions linting |
Important Caveats
pre-commit installis per-clone — each developer must run it--no-verifyskips ALL hooks — useSKIP=hook-idinstead- Hooks that modify files (formatting) leave changes unstaged — re-add before commit
additional_dependenciesmust stay in sync with project versions- Pre-commit manages own virtualenvs per hook — run
pre-commit gcperiodically