A hook developers wait on will soon be skipped. Keep pre-commit under a few seconds, move heavier checks to pre-push or a manual stage, and leave the full set to CI. The pre-commit framework supports all three with a stages setting on each hook.
Why speed is a security issue
The githooks documentation notes pre-commit can be bypassed with the no-verify option. Nobody reaches for that on a one second hook. They reach for it when a commit takes thirty seconds and they are in the middle of something.
So a time budget is part of the control design. Pick a number your team accepts, such as three seconds, and move anything above it.
The three stages
The pre-commit documentation describes how stages work.
stageslimits a hook to specific Git hook types, such as pre-commit or pre-push. You can set it per hook, or globally usingdefault_stages.stages: [manual]makes a hook run only on demand.- A hook type must be installed to fire. Install the pre-push type with the command in the block below.
default_stages: [pre-commit]
repos:
- repo: local
hooks:
- id: unit-tests
name: unit tests
entry: make test
language: system
pass_filenames: false
stages: [pre-push]
- id: full-scan
name: full scan
entry: make scan
language: system
pass_filenames: false
stages: [manual]
Install and run them like this:
pre-commit install --hook-type pre-push
pre-commit run --hook-stage manual full-scan
What to put where
- On commit: formatting, key detection, size checks, lint on changed files.
- On push: unit tests and quick static checks, where waiting a few seconds is accepted.
- Manual: full repository scans or anything that needs network or credentials.
- CI only: the slow, required, auditable checks.
Run on changed files only
Most hooks run on staged files by default. Beware of settings that force whole repo runs on every commit, such as turning off filename passing, unless the tool is quick enough.
Measure it
Run all hooks once over every file and time each one, which the output shows. Anything unusually slow is a candidate to move. Check again after upgrades.
A policy sentence
"Hooks give fast feedback. CI is the control." It sets expectations and answers the question of why the CI check duplicates the hook. For the reasoning see why local hooks need a CI backstop.
A free scan of your dependencies is available at gold.safeguard.sh/scan.
A worked time budget
Suppose your commit hooks take a total of eight seconds. List each hook with its time. Formatting might take two, key detection one, a linter four, and a type check one. The linter is the obvious candidate, so restrict it to changed files or move it to pre-push.
After the change, time it again and tell the team. People forgive a hook that is fast and useful. They do not forgive one that is slow and mostly right.
Quick check
How do I run only the slow hooks?
Give them the manual stage in the config and run them by id with the hook stage option, or run them in CI.
Sources
Last reviewed: 19 October 2026.