A git hook is a script that git runs automatically when a specific event happens, such as committing, pushing, or receiving a push on the server. Hooks turn git from a passive record-keeper into something that can enforce rules: no commit without a passing lint check, no push of a branch that fails tests, no merge without a properly formatted commit message. They have existed in git since the beginning, but most developers only meet them once a team adopts a shared tool to manage them.
What changed in 2026
- Shared hook tooling is now closer to a default than an add-on. Projects that once relied on developers remembering to run checks manually now wire hooks into the setup on day one, especially JavaScript projects using Husky or lint-staged and Python projects using the pre-commit framework.
- Hooks increasingly call out to the same checks CI runs, just scoped to changed files, so a failure is caught in seconds locally instead of minutes later in a pipeline.
- Secret-scanning hooks became common as a default line of defense, catching an accidentally staged API key before it is committed rather than after it is already in history.
The two kinds of git hook
The distinction that actually matters is where the hook runs.
|
Client-side |
Server-side |
| Runs on |
The developer machine |
The git server (e.g., a bare repo, self-hosted git) |
| Can be skipped |
Yes, with --no-verify or by editing the hook |
No, not by the person pushing |
| Common hooks |
pre-commit, commit-msg, pre-push |
pre-receive, update, post-receive |
| Best for |
Fast feedback: lint, format, quick tests |
Hard rules that must never be bypassed: branch protection, commit signing |
Most platforms like GitHub and GitLab do not expose raw server-side git hooks to you directly. You get their equivalent instead: branch protection rules, required status checks, and merge rules, which do the same enforcing job without you managing a script on a server.
The most useful hook points
- pre-commit — runs before a commit is created; good for linting and formatting only the staged files.
- commit-msg — runs after you write a commit message; good for enforcing a message format like Conventional Commits.
- pre-push — runs before a push leaves your machine; good for running the fast part of the test suite, the same tests that should already follow solid unit testing practices.
- post-checkout — runs after switching branches; good for reminding you to reinstall dependencies when the lockfile changed.
Setting one up
A raw hook is just an executable file dropped in .git/hooks/pre-commit with no extension. That works for one person on one machine, but .git/hooks is not committed, so nothing about it is shared with the team. In practice almost everyone uses a wrapper: Husky for JavaScript projects, or the pre-commit framework for polyglot projects, both of which commit a config file that installs real hooks into .git/hooks when a teammate runs the setup step.
Common pitfalls
- Slow hooks get disabled. A pre-commit hook that takes real time gets bypassed with --no-verify the first time someone is in a hurry, and then quietly forgotten about.
- Treating a client-side hook as a security control. Anyone can skip it, delete it, or clone the repo without it. Anything that must be enforced belongs on the server, in CI, or in branch protection.
- Hooks that behave differently per machine. A hook that depends on a globally installed tool works on the author machine and fails for everyone else; pin the tool version the same way you pin dependencies.
FAQ
Can a git hook be bypassed?
A client-side hook can always be bypassed by the person on that machine, usually with --no-verify. A server-side hook, or its platform equivalent like a required status check, cannot be bypassed by the person pushing.
Are git hooks the same as CI?
No. A hook runs locally, before code ever reaches a shared branch, and gives feedback in seconds. CI runs remotely, after a push, on infrastructure everyone trusts equally. Good setups use both: hooks for fast local feedback, CI as the real gate.
Do git hooks slow down every commit?
Only if you let them do too much. A well-scoped pre-commit hook checks only staged files and finishes in a second or two. Anything slower belongs in a pre-push hook or in CI instead.
What is the easiest way to share hooks across a team?
Use a wrapper tool like Husky or the pre-commit framework rather than raw .git/hooks scripts. Both commit a config file to the repository so every teammate gets the same hooks after one setup command.
Where to go next