Git Hooks
Introduction
Git hooks are scripts Git runs at events like commit or push—format code, run tests, or block bad messages before they enter history. Hooks live in .git/hooks/ and are local by default; teams often share them via husky in Node projects. This chapter shows sample hooks and front-end tooling patterns without requiring you to install everything.
Prerequisites
- Your First Commit Workflow
- Commit and PR Conventions
- Optional: Node project for husky examples
Hook Locations
After git init:
.git/hooks/
├── pre-commit.sample
├── commit-msg.sample
└── ...Active hook = file without .sample extension, executable bit set.
Code explanation:
- Hooks are not cloned to others—each developer enables locally unless shared via tooling
- Never commit secrets into hook scripts
pre-commit Example
Block commit if tests fail (simple shell):
#!/bin/sh
# Run unit tests before allowing commit
npm testSave as .git/hooks/pre-commit, then:
chmod +x .git/hooks/pre-commitFailed npm test exit code aborts commit.
Warning
Keep Hooks Fast
Slow hooks get skipped with --no-verify—run heavy checks in CI instead.
commit-msg Example
Enforce non-empty first line:
#!/bin/sh
# Reject empty commit message
commit_msg_file=$1
first_line=$(head -n1 "$commit_msg_file")
if [ -z "$first_line" ]; then
echo "Commit message cannot be empty"
exit 1
fiSave as .git/hooks/commit-msg, chmod +x.
Conventional Commits validation is common in shared hook libraries.
Skip Hooks (Emergency Only)
git commit --no-verify -m "Emergency hotfix"Team policy may forbid except incidents—CI still catches issues.
husky and lint-staged (Node Projects)
Share hooks via npm in repo root:
{
"devDependencies": {
"husky": "^9.0.0",
"lint-staged": "^15.0.0"
},
"scripts": {
"prepare": "husky"
}
}.husky/pre-commit:
npx lint-stagedlint-staged config (in package.json or separate file):
{
"lint-staged": {
"*.{js,ts}": ["eslint --fix", "prettier --write"]
}
}Code explanation:
prepareinstalls husky afternpm install- Only staged files formatted—fast commits
- Fits JavaScript and TypeScript apps
Block Secrets in Commits
Pre-commit scanners (concept):
- gitleaks, detect-secrets scan staged diff
- Reject if
.envor AWS keys pattern found
Complement .gitignore—ignore prevents track; hooks catch mistakes.
Server-Side Hooks
Hosting platforms run checks on push:
- GitHub branch protection + required status checks
- GitLab push rules
Client hooks help developers early; server hooks enforce for everyone.
FAQ
Hooks not running?
Check executable bit, shebang #!/bin/sh, filename exact (pre-commit not pre-commit.sh unless configured).
Clone repo—where hooks?
Not copied—run project npm install if husky configured.
Windows line endings?
Use Git Bash or ensure LF in hook scripts.
pre-push vs CI?
pre-push can run npm test before push; CI is authoritative for team.
Replace sample hooks?
Rename removes .sample; Git ships samples as documentation only.
Java projects?
Maven verify in CI; optional pre-commit with ./mvnw -q test—keep fast.