git-hooks-automation

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Git Hooks Automation

Git Hooks 自动化

Automate code quality enforcement at the Git level. Set up hooks that lint, format, test, and validate before commits and pushes ever reach your CI pipeline — catching issues in seconds instead of minutes.
在Git层面自动化执行代码质量校验。在提交和推送进入CI流水线之前,设置钩子来执行代码检查、格式化、测试和验证——在数秒内发现问题,而非等待数分钟。

When to Use This Skill

何时使用该技能

  • User asks to "set up git hooks" or "add pre-commit hooks"
  • Configuring Husky, lint-staged, or the pre-commit framework
  • Enforcing commit message conventions (Conventional Commits, commitlint)
  • Automating linting, formatting, or type-checking before commits
  • Setting up pre-push hooks for test runners
  • Migrating from Husky v4 to v9+ or adopting hooks from scratch
  • User mentions "pre-commit", "commit-msg", "pre-push", "lint-staged", or "githooks"
  • 用户要求"设置git hooks"或"添加pre-commit钩子"
  • 配置Husky、lint-staged或pre-commit框架
  • 强制执行提交信息规范(Conventional Commits、commitlint)
  • 提交前自动执行代码检查、格式化或类型校验
  • 设置pre-push钩子来运行测试用例
  • 从Husky v4迁移到v9+或从零开始引入钩子
  • 用户提及"pre-commit"、"commit-msg"、"pre-push"、"lint-staged"或"githooks"

Git Hooks Fundamentals

Git Hooks 基础

Git hooks are scripts that run automatically at specific points in the Git workflow. They live in
.git/hooks/
and are not version-controlled by default — which is why tools like Husky exist.
Git hooks是在Git工作流特定节点自动运行的脚本。它们默认存储在
.git/hooks/
目录下,且不会被版本控制——这也是Husky这类工具存在的原因。

Hook Types & When They Fire

钩子类型及触发时机

HookFires WhenCommon Use
pre-commit
Before commit is createdLint, format, type-check staged files
prepare-commit-msg
After default msg, before editorAuto-populate commit templates
commit-msg
After user writes commit messageEnforce commit message format
post-commit
After commit is createdNotifications, logging
pre-push
Before push to remoteRun tests, check branch policies
pre-rebase
Before rebase startsPrevent rebase on protected branches
post-merge
After merge completesInstall deps, run migrations
post-checkout
After checkout/switchInstall deps, rebuild assets
钩子触发时机常见用途
pre-commit
提交创建前对暂存文件执行代码检查、格式化、类型校验
prepare-commit-msg
生成默认提交信息后,打开编辑器前自动填充提交模板
commit-msg
用户编写提交信息后强制执行提交信息格式
post-commit
提交创建后发送通知、记录日志
pre-push
推送到远程仓库前运行测试、检查分支策略
pre-rebase
变基开始前禁止对受保护分支执行变基
post-merge
合并完成后安装依赖、运行迁移脚本
post-checkout
切换分支/检出后安装依赖、重新构建资源

Native Git Hooks (No Framework)

原生Git Hooks(无框架)

bash
undefined
bash
undefined

Create a pre-commit hook manually

Create a pre-commit hook manually

cat > .git/hooks/pre-commit << 'EOF' #!/bin/sh set -e
cat > .git/hooks/pre-commit << 'EOF' #!/bin/sh set -e

Run linter on staged files only

Run linter on staged files only

STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '.(js|ts|jsx|tsx)$' || true)
if [ -n "$STAGED_FILES" ]; then echo "🔍 Linting staged files..." echo "$STAGED_FILES" | xargs npx eslint --fix echo "$STAGED_FILES" | xargs git add # Re-stage after fixes fi EOF chmod +x .git/hooks/pre-commit

**Problem**: `.git/hooks/` is local-only and not shared with the team. Use a framework instead.
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '.(js|ts|jsx|tsx)$' || true)
if [ -n "$STAGED_FILES" ]; then echo "🔍 Linting staged files..." echo "$STAGED_FILES" | xargs npx eslint --fix echo "$STAGED_FILES" | xargs git add # Re-stage after fixes fi EOF chmod +x .git/hooks/pre-commit

