Ch. 16 · Git & CI/CD

Git Hooks and Local Automation

Automate checks with client and server hooks, share them through a framework, and enforce the same rules in CI where they cannot be bypassed.

~2 min readintermediateupdated Oct 5, 2026

Git hooks are scripts Git runs at points in its workflow, such as pre-commit before a commit and pre-receive before a push is accepted. Client hooks speed up the inner loop; server hooks enforce policy that a developer cannot skip with --no-verify.

Before you start

You should be comfortable with commits and the .git directory. This article covers hook categories and sharing them; it assumes basic shell scripting.

Step-by-step walkthrough

Step 1: Know which hook runs where

Client hooks (pre-commit, commit-msg, pre-push) run on the developer’s machine and are not versioned in the repository by default. Server hooks (pre-receive, update) run on the remote and cannot be bypassed, which is why CI checks and server hooks are the enforcement layer.

Step 2: Share client hooks with a framework

Because .git/hooks is not committed, teams use a tool such as Husky or lefthook that installs hooks on npm install and versions the configuration. Keep hooks fast — lint staged files, not the whole project — so commits stay quick.

Step 3: Enforce the important rules remotely

Anything that must be guaranteed belongs in CI or a server hook: running tests, checking commit message format, or scanning for secrets. Client hooks are a convenience; they can be skipped or missing on a fresh clone.

Worked scenario

A pre-commit hook lints only the staged files and blocks the commit on failure.

#!/bin/sh
# .git/hooks/pre-commit
npx lint-staged || exit 1
Terminal

Walk through the example

Git runs the script before creating the commit, passing no arguments. lint-staged reads the staged file list and lints just those files, so the check is fast. A non-zero exit stops the commit, so a lint failure cannot be committed unless the developer fixes it or overrides with --no-verify, which is exactly why CI must run the same check.

Common mistake

Relying on client hooks as the only quality gate, since --no-verify bypasses them and a fresh clone has none until the framework installs them. Another is running the full test suite in pre-commit, which makes commits slow enough that people disable hooks.

Verify the behavior

Stage a file with a lint error and confirm the commit is blocked, then confirm git commit --no-verify skips it. Clone the repo fresh, run install, and check the hooks are present. Confirm the same check runs in CI and fails the pipeline when the local hook was bypassed.

Interview exercise

Should a secret scan live in a client hook or in CI?

Answer and reasoning

Both, with CI as the source of truth. A client pre-commit scan gives fast feedback and stops most mistakes before they leave the machine, but it can be bypassed or absent. The CI scan is the guarantee, running on every push where it cannot be skipped, so a leaked secret is caught before it reaches the main branch.

Continue learning

Compare release automation in CI required checks and signal handling in Git secret management. Read the Git hooks documentation and try the Git interview questions.

More in Git & CI/CD

read ✓Git & CI/CD · easy

Git Detached HEAD

What a detached HEAD is, when it happens, and how to keep commits made in that state.

~2 min readread →
read ✓Git & CI/CD · hard

Git LFS for Large Files

Track large binaries with Git LFS, keep pointer files in the repository, and plan for LFS storage and access.

~2 min readread →
read ✓Git & CI/CD · hard

Git Partial and Shallow Clones

Fetch less history and content with shallow and partial clones, and understand what you give up.

~2 min readread →
esc