Git钩子与自动化:让代码质量自动守护
前三期我们掌握了Git的基础操作、分支管理和团队协作。现在,让我们把效率提升到下一个层次——自动化。
Git钩子(Hooks)是Git在特定事件发生时自动执行的脚本。比如,每次commit前自动检查代码格式,每次push前自动运行测试,每次merge后自动部署。
这一期,我们深入Git的自动化世界。
Git钩子基础
钩子存放在.git/hooks/目录:
ls -la .git/hooks/
你会看到很多.sample文件,这是示例。去掉.sample后缀就激活了。
钩子类型
客户端钩子(本地执行):
pre-commit:commit前,检查代码prepare-commit-msg:commit message生成后,编辑器启动前commit-msg:commit message写完后,检查格式post-commit:commit完成后,发通知pre-push:push前,跑测试post-checkout:切换分支后,安装依赖
服务端钩子(远程执行):
pre-receive:push到达服务器时update:每个分支更新时post-receive:push完成后
第一个钩子
创建一个简单的pre-commit钩子,检查是否有console.log:
# .git/hooks/pre-commit
#!/bin/bash
# 检查暂存区的JS文件
files=$(git diff --cached --name-only --filter=ACM | grep '\.js$')
if [ -z "$files" ]; then
exit 0
fi
# 检查是否有console.log
if grep -n "console\.log" $files; then
echo "❌ 发现console.log,请移除后再提交"
exit 1
fi
exit 0
chmod +x .git/hooks/pre-commit
现在,如果有console.log,commit会被拒绝。
Husky:现代钩子管理
直接编辑.git/hooks/有几个问题:
- 钩子不会被Git跟踪,队友无法共享
- 每次clone后需要手动设置
- 脚本管理混乱
Husky解决了这些问题。
安装
npm install husky --save-dev
# 初始化(自动创建.husky目录)
npx husky init
配置pre-commit
# 创建pre-commit钩子
echo "npm test" > .husky/pre-commit
现在每次commit前会自动运行测试。测试失败,commit就被拒绝。
配置commit-msg
验证提交信息格式:
# .husky/commit-msg
#!/bin/bash
msg=$(cat $1)
# 检查是否符合约定式提交
if ! echo "$msg" | grep -qE "^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,}"; then
echo "❌ 提交信息格式错误"
echo "格式: (): "
echo "示例: feat(auth): 添加OAuth登录"
exit 1
fi
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
lint-staged:只检查暂存文件
全量检查太慢?lint-staged只检查你改动的文件。
npm install lint-staged --save-dev
// package.json
{
"lint-staged": {
"*.js": [
"eslint --fix",
"prettier --write"
],
"*.css": [
"stylelint --fix",
"prettier --write"
],
"*.{json,md}": [
"prettier --write"
]
}
}
# .husky/pre-commit
npx lint-staged
流程:
git commit触发pre-commit- lint-staged检查暂存的文件
- ESLint/Prettier自动修复
- 修复后的文件重新add
- commit继续
commitlint:提交信息规范
npm install @commitlint/cli @commitlint/config-conventional --save-dev
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat',
'fix',
'docs',
'style',
'refactor',
'perf',
'test',
'build',
'ci',
'chore',
'revert'
]
],
'subject-case': [2, 'always', 'lower-case'] // 主题小写
}
};
现在,不符合规范的提交会被拒绝:
git commit -m "添加功能"
# ❌ 提交失败:必须以type开头
git commit -m "feat: 添加用户登录功能"
# ✅ 提交成功
完整工作流配置
一个现代化的前端项目配置:
// package.json
{
"scripts": {
"prepare": "husky install",
"lint": "eslint . --ext .js,.ts,.tsx",
"format": "prettier --write .",
"test": "jest"
},
"lint-staged": {
"*.{js,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{css,scss,json,md}": [
"prettier --write"
]
},
"devDependencies": {
"@commitlint/cli": "^18.0.0",
"@commitlint/config-conventional": "^18.0.0",
"eslint": "^8.0.0",
"husky": "^9.0.0",
"lint-staged": "^15.0.0",
"prettier": "^3.0.0"
}
}
# .husky/pre-commit
npx lint-staged
# .husky/commit-msg
npx --no -- commitlint --edit $1
# .husky/pre-push
npm test
// commitlint.config.js
module.exports = { extends: ['@commitlint/config-conventional'] };
一次clone后,所有开发者都会自动拥有相同的检查规则。
Git别名:效率提升
配置常用别名,减少输入:
# 基础别名
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
# 查看日志
git config --global alias.lg "log --oneline --graph --all"
git config --global alias.last 'log -1 HEAD'
# 撤销
git config --global alias.unstage 'reset HEAD --'
git config --global alias.undo 'checkout HEAD --'
# 有用的别名
git config --global alias.stash-all 'stash save --include-untracked'
git config --global alias.push-force 'push --force-with-lease'
使用:
git st # = git status
git lg # 美观的日志图
git unstage file.txt # 取消暂存
自定义Git命令
创建git-xxx脚本,就能用git xxx调用:
# ~/bin/git-publish
#!/bin/bash
# 发布当前分支到远程
branch=$(git branch --show-current)
git push -u origin $branch
chmod +x ~/bin/git-publish
export PATH=$PATH:~/bin
# 使用
git publish
自动部署
post-receive钩子(服务端)
在服务器上配置自动部署:
# /var/git/project.git/hooks/post-receive
#!/bin/bash
TARGET="/var/www/project"
GIT_DIR="/var/git/project.git"
while read oldrev newrev ref
do
BRANCH=$(git rev-parse --symbolic --abbrev-ref $ref)
if [ "$BRANCH" = "main" ]; then
echo "Deploying main branch..."
git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f main
cd $TARGET
npm install --production
pm2 restart project
echo "Deploy complete."
fi
done
现在,push到main分支会自动部署。
GitHub Actions自动部署
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm run build
- name: Deploy to server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /var/www/project
git pull
npm ci --production
pm2 restart project
Vercel/Netlify自动部署
最简单的方式:连接GitHub仓库,自动部署。
- push到main → 自动构建 → 自动部署
- PR预览 → 每个PR都有独立预览链接
- 回滚 → 点一下就回退
Git bisect:定位问题提交
当项目出现bug,但不知道是哪个提交引入的:
# 开始二分查找
git bisect start
# 标记当前提交有问题
git bisect bad
# 标记某个旧提交是正常的
git bisect good v1.0.0
# Git会自动跳到中间的提交
# 你测试后标记
git bisect good # 或 git bisect bad
# 重复直到找到问题提交
# Git会告诉你哪个提交有问题
# 结束
git bisect reset
自动化:
# 自动运行测试脚本来判断
git bisect run npm test
Git worktree:多分支并行开发
需要同时在多个分支工作,但不想频繁切换?
# 创建worktree
git worktree add ../project-feature feature-branch
# 现在有两个工作目录
# ../project (main分支)
# ../project-feature (feature-branch)
# 在project-feature中工作
cd ../project-feature
# 修改、提交...
# 完成后删除worktree
git worktree remove ../project-feature
小结
你现在掌握了Git自动化的核心技能:
- Git钩子原理和常用钩子
- Husky + lint-staged + commitlint现代工作流
- 配置Git别名提升效率
- 服务端自动部署
- GitHub Actions CI/CD
- Git bisect定位问题
- Git worktree多分支并行
下一期,我们将进入实战环节——常见坑与解决方案。你会学到如何处理那些让新手崩溃的场景。
练习任务:
- 为你的项目配置Husky + lint-staged
- 配置commitlint,尝试提交不规范信息(应该被拒绝)
- 配置一个Git别名,一键查看美化日志
- 用git bisect找一个故意引入的bug
下期预告:《Git实战:常见坑与解决方案》—— 误删分支怎么办?提交到错误分支怎么救?大文件怎么处理?
Views: 0
