pre-commit¶
Framework for managing git hooks. Runs linters, formatters, security scanners before commits. Language-agnostic (Python, Go, Node.js, Rust). Prevents bad commits (syntax errors, secrets, formatting issues). Hooks defined in .pre-commit-config.yaml. Fast parallel execution. Integrates with CI/CD.
2026 Update
pre-commit 3.x brings performance improvements (30% faster). Native support for Ruff, Biome. Docker hooks no longer experimental. Remote hooks cached locally. Auto-update via pre-commit autoupdate. GitHub Actions integration official. 200+ hooks in community registry.
Quick Hits¶
# Installation
pip install pre-commit # Python (recommended)
brew install pre-commit # macOS
# Or: pipx install pre-commit # Isolated install
# Install hooks in repository
pre-commit install # Install git hooks # (1)!
pre-commit install --hook-type commit-msg # Commit message hooks
pre-commit install --hook-type pre-push # Pre-push hooks
# Run hooks manually (without committing)
pre-commit run --all-files # Run on all files # (2)!
pre-commit run # Run on staged files only
pre-commit run <hook-id> # Run specific hook
pre-commit run --files file1.py file2.py # Run on specific files
# Update hooks to latest versions
pre-commit autoupdate # Update .pre-commit-config.yaml # (3)!
# Uninstall hooks
pre-commit uninstall # Remove git hooks
# Clean cache
pre-commit clean # Remove unused hook environments
# Validate configuration
pre-commit validate-config # Check .pre-commit-config.yaml syntax
# Sample output (passing)
# Trim Trailing Whitespace.......Passed
# Fix End of Files................Passed
# Check YAML.....................Passed
# Black...........................Passed # (4)!
# Sample output (failing)
# Black...........................Failed
# - hook id: black
# - files were modified by this hook
#
# reformatted app.py
# 1 file reformatted.
- Installs hooks into
.git/hooks/pre-commitautomatically - Useful for testing hooks before committing
- Updates hook versions to latest releases (safe upgrade)
- Hooks run in parallel (fast execution)
Real talk:
- First commit after install is slow (builds hook environments)
- Subsequent commits are fast (environments cached)
- Failed hooks prevent commit (fix issues, re-commit)
- Some hooks auto-fix (formatters), others report (linters)
- Skip hooks with
git commit --no-verify(emergency only)
Configuration file (.pre-commit-config.yaml):
# Python project example
repos:
# Built-in hooks (trailing whitespace, EOF, YAML, etc.)
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace # (1)!
- 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 # (2)!
# Python: Ruff (linter + formatter, fast)
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.14
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format # (3)!
# Python: mypy (type checking)
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
additional_dependencies: [types-requests] # (4)!
# Security: Gitleaks (secret scanning)
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.1
hooks:
- id: gitleaks # (5)!
# Markdown: markdownlint
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.39.0
hooks:
- id: markdownlint
args: [--fix]
# Shell: shellcheck
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.9.0.6
hooks:
- id: shellcheck
- Removes trailing whitespace from all files
- Prevents committing SSH keys, AWS credentials
- Ruff replaces Black + Flake8 + isort (50x faster)
- Additional dependencies for type stubs
- Scans for secrets in code and git history
JavaScript/TypeScript project:
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-json
- id: check-yaml
# Biome (linter + formatter, Rust-based)
- repo: https://github.com/biomejs/pre-commit
rev: v1.5.1
hooks:
- id: biome-check
args: [--apply] # (1)!
# ESLint (alternative to Biome)
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v9.0.0
hooks:
- id: eslint
files: \.[jt]sx?$ # .js, .jsx, .ts, .tsx
types: [file]
additional_dependencies:
- eslint@9.0.0
- '@typescript-eslint/parser@7.0.0'
# Prettier (formatter)
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0
hooks:
- id: prettier
types_or: [javascript, jsx, ts, tsx, json, css] # (2)!
# Security: npm audit
- repo: local
hooks:
- id: npm-audit
name: npm audit
entry: npm audit --audit-level=high
language: system
pass_filenames: false # (3)!
- Biome auto-fixes issues (format + lint)
- types_or runs on multiple file types
- Local hooks run system commands
Go project:
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
# Go: gofmt + goimports
- repo: https://github.com/dnephin/pre-commit-golang
rev: v0.5.1
hooks:
- id: go-fmt # (1)!
- id: go-imports
- id: go-vet
- id: go-mod-tidy
# Go: golangci-lint (meta-linter)
- repo: https://github.com/golangci/golangci-lint
rev: v1.56.0
hooks:
- id: golangci-lint
args: [--fix] # (2)!
# Go: govulncheck (vulnerability scanner)
- repo: local
hooks:
- id: govulncheck
name: govulncheck
entry: govulncheck ./...
language: system
pass_filenames: false
- gofmt enforces Go formatting (mandatory)
- golangci-lint runs 50+ linters in parallel
Multi-language project:
repos:
# Universal checks
- 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-merge-conflict
- id: detect-private-key
# Python
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.14
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
# JavaScript/TypeScript
- repo: https://github.com/biomejs/pre-commit
rev: v1.5.1
hooks:
- id: biome-check
args: [--apply]
files: \.[jt]sx?$
# Go
- repo: https://github.com/dnephin/pre-commit-golang
rev: v0.5.1
hooks:
- id: go-fmt
- id: go-imports
files: \.go$
# Docker
- repo: https://github.com/hadolint/hadolint
rev: v2.12.0
hooks:
- id: hadolint-docker # (1)!
# Security (all languages)
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.1
hooks:
- id: gitleaks
- Hadolint lints Dockerfiles (best practices)
CI/CD integration (GitHub Actions):
# .github/workflows/pre-commit.yml
name: pre-commit
on:
pull_request:
push:
branches: [main]
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: pre-commit/action@v3.0.0 # (1)!
- Official pre-commit GitHub Action
Why this works:
- Catches issues before code review (saves time)
- Auto-fixes formatting (no manual formatting needed)
- Prevents secrets from entering repository
- Runs in parallel (fast execution)
- Language-agnostic (works with any language)
- CI integration ensures hooks run on all PRs
Best Practices
- Start simple - Add hooks gradually (don't overwhelm team)
- Auto-fix hooks first - Formatters before linters (less friction)
- Pin versions - Use
revtag for reproducibility - Run in CI - Enforce hooks on pull requests (GitHub Actions)
- Document skipping - Explain when
--no-verifyis acceptable - Update regularly -
pre-commit autoupdatemonthly - Test before merge - Run
--all-filesbefore committing config - Cache in CI - Use
pre-commit/actionwith caching
Performance
- First run slow - Builds environments (subsequent runs fast)
- Limit scope - Use
files:regex to target specific files - Parallel execution - Hooks run concurrently (fast by default)
- Skip heavy checks - Move expensive checks to pre-push or CI
- Local hooks - Use
repo: localfor system commands - Cache environments - pre-commit caches in
~/.cache/pre-commit
Advanced Usage
- Commit message hooks -
--hook-type commit-msg(conventional commits) - Pre-push hooks - Expensive checks (tests, builds) before push
- Multiple stages -
stages: [commit, push]for different hooks - Exclude files -
exclude: ^migrations/to skip directories - Language version -
language_version: python3.11for specific version - Pass filenames -
pass_filenames: falsefor repo-wide checks - Docker hooks - Run hooks in containers (isolation)
Common Gotchas
- Skipping hooks -
--no-verifybypasses all checks (dangerous in CI) - Version conflicts - Different hook versions between team members
- Slow hooks - Heavy linters block commits (move to CI)
- Auto-fix loops - Hook modifies files, triggers again (use
--fix) - Missing dependencies - Hooks need runtime (e.g., Node.js for ESLint)
- Merge conflicts -
.pre-commit-config.yamlconflicts common - Cache issues - Corrupt cache fixed with
pre-commit clean - Windows compatibility - Some hooks bash-specific (Docker alternative)
Popular Hook Collections
- pre-commit-hooks - Basic checks (whitespace, EOF, YAML, JSON)
- commitizen - Conventional commit messages
- detect-secrets - Yelp's secret scanner (alternative to Gitleaks)
- prettier - Multi-language formatter
- hadolint - Dockerfile linter
- terraform-docs - Auto-generate Terraform documentation
- ansible-lint - Ansible playbook linter
Hook Categories¶
Code Quality¶
Python: - Ruff (linter + formatter, fastest) - Black (formatter, opinionated) - isort (import sorting) - pylint (comprehensive linting) - mypy (static type checking)
JavaScript/TypeScript: - Biome (linter + formatter, Rust-based) - ESLint (linter, configurable) - Prettier (formatter, multi-language) - TypeScript compiler (tsc --noEmit)
Go: - gofmt (formatter, mandatory) - goimports (import management) - golangci-lint (meta-linter, 50+ linters) - go vet (built-in static analysis)
Security¶
- Gitleaks - Secret scanning (API keys, passwords, tokens)
- detect-secrets - Entropy-based secret detection
- Bandit - Python security linter
- Safety - Python dependency vulnerability scanner
- npm audit - JavaScript dependency scanner
- Trivy - Multi-purpose security scanner
File Checks¶
- trailing-whitespace - Remove trailing spaces
- end-of-file-fixer - Ensure newline at EOF
- check-yaml - Validate YAML syntax
- check-json - Validate JSON syntax
- check-added-large-files - Prevent large files (default: 500KB)
- check-merge-conflict - Detect merge conflict markers
- detect-private-key - Prevent committing SSH keys
Documentation¶
- markdownlint - Markdown linter
- terraform-docs - Auto-generate Terraform docs
- godoc - Go documentation checker
Learning Resources¶
Official Resources¶
- pre-commit Documentation - Official docs (comprehensive)
- Supported Hooks - Community hook registry
- Creating Hooks - Build custom hooks
- GitHub Action - Official CI integration
Example Configurations¶
Minimal Python Project
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
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.14
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
Full-Stack Project
repos:
# Universal
- 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: detect-private-key
# Backend (Python)
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.14
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
files: ^backend/
# Frontend (TypeScript)
- repo: https://github.com/biomejs/pre-commit
rev: v1.5.1
hooks:
- id: biome-check
args: [--apply]
files: ^frontend/
# Security
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.1
hooks:
- id: gitleaks
DevOps/Infrastructure
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
# Terraform
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.86.0
hooks:
- id: terraform_fmt
- id: terraform_validate
- id: terraform_docs
# Docker
- repo: https://github.com/hadolint/hadolint
rev: v2.12.0
hooks:
- id: hadolint-docker
# Kubernetes
- repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.5.4
hooks:
- id: insert-license
files: \.yaml$
Worth Checking¶
-
Official Docs
-
Popular Hooks
-
Alternative Tools
Husky (Node.js-specific)
Lefthook (Go-based alternative)
lint-staged (Run on staged files only)
-
Community
Last Updated: 2026-02-02 | Vibe Check: Essential - pre-commit prevents bad commits (formatting, secrets, syntax errors). Auto-fixes save time. Language-agnostic (works with Python, Go, Node.js, etc.). First run slow (environment setup), subsequent fast. Integrate with CI (GitHub Actions). Team adoption requires discipline (--no-verify tempting but dangerous). 200+ hooks available.
Tags: pre-commit, git-hooks, code-quality, automation, linting