1. 问题背景:多分支协作的混乱与代价

在2023年初,我们团队采用经典的Git Flow模型:develop、feature、release、hotfix四类分支。但随着团队从8人扩张到20人,问题爆发:

  • 分支膨胀:同时活跃的feature分支超过15个,合并冲突每周平均出现12次,解决冲突平均耗时4.5小时
  • 发布延迟:release分支合并后,由于代码差异过大(平均滞后develop分支3天),集成测试失败率40%
  • Code Review流于形式:PR堆积超过48小时未审核,开发者为赶进度“强行合并”

数据对比(改前一个月):线上事故8起,其中5起是合并导致的回归。

目标:将发布周期压缩到1天以内,冲突率降低70%,Review时效控制在4小时内。

2. 环境与版本

组件 版本 用途
Git 2.39.2 核心版本控制
Gerrit 3.8.0 Code Review与门禁
Jenkins 2.440.1 CI/CD流水线
Trivy 0.49.0 镜像安全扫描
Docker 24.0.6 构建与部署
Kubernetes 1.28.3 生产部署

分支模型选择:经过调研,放弃Git Flow,改用Trunk-based Development(主干开发)配合短生命周期特性分支。原因:Git Flow的长周期分支(develop、release)引入大量延迟,而Trunk-based + 特性开关能实现持续集成。

3. 方案设计:三阶段工作流

3.1 分支策略(Trunk-based with Feature Flags)

  • master:唯一长期分支,始终可部署。每次提交必须通过CI/CD全链路测试
  • feature/xxx:短生命周期分支(存活=80%)
  • 分配给2名Reviewer,必须在4小时内完成审查
  • Reviewer通过后,需要CI/CD流水线再次验证(包括集成测试、安全扫描)
  • 最后通过“提交+1”按钮合并到master

关键配置:Gerrit的project.config中设置:

[access "refs/heads/master"]
    push = +2 group CI_Service
    push = +1 group Developers
    label-Code-Review = -2..+2 group Developers
    label-Verified = -1..+1 group CI_Service
    submit = merge if no change

3.3 CI/CD集成(Jenkins Pipeline + 自动门禁)

每个push到Gerrit的Change,触发Jenkins的Multibranch Pipeline。流水线包含:

  • Stage 1: 代码质量 → lint, security scan (Trivy)
  • Stage 2: 单元测试 → pnpm test --coverage,覆盖率0则失败)
  • Stage 6: 自动合并 → 当Reviewer给出+2且Jenkins通过,Gerrit自动merge

4. 核心实现(含可运行代码)

4.1 Jenkinsfile(声明式流水线)

pipeline {
    agent any
    triggers {
        gerritTrigger(
            gerritProjects: [[
                compareType: 'PLAIN',
                pattern: 'my-project'
            ]],
            triggerOnPatchsetUploaded: true,
            skipVote: false
        )
    }
    environment {
        REGISTRY = 'registry.example.com'
        IMAGE_NAME = "${REGISTRY}/my-app:${env.GERRIT_PATCHSET_REVISION}"
    }
    stages {
        stage('Lint & Security') {
            parallel {
                stage('ESLint') { steps { sh 'pnpm lint' } }
                stage('Trivy') { steps { sh 'trivy fs --severity CRITICAL --exit-code 1 .' } }
            }
        }
        stage('Unit Tests') {
            steps {
                sh 'pnpm test --coverage'
                junit 'test-results/*.xml'
            }
        }
        stage('Build Docker') {
            steps {
                sh "docker build -t ${IMAGE_NAME} ."
            }
        }
        stage('Integration Tests') {
            steps {
                sh "kubectl set image deployment/my-app-test my-app=${IMAGE_NAME} -n test"
                sh 'cypress run --config baseUrl=http://my-app-test.namespace.svc'
            }
        }
        stage('Gerrit Vote') {
            steps {
                // 给Gerrit Change打上Verified+1
                gerritReview(
                    vote: 'verified',
                    reviewLabel: 'Code-Review',
                    category: 'Verified',
                    value: 1
                )
            }
        }
    }
    post {
        failure {
            gerritReview(
                vote: 'verified',
                category: 'Verified',
                value: -1
            )
        }
    }
}

4.2 Gerrit钩子脚本(merge前强制检查)

在Gerrit服务器hooks/commit-msg中添加逻辑,阻止没有关联JIRA Issue的提交:

#!/bin/bash
# 要求commit message包含JIRA编号
MSG_FILE=$1
if ! grep -qE '\[(PROJ|TASK)-\d+\]' "$MSG_FILE"; then
    echo "ERROR: Commit message must contain JIRA issue (e.g., [PROJ-123])" >&2
    exit 1
fi
# 检查是否包含变更描述
if [ $(wc -l &2
    exit 1
fi
exit 0

部署钩子:将脚本放在/hooks/commit-msg,并赋予执行权限。

5. 踩坑与优化

坑1:Gerrit + Jenkins的“死亡循环”

现象:每次CI通过后,Jenkins再次触发自己(因为Gerrit Change状态变更)。解决:在Jenkinsfile中增加skipBuildIfMessage过滤:

when {
    beforeAgent true
    expression { return !env.GERRIT_CHANGE_COMMIT_MESSAGE?.contains('[CI SKIP]') }
}

坑2:Trivy扫描导致构建失败太频繁

初期设置--severity HIGH,结果每天有3-4次构建因基础镜像漏洞阻塞。调整为:CRITICAL才阻断,HIGH仅记录通知。

坑3:短分支存活时间太短导致Review堆积

强制feature分支存活不超过48小时,但Reviewer不够。优化:引入“Review轮值日历”,每人每天处理不超过3个Review,并设置Slack机器人提醒。

性能优化:并行化CI

将Lint、安全扫描、单元测试改为并行Stage(如上Jenkinsfile所示),单次流水线时间从18分钟降到7分钟。

6. 效果数据(改后3个月)

指标 改前 改后 变化
每周冲突次数 12次 3.2次 ↓73%
平均冲突解决时间 4.5h 1.1h ↓75%
发布周期 3天 4小时 ↓87%
Code Review时效 >48h 3.2h ↓93%
线上故障率 8次/月 3次/月 ↓62%
开发效率(人均PR/周) 3.1个 5.8个 ↑87%

7. 总结与建议

Trunk-based + 短分支 + 自动门禁的组合,本质是把集成负担从“人”转移到“自动化流水线”。关键原则:

  1. 分支寿命功能分支:永远不要在master外停留超过2天

如果团队规模超过30人,可以考虑引入GitHub Actions / GitLab CI替代Gerrit(Gerrit的UI对新人不够友好),但核心分支模型和门禁逻辑可以复用。

最后送一句:“Master is always deployable”——这不是口号,是流水线强制的结果。