**问题**:`.git/hooks/`目录仅本地存在,不会与团队共享。建议使用框架来管理。

Husky + lint-staged (Node.js Projects)

Husky + lint-staged(Node.js 项目)

The modern standard for JavaScript/TypeScript projects. Husky manages Git hooks; lint-staged runs commands only on staged files for speed.
这是JavaScript/TypeScript项目的现代标准方案。Husky负责管理Git hooks;lint-staged仅对暂存文件执行命令,提升速度。

Quick Setup (Husky v9+)

快速搭建(Husky v9+)

bash
undefined
bash
undefined

Install

Install

npm install --save-dev husky lint-staged
npm install --save-dev husky lint-staged

Initialize Husky (creates .husky/ directory)

Initialize Husky (creates .husky/ directory)

npx husky init
npx husky init

The init command creates a pre-commit hook — edit it:

The init command creates a pre-commit hook — edit it:

echo "npx lint-staged" > .husky/pre-commit
undefined
echo "npx lint-staged" > .husky/pre-commit
undefined

Configure lint-staged in
package.json

package.json
中配置lint-staged

json
{
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix --max-warnings=0",
      "prettier --write"
    ],
    "*.{css,scss}": [
      "prettier --write",
      "stylelint --fix"
    ],
    "*.{json,md,yml,yaml}": [
      "prettier --write"
    ]
  }
}
json
{
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix --max-warnings=0",
      "prettier --write"
    ],
    "*.{css,scss}": [
      "prettier --write",
      "stylelint --fix"
    ],
    "*.{json,md,yml,yaml}": [
      "prettier --write"
    ]
  }
}

Add Commit Message Linting

添加提交信息校验

bash
undefined
bash
undefined

Install commitlint

Install commitlint

npm install --save-dev @commitlint/cli @commitlint/config-conventional
npm install --save-dev @commitlint/cli @commitlint/config-conventional

Create commitlint config

Create commitlint config

cat > commitlint.config.js << 'EOF' module.exports = { extends: ['@commitlint/config-conventional'], rules: { 'type-enum': [2, 'always', [ 'feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore', 'revert' ]], 'subject-max-length': [2, 'always', 72], 'body-max-line-length': [2, 'always', 100] } }; EOF
cat > commitlint.config.js << 'EOF' module.exports = { extends: ['@commitlint/config-conventional'], rules: { 'type-enum': [2, 'always', [ 'feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore', 'revert' ]], 'subject-max-length': [2, 'always', 72], 'body-max-line-length': [2, 'always', 100] } }; EOF

Add commit-msg hook

Add commit-msg hook

echo "npx --no -- commitlint --edit $1" > .husky/commit-msg
undefined
echo "npx --no -- commitlint --edit $1" > .husky/commit-msg
undefined

Add Pre-Push Hook

添加Pre-Push钩子

bash
undefined
bash
undefined

Run tests before pushing

Run tests before pushing

echo "npm test" > .husky/pre-push
undefined
echo "npm test" > .husky/pre-push
undefined

Complete Husky Directory Structure

完整的Husky目录结构

project/
├── .husky/
│   ├── pre-commit        # npx lint-staged
│   ├── commit-msg        # npx --no -- commitlint --edit $1
│   └── pre-push          # npm test
├── commitlint.config.js
├── package.json          # lint-staged config here
└── ...
project/
├── .husky/
│   ├── pre-commit        # npx lint-staged
│   ├── commit-msg        # npx --no -- commitlint --edit $1
│   └── pre-push          # npm test
├── commitlint.config.js
├── package.json          # lint-staged config here
└── ...

pre-commit Framework (Python / Polyglot)

pre-commit框架(Python / 多语言)

Language-agnostic framework that works with any project. Hooks are defined in YAML and run in isolated environments.
语言无关的框架,适用于任何项目。钩子通过YAML定义,并在隔离环境中运行。

Setup

搭建步骤

bash
undefined
bash
undefined

Install (Python required)

Install (Python required)

pip install pre-commit
pip install pre-commit

Create config

Create config

cat > .pre-commit-config.yaml << 'EOF' repos:

