Git doesn’t just store history—it can also act on it. Git hooks are scripts that trigger on events: before committing, after merging, when pushing, and more. They’re hidden gems that let you enforce policies, automate workflows, and make Git an active teammate instead of a passive tool. In this chapter, we’ll explore Git hooks and how automation turns repositories into living systems.
What Are Git Hooks?
Hooks are executable scripts stored in .git/hooks. Each corresponds to a Git event: pre-commit, post-merge, pre-push, etc. When the event fires, Git runs the script. If it exits non-zero, Git aborts the operation.
List hooks in a repo:
ls .git/hooks
You’ll see samples like pre-commit.sample. To activate one, remove .sample and make it executable.
Visualization:
flowchart TD
Commit[git commit] --> PreCommit[pre-commit hook script]
PreCommit -->|pass| CommitOK[Commit recorded]
PreCommit -->|fail| Abort[Commit aborted]
Common Hooks
- pre-commit: Run linting/tests before allowing commit.
- prepare-commit-msg: Modify commit message templates.
- commit-msg: Validate commit messages.
- pre-push: Run checks before pushing to remote.
- post-merge: Run setup after merging (e.g., install deps).
Example: Pre-commit Hook
Save as .git/hooks/pre-commit:
#!/bin/sh
npm run lint
if [ $? -ne 0 ]; then
echo "Lint failed. Fix before committing."
exit 1
fi
Make it executable:
chmod +x .git/hooks/pre-commit
Now every commit runs linting first.
Example: Commit Message Validation
.git/hooks/commit-msg:
#!/bin/sh
msg=$(cat "$1")
if ! echo "$msg" | grep -qE "^(feat|fix|chore|docs|refactor):"; then
echo "Commit message must follow Conventional Commits format."
exit 1
fi
This enforces team standards.
Automating with Hooks
Hooks can automate repetitive tasks:
- Run tests automatically.
- Update documentation.
- Sync submodules.
- Prevent pushing secrets.
Visualization:
flowchart LR
DevAction[Developer Action] --> Hook[Hook Script]
Hook --> AutoTask[Automated Task]
Git becomes your assistant.
Sharing Hooks with Teams
By default, hooks live in .git/hooks and aren’t versioned. Solutions:
- Use a
hooks/directory tracked in repo, and configure:
git config core.hooksPath hooks
- Tools like Husky (for JS projects) manage hooks across teams.
Solo Workflow Example
As a solo dev, you add a pre-push hook to prevent pushing with TODOs left in code:
#!/bin/sh
if grep -R "TODO" .; then
echo "Remove TODOs before pushing!"
exit 1
fi
This saves you from embarrassing pushes.
Team Workflow Example
On a team, pre-commit hooks ensure code quality uniformly. Everyone’s commits pass linting and tests. Commit-msg hooks enforce standards like Conventional Commits or Jira ticket references.
Visualization of team discipline:
flowchart TD
DevA[Dev A commit] --> Hook[pre-commit hook: lint/tests]
DevB[Dev B commit] --> Hook
Hook --> Repo[Shared Repo: consistent quality]
Beyond Hooks: Automation with CI/CD
Hooks handle local automation. For shared automation, CI/CD pipelines pick up where hooks leave off. A pre-push hook can ensure tests run locally, while CI validates them remotely on pull requests.
Think Different Mindset
Hooks embody Git as more than storage—it’s collaboration with automation. They teach responsibility: don’t trust memory, automate discipline. Instead of hoping developers follow rules, hooks enforce them. Think of hooks as the repo’s immune system, fighting off bad code before it enters history.
Git hooks transform repos from static history into active systems. They enforce discipline, automate chores, and extend Git’s power. Whether solo or in teams, hooks save time and prevent mistakes. In the next chapter, we’ll explore aliasing and shortcuts—ways to make Git faster and friendlier.