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

Hook Locations

After git init:

text
.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):

bash
#!/bin/sh
# Run unit tests before allowing commit
npm test

Save as .git/hooks/pre-commit, then:

bash
chmod +x .git/hooks/pre-commit

Failed 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:

bash
#!/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
fi

Save as .git/hooks/commit-msg, chmod +x.

Conventional Commits validation is common in shared hook libraries.

Skip Hooks (Emergency Only)

bash
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:

json
{
  "devDependencies": {
    "husky": "^9.0.0",
    "lint-staged": "^15.0.0"
  },
  "scripts": {
    "prepare": "husky"
  }
}

.husky/pre-commit:

bash
npx lint-staged

lint-staged config (in package.json or separate file):

json
{
  "lint-staged": {
    "*.{js,ts}": ["eslint --fix", "prettier --write"]
  }
}

Code explanation:

  • prepare installs husky after npm 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 .env or 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.