Git钩子与自动化:让代码质量自动守护

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

流程:

  1. git commit触发pre-commit
  2. lint-staged检查暂存的文件
  3. ESLint/Prettier自动修复
  4. 修复后的文件重新add
  5. 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多分支并行

下一期,我们将进入实战环节——常见坑与解决方案。你会学到如何处理那些让新手崩溃的场景。


练习任务

  1. 为你的项目配置Husky + lint-staged
  2. 配置commitlint,尝试提交不规范信息(应该被拒绝)
  3. 配置一个Git别名,一键查看美化日志
  4. 用git bisect找一个故意引入的bug

下期预告:《Git实战:常见坑与解决方案》—— 误删分支怎么办?提交到错误分支怎么救?大文件怎么处理?

Views: 0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Index