ScriptsApr 14, 2026·3 min read

pre-commit — A Framework for Managing Git Hook Scripts

pre-commit manages and installs multi-language Git hooks from a YAML file. It runs linters, formatters, and checks before commits reach CI — catching issues early with zero manual setup per developer.

Introduction

pre-commit solves the "every developer has a slightly different setup" problem. You commit a .pre-commit-config.yaml to the repo, each developer runs pre-commit install once, and from then on every commit runs the same linters and formatters — in isolated environments, automatically installed per hook.

With over 12,000 GitHub stars, pre-commit is the standard Git hook manager across Python, JavaScript, Go, Rust, and shell projects.

What pre-commit Does

pre-commit installs itself as .git/hooks/pre-commit. On every commit it reads your config, creates virtualenvs / node_modules / go modules for each hook (cached after first run), and executes them against the staged files. Any failure blocks the commit.

Architecture Overview

git commit
    |
[pre-commit hook]
    |
[Read .pre-commit-config.yaml]
    |
  For each repo:
    clone at `rev`
    create isolated env (venv / npm / go / docker)
    run hook ids against staged files
    |
  Any failure -> commit aborted
    |
  All pass    -> commit proceeds

Self-Hosting & Configuration

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.6.0
    hooks:
      - id: check-added-large-files
      - id: check-merge-conflict
      - id: check-json

  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.6.9
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format

  - repo: https://github.com/pre-commit/mirrors-prettier
    rev: v3.1.0
    hooks:
      - id: prettier
        types_or: [javascript, ts, tsx, json, yaml, markdown]

  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks

  - repo: local
    hooks:
      - id: pytest-fast
        name: pytest (fast)
        entry: pytest -q -x tests/unit
        language: system
        pass_filenames: false
        stages: [pre-push]
pre-commit autoupdate     # bump pinned versions
pre-commit run ruff --all-files
SKIP=ruff git commit -m "wip"

Key Features

  • Multi-language hooks — Python, Node, Go, Rust, Ruby, Docker, system
  • Isolated environments — no global pollution, cached for speed
  • Run only on changed files — fast feedback for big repos
  • Version pinning — hook versions tracked in config; autoupdate to bump
  • CI integrationpre-commit run --all-files in GitHub Actions
  • Stages — pre-commit, pre-push, commit-msg, pre-merge-commit
  • pre-commit.ci — optional SaaS that auto-fixes PRs and updates hooks
  • SkippingSKIP=hook_id env var for emergency commits

Comparison with Similar Tools

Feature pre-commit Husky Lefthook Overcommit lint-staged
Languages Any Node-focused Any Ruby-focused Node
Config format YAML package.json/shell YAML YAML package.json
Isolated envs Yes No No No No
Hook catalog Huge Medium Small Medium N/A
Best For Polyglot repos Node projects Fast hooks Ruby projects JS file-based

FAQ

Q: Does pre-commit require Python? A: Yes, pre-commit itself is Python. But it can run hooks in any language (Node, Go, etc.) via its env-management.

Q: Will pre-commit slow down commits? A: First install is slow (env creation), subsequent commits are fast — only staged files are linted, envs are cached.

Q: What if a dev forgets to install the hook? A: Run pre-commit run --all-files in CI as a required check — even if local hooks are skipped, the PR still fails.

Q: Can hooks auto-fix and re-commit? A: Hooks can modify files (like ruff-format). pre-commit then aborts the commit so you can review + re-stage the fixes.

Sources

Discussion

Sign in to join the discussion.
No comments yet. Be the first to share your thoughts.

Related Assets