iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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.

Example

Example
# .husky/pre-commit
npm run lint && npm test
Try it Yourself »

Discussion

Loading…