git-hooks-setup
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGit Hooks Setup
Git Hooks 配置指南
Overview
概述
Configure Git hooks to enforce code quality standards, run automated checks, and prevent problematic commits from being pushed to shared repositories.
配置Git hooks以强制执行代码质量标准、运行自动化检查,并防止有问题的提交被推送到共享仓库。
When to Use
适用场景
- Pre-commit code quality checks
- Commit message validation
- Preventing secrets in commits
- Running tests before push
- Code formatting enforcement
- Linting configuration
- Team-wide standards enforcement
- 提交前代码质量检查
- 提交信息验证
- 防止提交中包含敏感信息
- 推送前运行测试
- 代码格式化强制执行
- 代码扫描(Linting)配置
- 团队统一标准强制执行
Implementation Examples
配置示例
1. Husky Installation and Configuration
1. Husky的安装与配置
bash
#!/bin/bashbash
#!/bin/bashsetup-husky.sh
setup-husky.sh
Install Husky
Install Husky
npm install husky --save-dev
npm install husky --save-dev
Initialize Husky
Initialize Husky
npx husky install
npx husky install
Create pre-commit hook
Create pre-commit hook
npx husky add .husky/pre-commit "npm run lint"
npx husky add .husky/pre-commit "npm run lint"
Create commit-msg hook
Create commit-msg hook
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
Create pre-push hook
Create pre-push hook
npx husky add .husky/pre-push "npm run test"
npx husky add .husky/pre-push "npm run test"
Create post-merge hook
Create post-merge hook
npx husky add .husky/post-merge "npm install"
undefinednpx husky add .husky/post-merge "npm install"
undefined2. Pre-commit Hook (Node.js)
2. Node.js环境下的Pre-commit钩子
bash
#!/usr/bin/env nodebash
#!/usr/bin/env node.husky/pre-commit
.husky/pre-commit
const { execSync } = require('child_process');
const fs = require('fs');
console.log('🔍 Running pre-commit checks...\n');
try {
// Get staged files
const stagedFiles = execSync('git diff --cached --name-only', { encoding: 'utf-8' })
.split('\n')
.filter(file => file && (file.endsWith('.js') || file.endsWith('.ts')))
.join(' ');
if (!stagedFiles) {
console.log('✅ No JavaScript/TypeScript files to check');
process.exit(0);
}
// Run linter on staged files
console.log('📝 Running ESLint...');
execSync(, { stdio: 'inherit' });
npx eslint ${stagedFiles} --fix// Run Prettier
console.log('✨ Running Prettier...');
execSync(, { stdio: 'inherit' });
npx prettier --write ${stagedFiles}// Stage the fixed files
console.log('📦 Staging fixed files...');
execSync();
git add ${stagedFiles}console.log('\n✅ Pre-commit checks passed!');
} catch (error) {
console.error('❌ Pre-commit checks failed!');
process.exit(1);
}
undefinedconst { execSync } = require('child_process');
const fs = require('fs');
console.log('🔍 正在运行提交前检查...\n');
try {
// Get staged files
const stagedFiles = execSync('git diff --cached --name-only', { encoding: 'utf-8' })
.split('\n')
.filter(file => file && (file.endsWith('.js') || file.endsWith('.ts')))
.join(' ');
if (!stagedFiles) {
console.log('✅ 没有需要检查的JavaScript/TypeScript文件');
process.exit(0);
}
// Run linter on staged files
console.log('📝 运行ESLint检查...');
execSync(, { stdio: 'inherit' });
npx eslint ${stagedFiles} --fix// Run Prettier
console.log('✨ 运行Prettier格式化...');
execSync(, { stdio: 'inherit' });
npx prettier --write ${stagedFiles}// Stage the fixed files
console.log('📦 暂存修复后的文件...');
execSync();
git add ${stagedFiles}console.log('\n✅ 提交前检查通过!');
} catch (error) {
console.error('❌ 提交前检查失败!');
process.exit(1);
}
undefined3. Commit Message Validation
3. 提交信息验证
bash
#!/bin/bashbash
#!/bin/bash.husky/commit-msg
.husky/commit-msg
Validate commit message format
Validate commit message format
COMMIT_MSG=$(<"$1")
COMMIT_MSG=$(<"$1")
Pattern: type(scope): description
Pattern: type(scope): description
PATTERN="^(feat|fix|docs|style|refactor|test|chore|perf)(([a-z-]+))?: .{1,50}"
if ! [[ $COMMIT_MSG =~ $PATTERN ]]; then
echo "❌ Invalid commit message format"
echo "Format: type(scope): description"
echo "Types: feat, fix, docs, style, refactor, test, chore, perf"
echo ""
echo "Examples:"
echo " feat: add new feature"
echo " fix(auth): resolve login bug"
echo " docs: update README"
exit 1
fi
PATTERN="^(feat|fix|docs|style|refactor|test|chore|perf)(([a-z-]+))?: .{1,50}"
if ! [[ $COMMIT_MSG =~ $PATTERN ]]; then
echo "❌ 提交信息格式无效"
echo "格式要求: type(scope): description"
echo "类型选项: feat, fix, docs, style, refactor, test, chore, perf"
echo ""
echo "示例:"
echo " feat: add new feature"
echo " fix(auth): resolve login bug"
echo " docs: update README"
exit 1
fi
Check message length
Check message length
FIRST_LINE=$(echo "$COMMIT_MSG" | head -n1)
if [ ${#FIRST_LINE} -gt 72 ]; then
echo "❌ Commit message too long (max 72 characters)"
exit 1
fi
echo "✅ Commit message is valid"
undefinedFIRST_LINE=$(echo "$COMMIT_MSG" | head -n1)
if [ ${#FIRST_LINE} -gt 72 ]; then
echo "❌ 提交信息过长(最多72个字符)"
exit 1
fi
echo "✅ 提交信息格式有效"
undefined4. Commitlint Configuration
4. Commitlint配置
javascript
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore']],
'subject-case': [2, 'never', ['start-case', 'pascal-case', 'upper-case']],
'type-empty': [2, 'never']
}
};javascript
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore']],
'subject-case': [2, 'never', ['start-case', 'pascal-case', 'upper-case']],
'type-empty': [2, 'never']
}
};5. Pre-push Hook (Comprehensive)
5. 全面的Pre-push钩子
bash
#!/usr/bin/env bashbash
#!/usr/bin/env bash.husky/pre-push
.husky/pre-push
BRANCH=$(git rev-parse --abbrev-ref HEAD)
BRANCH=$(git rev-parse --abbrev-ref HEAD)
Prevent direct pushes to main
Prevent direct pushes to main
if [[ "$BRANCH" =~ ^(main|master)$ ]]; then
echo "❌ Direct push to $BRANCH not allowed"
exit 1
fi
npm test && npm run lint && npm run build
undefinedif [[ "$BRANCH" =~ ^(main|master)$ ]]; then
echo "❌ 不允许直接推送到$BRANCH分支"
exit 1
fi
npm test && npm run lint && npm run build
undefined6. Pre-commit Framework (Python)
6. Python环境下的Pre-commit框架
yaml
undefinedyaml
undefined.pre-commit-config.yaml
.pre-commit-config.yaml
repos:
-
repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files args: ['--maxkb=1000']
- id: detect-private-key
- id: check-merge-conflict
-
repo: https://github.com/psf/black rev: 23.3.0 hooks:
- id: black language_version: python3.11
-
repo: https://github.com/PyCQA/flake8 rev: 6.0.0 hooks:
- id: flake8 args: ['--max-line-length=88', '--extend-ignore=E203,W503']
-
repo: https://github.com/PyCQA/isort rev: 5.12.0 hooks:
- id: isort args: ['--profile', 'black']
-
repo: https://github.com/Yelp/detect-secrets rev: v1.4.0 hooks:
- id: detect-secrets args: ['--baseline', '.secrets.baseline']
-
repo: https://github.com/commitizen-tools/commitizen rev: 3.5.2 hooks:
- id: commitizen stages: [commit-msg]
undefinedrepos:
-
repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files args: ['--maxkb=1000']
- id: detect-private-key
- id: check-merge-conflict
-
repo: https://github.com/psf/black rev: 23.3.0 hooks:
- id: black language_version: python3.11
-
repo: https://github.com/PyCQA/flake8 rev: 6.0.0 hooks:
- id: flake8 args: ['--max-line-length=88', '--extend-ignore=E203,W503']
-
repo: https://github.com/PyCQA/isort rev: 5.12.0 hooks:
- id: isort args: ['--profile', 'black']
-
repo: https://github.com/Yelp/detect-secrets rev: v1.4.0 hooks:
- id: detect-secrets args: ['--baseline', '.secrets.baseline']
-
repo: https://github.com/commitizen-tools/commitizen rev: 3.5.2 hooks:
- id: commitizen stages: [commit-msg]
undefined7. Secret Detection Hook
7. 敏感信息检测钩子
bash
#!/bin/bashbash
#!/bin/bash.husky/pre-commit-secrets
.husky/pre-commit-secrets
git diff --cached | grep -E 'password|api_key|secret|token' && exit 1
echo "✅ No secrets detected"
undefinedgit diff --cached | grep -E 'password|api_key|secret|token' && exit 1
echo "✅ 未检测到敏感信息"
undefined8. Husky in package.json
8. package.json中的Husky配置
json
{
"scripts": { "prepare": "husky install" },
"devDependencies": {
"husky": "^8.0.0",
"@commitlint/cli": "^17.0.0"
},
"lint-staged": {
"*.{js,ts}": "eslint --fix"
}
}json
{
"scripts": { "prepare": "husky install" },
"devDependencies": {
"husky": "^8.0.0",
"@commitlint/cli": "^17.0.0"
},
"lint-staged": {
"*.{js,ts}": "eslint --fix"
}
}Best Practices
最佳实践
✅ DO
✅ 建议
- Enforce pre-commit linting and formatting
- Validate commit message format
- Scan for secrets before commit
- Run tests on pre-push
- Skip hooks only with (rarely)
--no-verify - Document hook requirements in README
- Use consistent hook configuration
- Make hooks fast (< 5 seconds)
- Provide helpful error messages
- Allow developers to bypass with clear warnings
- 强制执行提交前的代码扫描与格式化
- 验证提交信息格式
- 提交前扫描敏感信息
- 推送前运行测试
- 仅在特殊情况下使用跳过钩子
--no-verify - 在README中记录钩子要求
- 使用统一的钩子配置
- 确保钩子执行速度快(<5秒)
- 提供清晰的错误提示信息
- 允许开发者绕过钩子并给出明确警告
❌ DON'T
❌ 避免
- Skip checks with
--no-verify - Store secrets in committed files
- Use inconsistent implementations
- Ignore hook errors
- Run full test suite on pre-commit
- 随意使用跳过检查
--no-verify - 在已提交的文件中存储敏感信息
- 使用不一致的钩子实现
- 忽略钩子错误
- 在提交前运行完整测试套件