Local Git hooks are a convenience, not a control. The git documentation says the pre-commit hook can be bypassed with the no-verify option, so any check that must always run belongs on the server side, in CI or a receiving hook, where a developer cannot skip it.
What the no-verify flag skips
The githooks page states that pre-commit "can be bypassed with the" no-verify option on the commit command. The same flag also skips the pre-merge-commit and commit-msg hooks. The prepare-commit-msg hook is not suppressed by it.
The page does not say whether the flag affects pre-push, so do not assume either way. Test it on your own Git version if you rely on that.
There is a second way hooks never run: the developer simply never installed them. A hook in a local checkout only exists after someone sets it up.
Why this matters for security checks
Hooks that catch a leaked key or a banned file type are useful. But a rushed developer on a Friday can pass the flag and move on, and nobody is told. If your only secret check is a local hook, your real policy is "most people run it most of the time".
That is fine for style rules. It is not fine for anything you would explain to an auditor as a control.
Where to enforce instead
Put the same checks in two places.
- Locally, so developers get feedback in seconds, before a commit exists.
- In CI on every pull request, as a required status check, so the merge button stays blocked until it passes.
Git also has a receiving side. The pre-receive hook runs when the server handles a push, and if it exits non-zero "none of the refs will be updated". Hosted platforms usually expose this idea as push rules or push protection rather than letting you run your own script.
A simple way to keep both in sync is to run the same config in both places. With the pre-commit framework, the run all files command is documented as usable in CI, so the local hook and the CI job read one file.
# CI step, same config the developers use
- run: pre-commit run --all-files
A short rule of thumb
Ask of each check: if someone skips it, do we want to know? If yes, it needs a server side copy. If no, a local hook is enough.
Do not punish the flag itself. Sometimes a hook is broken or a commit is a throwaway on a private branch. The point is that the merge path does not depend on the honesty of the commit step.
When to still run hooks locally
Local hooks shorten the loop. A failed CI job costs minutes and a context switch. A failed hook costs seconds. Keep them for fast checks and let CI hold the line.
Safeguard can scan dependencies and SBOMs in CI and apply policies and gates to pull requests. A free scan is available at gold.safeguard.sh/scan.
Quick check
Does the no-verify flag skip every hook?
No. The githooks page says it bypasses pre-commit, pre-merge-commit and commit-msg, and that prepare-commit-msg is not suppressed. It does not state the effect on pre-push, so verify that yourself.
Is a pre-receive hook a good place for checks?
It runs on the receiving side and a non-zero exit rejects the push, so it cannot be skipped by the pusher. On hosted platforms you usually get the same effect through push rules or required status checks.
Sources
Last reviewed: 19 October 2026.