Python Git Hooks Setup
Configure git pre-commit hooks using the pre-commit framework to enforce code quality before commits.
Objectives
- •Install pre-commit framework with Python quality hooks
- •Migrate any existing manual hooks to pre-commit framework
- •Configure Claude Code PostToolUse hooks for faster feedback
- •Ensure hooks run incrementally on changed files only
- •Auto-fix issues where possible, block on critical errors
Required Tools
Add to [dependency-groups] dev: "pre-commit", "ruff", "mypy", "basedpyright"
- •pre-commit: Hook management framework (required)
- •ruff: Fast linter with auto-fix capability
- •mypy: Standard Python type checker
- •basedpyright: Enhanced type analysis
Setup Workflow
Step 1: Check for Existing Hooks
# Check if manual hooks exist ls -la .git/hooks/pre-commit 2>/dev/null # If exists and not a pre-commit managed hook, migrate it (see Migration section)
Step 2: Create Pre-commit Configuration
Create .pre-commit-config.yaml:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.0
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.13.0
hooks:
- id: mypy
additional_dependencies: [] # Add type stubs as needed
- repo: local
hooks:
- id: basedpyright
name: basedpyright
entry: basedpyright
language: system
types: [python]
pass_filenames: true
Step 3: Install Hooks
# Install pre-commit to project venv uv pip install pre-commit # Install git hooks pre-commit install # Verify installation ls -la .git/hooks/pre-commit
Step 4: Test Hooks
# Run on all files (initial validation) pre-commit run --all-files # Or test on staged files only git add some_file.py pre-commit run
Step 5: Configure Claude Code Hooks (Recommended)
Strongly recommended: Configure Claude Code PostToolUse hooks to run quality checks after every Edit, providing immediate feedback during development.
Check if hooks are configured:
cat ~/.claude/settings.json | grep -A 20 '"hooks"'
If not configured, add to ~/.claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "cd \"$PROJECT_DIR\" && source .venv/bin/activate 2>/dev/null && ruff check \"$FILE_PATH\""
}
]
}
]
}
}
IMPORTANT: Check only, never auto-fix in PostToolUse hooks. Using --fix or ruff format in PostToolUse hooks silently modifies files between edits. When adding an import and its usage in two separate edits, --fix removes the "unused" import after the first edit, causing the second edit to fail with an undefined name. This creates a chicken-and-egg problem that wastes many retries. Auto-fixing belongs exclusively in the pre-commit hook (Step 2), where all edits are complete.
Benefits of Claude Code hooks:
- •Immediate feedback on every edit (faster than waiting for commit)
- •Catches issues before they accumulate
- •Does not modify files — avoids interference with multi-edit workflows
Migration from Manual Hooks
If the project has an existing manual .git/hooks/pre-commit script:
Step 1: Backup Existing Hook
cp .git/hooks/pre-commit .git/hooks/pre-commit.backup
Step 2: Analyze Existing Hook
Read the existing hook to understand what checks it runs:
- •Linting (ruff, flake8, pylint)?
- •Type checking (mypy, pyright)?
- •Formatting (black, isort)?
- •Custom checks?
Step 3: Map to Pre-commit Config
For each check in the manual hook, add equivalent to .pre-commit-config.yaml:
| Manual Hook Check | Pre-commit Equivalent |
|---|---|
ruff check | ruff-pre-commit repo |
black | ruff-format (ruff replaces black) |
isort | ruff with isort rules (I) |
flake8 | ruff (replaces flake8) |
mypy | mirrors-mypy repo |
basedpyright | local hook |
| Custom script | local hook with entry: ./script.sh |
Step 4: Install Pre-commit and Remove Manual Hook
pre-commit install # This overwrites .git/hooks/pre-commit pre-commit run --all-files # Verify all checks pass
Step 5: Verify and Clean Up
# Test that hooks work git add . git commit -m "test" --dry-run # If successful, remove backup rm .git/hooks/pre-commit.backup
Hook Behavior
Auto-fixable issues (ruff, pre-commit only):
- •Import sorting
- •Trailing whitespace
- •Simple style violations
- •Many code quality issues
Note: --fix runs only in pre-commit hooks, never in PostToolUse hooks.
Blocking issues:
- •Type errors (mypy/basedpyright)
- •Syntax errors
- •Complex linting violations that can't be auto-fixed
Bypass (use sparingly):
git commit --no-verify
Performance Optimization
For large codebases:
- •Pre-commit runs on staged files only by default
- •Use mypy incremental mode (cache in
.mypy_cache) - •Consider running mypy/basedpyright in CI only for speed
For monorepos:
- •Use
files:pattern in hook config to limit scope - •Configure separate hooks per package
- id: mypy files: ^src/mypackage/
Troubleshooting
Hook not running:
pre-commit install --force # Reinstall hooks
Tools not found:
# Ensure tools installed in venv uv pip install ruff mypy basedpyright
Hook too slow:
- •Profile:
pre-commit run --verbose - •Consider removing mypy/basedpyright from pre-commit, run in CI instead
- •Use
stages: [manual]for slow hooks, run explicitly
Update hook versions:
pre-commit autoupdate
Verification Checklist
- •
.pre-commit-config.yamlexists with ruff, mypy, basedpyright hooks - •
pre-commit installhas been run - •
pre-commit run --all-filespasses - • Any existing manual hooks have been migrated
- • Claude Code PostToolUse hooks configured (recommended)
- • Bypass with
--no-verifyworks for emergencies
Examples
Example: New project setup
1. Create venv: uv venv && source .venv/bin/activate 2. Install tools: uv pip install pre-commit ruff mypy basedpyright 3. Create .pre-commit-config.yaml with ruff, mypy, basedpyright 4. Install hooks: pre-commit install 5. Test: pre-commit run --all-files 6. Configure Claude Code PostToolUse hooks for real-time feedback
Example: Migrate existing manual hook
1. Backup: cp .git/hooks/pre-commit .git/hooks/pre-commit.backup 2. Read backup to identify checks (ruff, mypy, custom scripts) 3. Create .pre-commit-config.yaml mapping each check 4. Install: pre-commit install (overwrites manual hook) 5. Verify: pre-commit run --all-files 6. Clean up: rm .git/hooks/pre-commit.backup
Example: Add to existing pre-commit config
1. Edit .pre-commit-config.yaml 2. Add basedpyright local hook 3. Run: pre-commit run --all-files 4. Commit updated config
Related Skills
- •Prerequisites: py-quality-setup (tools must be configured before adding hooks)
- •Complements: py-security (add bandit to pre-commit for security scanning)
- •See also: All skills benefit from automated enforcement via hooks