Built-in checks

  • 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
    • id: check-json
    • id: check-added-large-files args: ['--maxkb=500']
    • id: check-merge-conflict
    • id: detect-private-key

Python formatting

Python linting

Shell script linting

Commit message format

cat > .pre-commit-config.yaml << 'EOF' repos:

Built-in checks

  • 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
    • id: check-json
    • id: check-added-large-files args: ['--maxkb=500']
    • id: check-merge-conflict
    • id: detect-private-key

Python formatting

Python linting

Shell script linting

Commit message format

Install hooks into .git/hooks/

Install hooks into .git/hooks/

pre-commit install pre-commit install --hook-type commit-msg
pre-commit install pre-commit install --hook-type commit-msg

Run against all files (first time)

Run against all files (first time)

pre-commit run --all-files
undefined
pre-commit run --all-files
undefined

Key Commands

常用命令

bash
pre-commit install              # Install hooks
pre-commit run --all-files      # Run on everything (CI or first setup)
pre-commit autoupdate           # Update hook versions
pre-commit run <hook-id>        # Run a specific hook
pre-commit clean                # Clear cached environments
bash
pre-commit install              # Install hooks
pre-commit run --all-files      # Run on everything (CI or first setup)
pre-commit autoupdate           # Update hook versions
pre-commit run <hook-id>        # Run a specific hook
pre-commit clean                # Clear cached environments

Custom Hook Scripts (Any Language)

自定义钩子脚本(任意语言)

For projects not using Node or Python, write hooks directly in shell.
对于不使用Node或Python的项目,可以直接用Shell编写钩子。

Portable Pre-Commit Hook

可移植的Pre-Commit钩子

bash
#!/bin/sh
bash
#!/bin/sh

.githooks/pre-commit — Team-shared hooks directory

.githooks/pre-commit — Team-shared hooks directory

set -e
echo "=== Pre-Commit Checks ==="
set -e
echo "=== Pre-Commit Checks ==="

1. Prevent commits to main/master

1. Prevent commits to main/master

BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "detached") if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then echo "❌ Direct commits to $BRANCH are not allowed. Use a feature branch." exit 1 fi
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "detached") if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then echo "❌ Direct commits to $BRANCH are not allowed. Use a feature branch." exit 1 fi

2. Check for debugging artifacts

2. Check for debugging artifacts

if git diff --cached --diff-filter=ACM | grep -nE '(console.log|debugger|binding.pry|import pdb)' > /dev/null 2>&1; then echo "⚠️ Debug statements found in staged files:" git diff --cached --diff-filter=ACM | grep -nE '(console.log|debugger|binding.pry|import pdb)' echo "Remove them or use git commit --no-verify to bypass." exit 1 fi
if git diff --cached --diff-filter=ACM | grep -nE '(console.log|debugger|binding.pry|import pdb)' > /dev/null 2>&1; then echo "⚠️ Debug statements found in staged files:" git diff --cached --diff-filter=ACM | grep -nE '(console.log|debugger|binding.pry|import pdb)' echo "Remove them or use git commit --no-verify to bypass." exit 1 fi

3. Check for large files (>1MB)

3. Check for large files (>1MB)

LARGE_FILES=$(git diff --cached --name-only --diff-filter=ACM | while read f; do size=$(wc -c < "$f" 2>/dev/null || echo 0) if [ "$size" -gt 1048576 ]; then echo "$f ($((size/1024))KB)"; fi done) if [ -n "$LARGE_FILES" ]; then echo "❌ Large files detected:" echo "$LARGE_FILES" exit 1 fi
LARGE_FILES=$(git diff --cached --name-only --diff-filter=ACM | while read f; do size=$(wc -c < "$f" 2>/dev/null || echo 0) if [ "$size" -gt 1048576 ]; then echo "$f ($((size/1024))KB)"; fi done) if [ -n "$LARGE_FILES" ]; then echo "❌ Large files detected:" echo "$LARGE_FILES" exit 1 fi

4. Check for secrets patterns

4. Check for secrets patterns

