Chapter 50: Git Hooks
Git Hooks
These are small scripts that Git automatically runs at very specific moments in your workflow.
Think of them as Git’s personal assistants that whisper in your ear (or shout at you) right before or right after important actions:
- “Hey, you are about to commit — let me check something first…”
- “You just committed — let me run tests / format code / send notification…”
- “You are about to push — let me make sure you didn’t forget something…”
Hooks are not magic — they are just normal executable scripts (shell, Python, Node.js, whatever you want) that live inside your .git/ folder.
Once you understand hooks, you can automate almost any repetitive quality/safety step and make your team’s life much easier (and your own commits much cleaner).
1. Two Main Types of Hooks (very important distinction)
| Type | Folder location | Runs on whose machine? | Most common use cases | Can be bypassed? |
|---|---|---|---|---|
| Local hooks | .git/hooks/ | Your laptop only | Personal pre-commit checks, linting, formatting | Yes — very easy (–no-verify) |
| Server-side hooks | Remote repo (GitHub/GitLab server) | The server (GitHub, GitLab, Bitbucket…) | Enforce rules for everyone (no secrets, signed commits, branch naming) | No (unless you have admin rights) |
In 2026, most developers start with local hooks, then teams move important checks to server-side hooks (GitHub Actions / GitLab CI / pre-receive hooks).
2. Most Useful Local Hooks (you will use these daily)
Git comes with sample hooks in .git/hooks/ — they end with .sample. Remove .sample to activate them.
| Hook name | When it runs | Most common real use in 2026 | Typical script language |
|---|---|---|---|
| pre-commit | Right before git commit | Run linter, prettier, tests, block secrets, block large files | bash, node, python |
| prepare-commit-msg | Before commit message editor opens | Auto-add ticket ID / branch name to message | bash |
| commit-msg | After message written, before commit finalizes | Enforce Conventional Commits, block bad words | bash, node |
| post-commit | Right after successful commit | Send notification, update changelog draft | bash |
| pre-push | Right before git push | Run full test suite, block push to main without PR | bash, node |
3. Realistic Example – Create Useful Local Hooks Right Now
Step 1 – Initialize repo & enable hooks
|
0 1 2 3 4 5 6 7 8 |
mkdir git-hooks-demo cd git-hooks-demo git init |
Step 2 – Create pre-commit hook (most popular one)
|
0 1 2 3 4 5 6 7 8 9 10 11 |
# Go to hooks folder cd .git/hooks # Create executable script touch pre-commit chmod +x pre-commit |
Open pre-commit in VS Code / nano:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#!/usr/bin/env bash set -euo pipefail echo "🔍 Running pre-commit checks..." # 1. Run prettier (if you have it) if command -v npx >/dev/null 2>&1; then echo "→ Prettier check..." npx prettier --check . fi # 2. Simple secret scanner (very basic example) echo "→ Checking for secrets..." if grep -r -E "(password|api_key|secret|token)[=: ]" . --exclude-dir={.git,node_modules,.husky}; then echo "❌ Possible secret found! Commit aborted." exit 1 fi # 3. Run tests (if you have them) if [ -f package.json ] && grep -q '"test":' package.json; then echo "→ Running tests..." npm test || { echo "❌ Tests failed!"; exit 1; } fi echo "✅ All pre-commit checks passed!" |
Step 3 – Test it
Create a file with fake secret:
|
0 1 2 3 4 5 6 7 8 |
echo "api_key=supersecret123" > config.txt git add config.txt git commit -m "Add config" |
→ Hook runs → finds secret → aborts commit!
Remove bad line → try again → commit succeeds.
Step 4 – Make it permanent (don’t put hooks in .git/)
Problem: .git/hooks/ is not tracked — if you delete repo or clone fresh → hooks disappear.
Modern solution (2026 standard) — use Husky or lefthook (they manage hooks in tracked files)
Install Husky (Node.js projects):
|
0 1 2 3 4 5 6 7 |
npm install husky --save-dev npx husky init |
Now hooks live in .husky/pre-commit — committed to repo — everyone gets them.
4. Very Common Real-World .git/hooks / Husky Examples (2026)
pre-commit (most used)
- Run ESLint + Prettier
- Run unit tests
- Scan for secrets (gitleaks / truffleHog)
- Check file size (find . -size +10M → block huge files)
pre-push
- Run full integration/e2e test suite
- Block direct push to main / develop
|
0 1 2 3 4 5 6 7 8 9 10 |
# .husky/pre-push example if [[ $(git rev-parse --abbrev-ref HEAD) == "main" ]]; then echo "❌ Direct push to main is forbidden! Use Pull Request." exit 1 fi |
commit-msg — enforce Conventional Commits
|
0 1 2 3 4 5 6 7 8 9 10 11 |
# .husky/commit-msg msg=$(cat $1) if ! echo "$msg" | grep -Eq "^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .+"; then echo "❌ Commit message does not follow Conventional Commits!" exit 1 fi |
5. Server-side Hooks (when local is not enough)
Local hooks can be bypassed (git commit –no-verify) — so serious teams enforce rules on server.
GitHub / GitLab options in 2026:
- GitHub → Repository rulesets + required status checks (Actions)
- GitLab → Server-side pre-receive hooks or push rules
- pre-receive / update / post-receive scripts (self-hosted Git servers)
Example: GitHub branch protection rule + required passing Actions
→ No one can push to main without passing tests + signed commits.
6. Quick Git Hooks Cheat Sheet (2026)
| Hook name | When it runs | Can be bypassed? | Typical modern location (tracked) | Most used checks |
|---|---|---|---|---|
| pre-commit | Before commit | Yes | .husky/pre-commit | lint, test, secrets, formatting |
| commit-msg | After message written | Yes | .husky/commit-msg | Conventional Commits, ticket ID |
| pre-push | Before push | Yes | .husky/pre-push | block direct push to main, full tests |
| pre-receive (server) | On server before accepting push | No | GitLab / self-hosted | Enforce branch naming, no force-push to main |
Got the hooks feeling now?
Git hooks = “little scripts that run automatically at key moments — your personal quality/safety police”
They turn Git from “just version control” into enforced development standards.
Next?
- Want to set up Husky + pre-commit + commit-msg together?
- See how to block secrets with gitleaks hook?
- Or do one final big summary of all Git concepts we covered today?
Just tell me — we’ll finish strong. You’ve now gone from zero to very advanced Git — truly impressive work! 🚀
