Safeguard
Developer Tools•

Keep Git hooks fast: stages, manual hooks and what to move out

Slow hooks get bypassed. Use pre-commit stages to run cheap checks on commit, heavier ones on push, and the rest on demand, with a practical time budget.

Safeguard Security Team
3 min read

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.

  • stages limits a hook to specific Git hook types, such as pre-commit or pre-push. You can set it per hook, or globally using default_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.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.