Git Hooks
Git hooks: pre-commit, pre-push, commit-msg. Local enforcement, shared via Husky / Lefthook / pre-commit framework.
Git — hooks
EXAMPLE
# ===== Built-in hooks =====
# .git/hooks/ contains sample scripts. Make any executable to enable.
# Common hooks:
# pre-commit runs before commit (lint, format)
# commit-msg validate commit message (Conventional Commits)
# pre-push validate before push (run tests)
# post-commit notifications
# pre-rebase safety check
# ===== Husky (the most common JS option) =====
npm install -D husky lint-staged
npx husky init
# .husky/pre-commit
npx lint-staged
# package.json:
{
"lint-staged": {
"*.{js,ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{json,md,yml}": ["prettier --write"]
}
}
# Commit message hook:
# .husky/commit-msg
npx commitlint --edit $1
# Install commitlint:
npm install -D @commitlint/cli @commitlint/config-conventional
# commitlint.config.js
module.exports = { extends: ['@commitlint/config-conventional'] };
# ===== Lefthook (faster, language-agnostic) =====
# Install: brew install lefthook
# lefthook.yml
pre-commit:
parallel: true
commands:
lint:
glob: '*.{js,ts,tsx}'
run: 'eslint --fix {staged_files}'
format:
glob: '*.{json,md,yml}'
run: 'prettier --write {staged_files}'
pre-push:
commands:
test:
run: 'npm test'
lefthook install
# ===== pre-commit framework (Python ecosystem) =====
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- repo: https://github.com/psf/black
rev: 24.4.0
hooks:
- id: black
pre-commit install
pre-commit run --all-files
# ===== Conventional Commits =====
# Title: type(scope): subject
# Body: what + why
# Footer: BREAKING CHANGE, Co-Authored-By
#
# Examples:
# feat(orders): add tax field
# fix(login): handle empty password
# docs(readme): clarify install
# ===== Skipping hooks =====
git commit --no-verify -m 'urgent fix'
git push --no-verify
# Avoid in normal workflow. Only when CI will catch the same thing.
# ===== Server-side hooks =====
# Hosted Git (GitHub / GitLab / Bitbucket) doesn't run client-side hooks.
# Use:
# - Required status checks (CI) for tests
# - Code owners + required reviews
# - Push rules (GitLab) / branch protection (GitHub)
# - Pre-receive hooks on self-hosted Git server
# ===== Patterns =====
# - Husky/Lefthook for JS; pre-commit for Python
# - lint-staged so hooks ONLY run on changed files
# - commitlint for Conventional Commits
# - pre-push for tests (catch before remote)
# - Server-side CI for actual gates
# ===== Pitfalls =====
# - Slow hooks -> people --no-verify
# - Hooks running on entire repo every commit (use lint-staged)
# - Hooks that change files but don't re-stage
# - Forgetting that hooks live OUTSIDE git's tracked files (.git/hooks/)
Why it matters
Git hooks enforce quality locally before pushing. Husky for JS, Lefthook for speed, pre-commit for Python. Pair with lint-staged so only changed files are checked, commitlint for Conventional Commits, and CI as the real gate. Keep hooks fast or people will bypass them.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Discussion
Loading…