1. 问题背景:从“能跑”到“失控”的Git之痛

2024年初,我们后端团队从5人扩张到30人,业务迭代速度翻倍。但Git仓库开始变得混乱不堪:develop分支平均每天有40+次提交,feature分支平均存活时间超过2周,Code Review经常被git push --force绕过,CI构建在develop上的失败率高达32%。最离谱的一次,一个同事把test环境的配置直接合到了main分支,导致线上事故。

我们意识到,问题不在工具,而在流程。Git本身不提供“团队协作规范”,它只提供命令。我们需要一套强约束的工作流,而不是依赖每个人的自觉。

2. 环境与版本基线

  • Git版本:2.44.0(统一要求,避免老版本对git switch等新语法支持不一致)
  • 托管平台:GitHub Enterprise Cloud(SAML SSO + 受管用户)
  • CI平台:GitHub Actions(Ubuntu 22.04 Runner)
  • 包管理器:pnpm 9.x(Monorepo结构,30+个内部包)
  • 基础分支main(生产)、develop(预发)、release/*(版本分支)

我们放弃纯GitFlow(太重,不适合快速迭代),也放弃纯trunk-based(30人直接提交主干,Review压力太大)。最终选择混合模型

  • 短生命周期特性分支feat/xxx,基于develop,必须在3天内合并。
  • 长生命周期版本分支release/2024.08.x,从develop拉出,只接受bug fix。
  • 紧急修复分支hotfix/xxx,基于main,合并后同时同步到develop

3. 方案设计:分支策略与强制规则

核心原则是:所有合并必须经过PR,所有PR必须通过自动化检查,所有检查必须包含人工Review

我们通过GitHub的branch protection rules强制执行:

  • maindevelop分支:禁止直接push,必须通过PR。
  • 要求2个approval,且CODEOWNERS文件指定的核心成员必须approve。
  • 要求所有CI check通过(包括单元测试、构建、代码扫描)。

关键设计:我们写了一个自定义的GitHub Action,在PR创建时自动检查分支命名规范和提交信息格式。

# .github/workflows/pr-validator.yml
name: PR Validator
on:
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - name: Check branch name
        run: |
          BRANCH_NAME="${{ github.head_ref }}"
          if [[ ! "$BRANCH_NAME" =~ ^(feat|fix|hotfix|release|docs|refactor)/ ]]; then
            echo "❌ 分支命名必须以 feat/ fix/ hotfix/ release/ docs/ refactor/ 开头"
            exit 1
          fi
      - name: Check commit message
        run: |
          COMMIT_MSG=$(git log --format=%s -1 "${{ github.event.pull_request.head.sha }}")
          if [[ ! "$COMMIT_MSG" =~ ^(feat|fix|hotfix|docs|refactor|chore)(\([a-z]+\))?: ]]; then
            echo "❌ 提交信息不符合 Conventional Commits 规范"
            exit 1
          fi

这个Action从根上杜绝了wipasdffix bug这类无意义提交。

4. 核心实现:Code Review流程与本地钩子

Code Review流程:我们不仅要求看代码,还要求看变更范围。用Git自带的能力生成差异报告,让Reviewer快速定位风险区。

在PR描述中,我们强制模板包含:

## 变更范围
- [ ] 数据库迁移
- [ ] API 契约变更
- [ ] 依赖升级
- [ ] 配置修改

## 影响分析
- 受影响服务:`user-service`, `order-service`
- 性能影响:新增索引`idx_order_created_at`,预计查询耗时下降40%

本地钩子:为了减轻CI压力,我们在本地强制跑lint-staged。注意,这里有一个关键细节husky在Git 2.44下需要v9+版本,否则prepare脚本会失效。

# .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged --concurrent false
// package.json
{
  "lint-staged": {
    "*.{ts,tsx}": [
      "eslint --fix --max-warnings=0",
      "prettier --write",
      "git add"
    ],
    "*.{json,md,yaml}": [
      "prettier --write",
      "git add"
    ]
  }
}

这里有个坑:最开始我们设置了concurrent: true,导致ESLint和Prettier同时修改文件,产生大量冲突。改为false后,按顺序执行,冲突率下降90%。

5. CI/CD集成:一条流水线,三个环境

我们的CI流水线设计为三段式test → build → deploy。在GitHub Actions中,通过environment实现环境级保护。

# .github/workflows/ci.yml (核心片段)
name: CI Pipeline

on:
  pull_request:
    branches: [develop, main]
  push:
    branches: [develop, main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # 关键:拉取全部历史,用于增量测试
      - uses: pnpm/action-setup@v4
        with:
          version: 9.12.0
      - name: Install dependencies
        run: pnpm install --frozen-lockfile
      - name: Run unit tests with coverage
        run: pnpm test -- --coverage --maxWorkers=2
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          fail_ci_if_error: true

  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Build all packages
        run: pnpm build
      - name: Check bundle size
        run: |
          MAX_SIZE_KB=500
          BUNDLE_SIZE=$(du -sk dist | cut -f1)
          if [ "$BUNDLE_SIZE" -gt "$MAX_SIZE_KB" ]; then
            echo "❌ Bundle size $BUNDLE_SIZE KB exceeds limit $MAX_SIZE_KB KB"
            exit 1
          fi

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment: production  # 这里关联GitHub Environment保护规则
    if: github.ref == 'refs/heads/main'
    steps:
      - name: Deploy to AWS ECS
        run: |
          aws ecs update-service --cluster production --service api-gateway --force-new-deployment

踩坑记录fetch-depth: 0一开始没加,导致pnpm在Monorepo下无法正确计算变更包,--filter参数失效。加了之后,增量构建时间从4分30秒降到1分50秒。

6. 效果数据与总结

重构三个月后,数据对比如下:

指标 重构前 重构后 提升
develop分支CI通过率 68% 94% +26%
平均PR存活时间 3.2天 1.1天 -65%
代码冲突次数/周 15次 3次 -80%
线上事故次数/季度 4次 1次 -75%

最大的感悟:技术债不只是代码里的,流程债更可怕。Git工作流不是选一个模板就完事,而是要根据团队节奏、发布频率、人员水平做定制裁剪。我们的混合模型不一定适合所有人,但强约束自动化的方向一定是对的——把规则写进CI,而不是指望人记住。

最后给同行的建议:如果你的团队超过15人,请立刻检查你的branch protection rules是否覆盖了所有受保护分支,你的CI是否在PR阶段就跑全量测试,你的Review是否只看了Files changed而忽略了Commits。这些细节,决定了你是在用Git,还是被Git用。