Git Hooks

Git hooks are scripts that Git automatically runs when specific events occur — such as before a commit is made, after a push, before a merge, and so on. They allow teams to automate tasks, enforce rules, and maintain code quality without requiring manual effort.

Real-life analogy: Think of a Git hook like a security checkpoint at an office building. Every time someone enters (commits code), they must pass through the checkpoint automatically. The checkpoint can run tests, check formatting, validate badges, and so on. If they do not pass the check, they are not allowed in.

Where are Hooks Stored?

Git hooks are stored as executable scripts inside the .git/hooks/ directory of a repository:

.git/
└── hooks/
    ├── pre-commit.sample
    ├── commit-msg.sample
    ├── pre-push.sample
    ├── post-commit.sample
    ├── pre-merge-commit.sample
    └── ... (many more)

The .sample extension is added by default, which means they are disabled. To enable a hook, the .sample extension is removed and the script is made executable.

Types of Git Hooks

Client-Side Hooks

These run on the developer's local machine during common operations:

HookWhen It RunsCommon Use
pre-commitBefore the commit is createdRun linters, check formatting, run tests
commit-msgAfter commit message is enteredEnforce commit message format
post-commitAfter commit is createdNotifications, logging
pre-pushBefore pushing to remoteRun full test suite before sending code
pre-rebaseBefore a rebase startsValidation checks before rewriting history
post-checkoutAfter switching branchesUpdate dependencies, rebuild environment
post-mergeAfter a merge completesInstall new packages if package.json changed

Server-Side Hooks

These run on the remote server (GitHub/GitLab self-hosted) and cannot be set up on GitHub.com directly:

HookWhen It Runs
pre-receiveBefore accepting a push — can reject it
updateCalled once per branch being pushed
post-receiveAfter the push is accepted — trigger deployments

Creating a pre-commit Hook

This example creates a pre-commit hook that prevents commits if there are any TODO comments left in the code:

# Step 1: Navigate to the hooks directory
cd .git/hooks

# Step 2: Create the hook script
nano pre-commit

Add the following script:

#!/bin/sh
# Check for TODO comments in staged files
if git diff --cached | grep -q "TODO"; then
  echo "ERROR: Staged changes contain TODO comments."
  echo "Please resolve them before committing."
  exit 1
fi
exit 0
# Step 3: Make the hook executable
chmod +x .git/hooks/pre-commit

Now, every time git commit is run, Git will check for TODO comments. If found, the commit will be rejected with the error message.

Creating a commit-msg Hook

This hook enforces that every commit message must start with a capital letter:

#!/bin/sh
# Read the commit message
commit_msg=$(cat "$1")
first_char=$(echo "$commit_msg" | head -c 1)

if [ "$first_char" != "$(echo "$first_char" | tr '[:lower:]' '[:upper:]')" ]; then
  echo "ERROR: Commit message must start with a capital letter."
  exit 1
fi
exit 0

Creating a pre-push Hook

This hook runs the test suite before every push:

#!/bin/sh
echo "Running tests before push..."
npm test

if [ $? -ne 0 ]; then
  echo "Tests failed! Push rejected."
  exit 1
fi

echo "All tests passed. Proceeding with push."
exit 0

Bypassing Hooks (Emergency Use Only)

Hooks can be skipped with the --no-verify flag. This should only be used in genuine emergencies:

git commit --no-verify -m "Emergency hotfix"
git push --no-verify

Sharing Hooks with the Team

A critical limitation of Git hooks is that the .git/hooks/ directory is not committed to the repository — each developer's hooks are local only. To share hooks with a team:

Option 1 — Store hooks in the repo and configure Git

# Create a hooks folder in the project (not .git/hooks)
mkdir -p .githooks

# Add hooks there
cp pre-commit .githooks/

# Tell Git to use this directory for hooks
git config core.hooksPath .githooks

Commit the .githooks folder to the repository so the entire team gets the same hooks.

Option 2 — Use Husky (for JavaScript/Node.js projects)

Husky is a popular npm package that manages Git hooks for the entire team automatically:

npm install husky --save-dev
npx husky init

Husky hooks are stored in a .husky/ folder that is committed to the repository.

Summary

Git hooks are executable scripts that run automatically at specific Git events. The most useful ones are pre-commit (enforce code quality), commit-msg (enforce message format), and pre-push (run tests). Hooks are stored in .git/hooks/ and are not shared by default — use a .githooks/ folder or a tool like Husky to share hooks with the whole team. Hooks help enforce consistency and catch problems before they reach the repository.

Leave a Comment

Your email address will not be published. Required fields are marked *