if git diff --cached --diff-filter=ACM | grep -nEi '(AKIA[0-9A-Z]{16}|sk-[a-zA-Z0-9]{48}|ghp_[a-zA-Z0-9]{36}|password\s*=\s*["\x27][^"\x27]+["\x27])' > /dev/null 2>&1; then echo "🚨 Potential secrets detected in staged changes! Review before committing." exit 1 fi
echo "✅ All pre-commit checks passed"
undefined
if git diff --cached --diff-filter=ACM | grep -nEi '(AKIA[0-9A-Z]{16}|sk-[a-zA-Z0-9]{48}|ghp_[a-zA-Z0-9]{36}|password\s*=\s*["\x27][^"\x27]+["\x27])' > /dev/null 2>&1; then echo "🚨 Potential secrets detected in staged changes! Review before committing." exit 1 fi
echo "✅ All pre-commit checks passed"
undefined

Share Custom Hooks via
core.hooksPath

通过
core.hooksPath
共享自定义钩子

bash
undefined
bash
undefined

In your repo, set a shared hooks directory

In your repo, set a shared hooks directory

git config core.hooksPath .githooks
git config core.hooksPath .githooks

Add to project setup docs or Makefile

Add to project setup docs or Makefile

Makefile

Makefile

setup: git config core.hooksPath .githooks chmod +x .githooks/*
undefined
setup: git config core.hooksPath .githooks chmod +x .githooks/*
undefined

CI Integration

CI集成

Hooks are a first line of defense, but CI is the source of truth.
钩子是第一道防线,但CI才是最终的保障。

Run pre-commit in CI (GitHub Actions)

在CI中运行pre-commit(GitHub Actions)

yaml
undefined
yaml
undefined

.github/workflows/lint.yml

.github/workflows/lint.yml

name: Lint on: [push, pull_request] jobs: pre-commit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.12' - uses: pre-commit/action@v3.0.1
undefined
name: Lint on: [push, pull_request] jobs: pre-commit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.12' - uses: pre-commit/action@v3.0.1
undefined

Run lint-staged in CI (Validation Only)

在CI中运行lint-staged(仅验证)

yaml
undefined
yaml
undefined

Validate that lint-staged would pass (catch bypassed hooks)

Validate that lint-staged would pass (catch bypassed hooks)

name: Lint Check on: [pull_request] jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npx eslint . --max-warnings=0 - run: npx prettier --check .
undefined
name: Lint Check on: [pull_request] jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npx eslint . --max-warnings=0 - run: npx prettier --check .
undefined

Common Pitfalls & Fixes

常见问题及解决方法

Hooks Not Running

钩子未运行

SymptomCauseFix
Hooks silently skippedNot installed in
.git/hooks/
Run
npx husky init
or
pre-commit install
"Permission denied"Hook file not executable
chmod +x .husky/pre-commit
Hooks run but wrong onesStale hooks from old setupDelete
.git/hooks/
contents, reinstall
Works locally, fails in CIDifferent Node/Python versionsPin versions in CI config
症状原因解决方法
钩子静默跳过未安装到
.git/hooks/
目录
运行
npx husky init
pre-commit install
"Permission denied"(权限拒绝)钩子文件无执行权限执行
chmod +x .husky/pre-commit
钩子运行但执行的是旧钩子旧设置留下的过期钩子删除
.git/hooks/
目录下的内容,重新安装
本地正常运行,CI中失败Node/Python版本不一致在CI配置中固定版本

Performance Issues

性能问题

json
// ❌ Slow: runs on ALL files every commit
{
  "scripts": {
    "precommit": "eslint src/ && prettier --write src/"
  }
}

// ✅ Fast: lint-staged runs ONLY on staged files
{
  "lint-staged": {
    "*.{js,ts}": ["eslint --fix", "prettier --write"]
  }
}
json
// ❌ 缓慢:每次提交都检查所有文件
{
  "scripts": {
    "precommit": "eslint src/ && prettier --write src/"
  }
}

// ✅ 快速:lint-staged仅检查暂存文件
{
  "lint-staged": {
    "*.{js,ts}": ["eslint --fix", "prettier --write"]
  }
}

Bypassing Hooks (When Needed)

跳过钩子(必要时)

bash
undefined
bash
undefined

Skip all hooks for a single commit

单次提交跳过所有钩子

git commit --no-verify -m "wip: quick save"
git commit --no-verify -m "wip: quick save"

Skip pre-push only

仅跳过pre-push钩子

git push --no-verify
git push --no-verify

Skip specific pre-commit hooks

跳过特定的pre-commit钩子

SKIP=eslint git commit -m "fix: update config"

> **Warning**: Bypassing hooks should be rare. If your team frequently bypasses, the hooks are too slow or too strict — fix them.
SKIP=eslint git commit -m "fix: update config"

> **警告**:跳过钩子应是极少数情况。如果团队频繁跳过钩子,说明钩子速度太慢或规则太严格——需要优化它们。

Migration Guide

迁移指南

Husky v4 → v9 Migration

Husky v4 → v9迁移

bash
undefined
bash
undefined

1. Remove old Husky

1. 移除旧版Husky

npm uninstall husky rm -rf .husky
npm uninstall husky rm -rf .husky

2. Remove old config from package.json

2. 从package.json中移除旧配置

Delete "husky": { "hooks": { ... } } section

删除"husky": { "hooks": { ... } }部分

3. Install fresh

3. 安装新版

npm install --save-dev husky npx husky init
npm install --save-dev husky npx husky init

4. Recreate hooks

4. 重新创建钩子

echo "npx lint-staged" > .husky/pre-commit echo "npx --no -- commitlint --edit $1" > .husky/commit-msg
echo "npx lint-staged" > .husky/pre-commit echo "npx --no -- commitlint --edit $1" > .husky/commit-msg

5. Clean up — old Husky used package.json config,

5. 清理——旧版Husky使用package.json配置,

new Husky uses .husky/ directory with plain scripts

新版Husky使用.husky/目录下的纯脚本

undefined
undefined

Adopting Hooks on an Existing Project

在现有项目中引入钩子

bash
undefined
bash
undefined

Step 1: Start with formatting only (low friction)

步骤1:从仅格式化开始(低阻力)

lint-staged config:

lint-staged配置:

{ "*.{js,ts}": ["prettier --write"] }
{ "*.{js,ts}": ["prettier --write"] }

Step 2: Add linting after team adjusts (1-2 weeks later)

步骤2:团队适应后添加代码检查(1-2周后)

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

Step 3: Add commit message linting

步骤3:添加提交信息校验

Step 4: Add pre-push test runner

步骤4:添加pre-push测试运行器

Gradual adoption prevents team resistance

逐步引入可避免团队抵触

undefined
undefined

Key Principles

核心原则

  • Staged files only — Never lint the entire codebase on every commit
  • Auto-fix when possible
    --fix
    flags reduce developer friction
  • Fast hooks — Pre-commit should complete in < 5 seconds
  • Fail loud — Clear error messages with actionable fixes
  • Team-shared — Use Husky or
    core.hooksPath
    so hooks are version-controlled
  • CI as backup — Hooks are convenience; CI is the enforcer
  • Gradual adoption — Start with formatting, add linting, then testing
  • 仅针对暂存文件——不要在每次提交时检查整个代码库
  • 尽可能自动修复——使用
    --fix
    参数减少开发者的操作成本
  • 钩子要快速——pre-commit钩子应在5秒内完成
  • 明确报错——提供清晰的错误信息和可操作的修复方案
  • 团队共享——使用Husky或
    core.hooksPath
    让钩子被版本控制
  • CI作为备份——钩子是便利工具;CI才是最终的执行者
  • 逐步引入——从格式化开始,再添加代码检查,最后加入测试

Related Skills

相关技能

  • @codebase-audit-pre-push
    - Deep audit before GitHub push
  • @verification-before-completion
    - Verification before claiming work is done
  • @bash-pro
    - Advanced shell scripting for custom hooks
  • @github-actions-templates
    - CI/CD workflow templates
  • @codebase-audit-pre-push
    - GitHub推送前深度审计
  • @verification-before-completion
    - 完成工作前的验证
  • @bash-pro
    - 用于自定义钩子的高级Shell脚本
  • @github-actions-templates
    - CI/CD工作流模板