TL;DR

A developer walked through building a Rust pre-commit hook that checks formatting and found multiple failure modes: hooks that run against the working tree miss staged edits, hooks run during rebases and merges and can break unrelated branches, and many hooks are slow or brittle. The author argues pre-commit hooks are a bad pattern and recommends using pre-push hooks with strict constraints instead.

What happened

The author created a tiny Rust project and added a pre-commit hook that ran rustfmt over *.rs in the working directory. That initial hook missed the case where rustfmt changed files but those edits were not staged, so the repository still contained unformatted code even after the check passed. To fix that, the author copied the indexed files out of Git with git checkout-index into a temporary directory and ran rustfmt checks there, then refined the script to check only staged files by enumerating cached file names. However, interactive rebases and other operations exposed further problems: hooks ran while rebasing other branches and failed when no Rust files were present, causing rebase errors. The author concludes that pre-commit hooks interact poorly with common workflows (rebases, merges, stash-like workflows) and with other developers’ branches, and recommends avoiding pre-commit hooks in favor of pre-push hooks. The post finishes with practical rules for writing safer pre-push hooks.

Why it matters

  • Pre-commit hooks that operate on the working tree can miss staged changes, giving a false sense of safety.
  • Hooks run automatically during rebases or merge conflict resolution and can break common workflows or third-party branches.
  • Slow, networked, or credentialed checks are unsuitable for hooks and will make everyday git operations fragile.
  • Widespread use of broken hooks can force developers to bypass checks (git –no-verify), undermining the intended protections.

Key facts

  • Initial example: a pre-commit hook ran rustfmt on *.rs in the working directory and did not catch unstaged formatting changes.
  • A more robust approach used git checkout-index –prefix to run checks against a copy of the index rather than the worktree.
  • The script was later modified to inspect only staged files using git diff –name-only –cached –no-ext-diff –diff-filter=d.
  • Pre-commit hooks run during interactive rebases and when resolving merge conflicts, which can cause failures on commits from other branches.
  • Because hooks can run on commits that developers don't control, they may force users to employ –no-verify frequently.
  • Author recommends using pre-push hooks instead, with several constraints to avoid the pitfalls of pre-commit hooks.
  • Advice for hooks: run against the index, keep checks fast and reliable, avoid network or credential requirements, and be quiet in output.
  • The author warns against automatically installing hooks; instead document manual setup in CONTRIBUTING material.

What to watch next

  • Consider replacing pre-commit hooks with pre-push hooks to minimize interference with local commits (author recommendation).
  • If you keep hooks, ensure they run against the index and only include fast, non-network checks.
  • not confirmed in the source

Quick glossary

  • pre-commit hook: A Git hook that runs locally before a commit is finalized, typically used to run linters or formatting checks.
  • pre-push hook: A Git hook that runs before a push to a remote, useful for checks that are safe to perform less frequently or that require repository context.
  • index (staging area): Git's staging area where changes are listed to be included in the next commit; distinct from the working directory.
  • git checkout-index: A Git command that writes files from the index to the filesystem, which can be used to inspect staged content without relying on the worktree.
  • rebase (interactive): A Git operation that replays commits on top of another base commit; interactive rebase allows editing commits and can trigger hooks.

Reader FAQ

Should I use pre-commit hooks?
The author argues against pre-commit hooks because they are brittle and interfere with workflows; they recommend pre-push hooks instead.

Can pre-commit hooks check staged files reliably?
You can check the index by copying staged files out with git checkout-index, but the process is fiddly and still has edge cases.

What makes a hook appropriate?
Keep it fast, deterministic, run it against the index (not the worktree), avoid network or credential requirements, and be quiet in output.

Do hooks run during rebase or merge?
Yes — interactive rebases and merge conflict resolution can trigger hooks, which can cause failures when handling other branches or commits.

pre-commit hooks are fundamentally broken 2025-12-26 • git • workflows • devtools Let's start a new Rust project. $ mkdir best-fizzbuzz-ever $ cd best-fizzbuzz-ever $ cat main.rs fn main() {…

Sources

Related posts

By

Leave a Reply

Your email address will not be published. Required fields are marked *