Skip to content

Instantly share code, notes, and snippets.

@MangaD
Created June 1, 2025 10:05
Show Gist options
  • Save MangaD/6a85ee73dd19c833270524269159ed6e to your computer and use it in GitHub Desktop.
Save MangaD/6a85ee73dd19c833270524269159ed6e to your computer and use it in GitHub Desktop.
The Comprehensive Guide to `pre-commit`

The Comprehensive Guide to pre-commit

Table of Contents

  1. Introduction: What is pre-commit?
  2. Why Use pre-commit?
  3. How pre-commit Works
  4. Installing and Setting Up pre-commit
  5. Configuring .pre-commit-config.yaml
  6. Built-in and Community Hooks
  7. How to Run and Use pre-commit
  8. Popular Use Cases
  9. Writing Custom Hooks
  10. Advanced Configuration and Features
  11. Integrating with CI/CD
  12. Best Practices
  13. Troubleshooting and Common Issues
  14. Alternatives and Related Tools
  15. Resources

1. Introduction: What is pre-commit?

pre-commit is an open-source framework for managing and maintaining multi-language pre-commit hooks. In software development, Git hooks are scripts that run automatically on specific Git events (e.g., commit, push). pre-commit makes it easy to install and run hooks for code quality, security, style, and more—before code gets committed.

It is language-agnostic and supports everything from Python, JavaScript, Go, and Shell scripts, to Rust, Ruby, and more.


2. Why Use pre-commit?

  • Automate Code Quality: Catch formatting errors, linting issues, and security flaws before they reach your repo.
  • Consistency Across Teams: Ensures everyone runs the same checks, reducing “works on my machine” problems.
  • Developer Productivity: Automates repetitive checks, freeing up dev time.
  • Multi-language Support: Works across codebases with multiple languages and ecosystems.
  • Easy CI/CD Integration: Hooks can be run manually or in CI to ensure consistency.

3. How pre-commit Works

  • You define a list of hooks (scripts) in a .pre-commit-config.yaml file.
  • pre-commit installs the required environments for each hook (virtualenvs, Node, etc.).
  • When you run git commit, pre-commit runs all the enabled hooks on the files you’re committing.
  • If a hook fails, the commit is blocked.

4. Installing and Setting Up pre-commit

Installation

  • Python users:

    pip install pre-commit
  • Homebrew (macOS/Linux):

    brew install pre-commit
  • Other: Install via conda, pipx, or download from GitHub.

Setting Up

  1. Add to your project:

    pre-commit sample-config > .pre-commit-config.yaml
  2. Install the Git hook scripts:

    pre-commit install

    This sets up .git/hooks/pre-commit to run pre-commit on every commit.

  3. Run on all files (initial run):

    pre-commit run --all-files

5. Configuring .pre-commit-config.yaml

This YAML file tells pre-commit which hooks to use and how to run them.

Example

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0  # Use the latest stable tag
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml

  - repo: https://github.com/psf/black
    rev: 24.3.0
    hooks:
      - id: black

Key Fields:

  • repo: URL or path to the repo with hooks
  • rev: Version tag or commit hash
  • hooks: List of hook definitions (id, args, files, etc.)

Hook Options

  • id: The hook’s unique name.
  • args: Custom arguments for the hook.
  • files: Regex for files to include.
  • exclude: Regex for files to exclude.
  • language_version: Set the language version (e.g., python3.11).

6. Built-in and Community Hooks

There’s a huge list of pre-commit hooks:


7. How to Run and Use pre-commit

  • On every commit: Automatically runs via the installed hook.

  • Manually on staged files:

    pre-commit run
  • On all files:

    pre-commit run --all-files
  • On a specific hook:

    pre-commit run black --all-files
  • Update all hooks:

    pre-commit autoupdate

8. Popular Use Cases

  • Enforce code style (formatters, linters)
  • Check for secrets (AWS keys, passwords)
  • Fix whitespace, newlines, EOF issues
  • Enforce commit message format
  • Check for large files or merge conflicts
  • Security vulnerability checks

9. Writing Custom Hooks

You can write your own hooks in any language.

Local Hook Example (in .pre-commit-config.yaml):

repos:
  - repo: local
    hooks:
      - id: my-linter
        name: My Custom Linter
        entry: ./scripts/my_linter.sh
        language: script
        files: \.py$

Local Python Hook Example

      - id: check-todo
        name: Check for TODOs
        entry: python scripts/check_todo.py
        language: python
        files: .*

Hook script must accept a list of filenames as arguments. Return 0 for pass, non-zero for fail.


10. Advanced Configuration and Features

  • Staged-only or all files: Use always_run, pass_filenames, and stages options.
  • Multiple stages: Use stages: [commit, push, manual].
  • Parallel execution: By default, hooks run in parallel.
  • Failing gracefully: Hooks can be set as required or optional.
  • Excluding files: Use the exclude regex for large/irrelevant files.
  • Languages: Hooks support many languages (python, node, golang, docker, system, etc.).
  • Hook ordering: Hooks run in the order they’re listed.

11. Integrating with CI/CD

  • Why: Ensures code quality checks run in CI, not just on developer machines.

  • How:

    • In your CI job, install pre-commit and run:

      pre-commit run --all-files --show-diff-on-failure
    • For some CIs (e.g., GitHub Actions), there are pre-built actions: pre-commit/action

  • Fail the build if hooks fail.


12. Best Practices

  • Pin hook versions: Always use specific rev values, never master/main.

  • Run pre-commit run --all-files after adding new hooks.

  • Add .pre-commit-config.yaml to source control.

  • Document setup in CONTRIBUTING.md.

  • Keep hooks fast—slow hooks slow down developer flow.

  • Autoupdate hooks regularly:

    pre-commit autoupdate
  • Review hook changes on update (some hooks change behavior across versions).


13. Troubleshooting and Common Issues

  • Hook not running? Did you run pre-commit install?

  • Hook fails but works manually? Check environment—pre-commit uses isolated environments for each hook.

  • Can’t find a hook or dependency? Sometimes you need to manually install system packages (e.g., libyaml-dev).

  • Files modified by hooks? By default, if a hook modifies files (e.g., a formatter), you must git add them and recommit.

  • Want to skip pre-commit just once?

    git commit --no-verify
  • Speed up repeated runs: Use pre-commit run --files ... or only staged files.

  • Cross-platform issues: Some hooks may have trouble on Windows—test in your team’s environments.


14. Alternatives and Related Tools

  • Husky (JavaScript-centric, popular in frontend projects)
  • lefthook (Go, cross-platform, fast)
  • Overcommit (Ruby)
  • git hooks (native, but more manual)

pre-commit is the most widely used and language-agnostic solution.


15. Resources


TL;DR / Executive Summary

  • pre-commit is a framework for managing and running Git hooks.
  • It is configured via .pre-commit-config.yaml and supports many languages.
  • You install it, configure hooks, and let it run checks/formatters automatically on commit.
  • It boosts code quality, developer experience, and consistency.

Have a specific use case or problem with pre-commit?

Let me know—I'm happy to go even deeper into any area, write advanced examples, or help troubleshoot!

@MangaD
Copy link
Author

MangaD commented Jul 7, 2025

@MangaD
Copy link
Author

MangaD commented Jul 8, 2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment