Automating Development
Git has a way to fire off custom scripts when certain important actions occur. These are called Hooks. They are essentially the “events” of the Git lifecycle.
There are two groups of hooks:
- Client-side: Triggered by operations like committing and merging.
- Server-side: Triggered by network operations like receiving pushed commits.
Client-Side Hooks
Hooks are stored in the .git/hooks directory of your repository. By default, Git populates this folder with sample scripts (pre-commit.sample, etc.). To enable a hook, you simply create an executable file with the name of the hook (no extension).
pre-commit
This hook runs before you even type a commit message. It is used to inspect the snapshot that is about to be committed.
- Use cases: Linting code, checking for trailing whitespace, running unit tests.
- Exit Code: If the script exits non-zero, the commit is aborted.
Example pre-commit script (Bash):
#!/bin/sh
echo "Running tests..."
npm test
if [ $? -ne 0 ]; then
echo "Tests failed! Commit aborted."
exit 1
fi
prepare-commit-msg
Run before the commit message editor is fired up but after the default message is created.
- Use cases: Automatically prepending the ticket number from the branch name to the commit message.
commit-msg
Takes one parameter (the path to the temporary file containing the commit message).
- Use cases: Enforcing a commit message pattern (e.g., “feat: …”).
post-merge
Runs after a successful merge command.
- Use cases: Re-installing dependencies (like
npm install) ifpackage.jsonchanged.
Server-Side Hooks
These scripts run on the server (remote) when it receives a push.
pre-receive
The first script to run when handling a push from a client. It takes a list of references that are being pushed from stdin.
- Use cases: Rejecting force pushes to main, enforcing access control.
update
Similar to pre-receive, but runs once for each branch the pusher is trying to update.
post-receive
Runs after the entire process is completed.
- Use cases: Triggering a CI/CD build, notifying Slack/Email, deploying to production.
Sharing Hooks
Hooks are stored in .git/hooks, which is not version controlled. This means if you clone a repo, you don’t get the hooks.
To share hooks with your team, you have two common strategies:
-
Symlinks: Store scripts in a
scripts/hooksfolder (which is versioned) and symlink them to.git/hooks.git config core.hooksPath scripts/hooks -
Husky (Node.js): A popular tool for JavaScript projects that automatically configures hooks.
// package.json "husky": { "hooks": { "pre-commit": "npm test" } }
Bypassing Hooks
Sometimes you need to commit code that you know fails the linter (e.g., a WIP save). You can bypass client-side hooks with:
git commit --no-verify
(or -n)
Where are Git hooks stored by default?
Bypassing Hooks
# You need to commit immediately and skip the pre-commit hook. git commit -m "Emergency fix"