一、问题背景:从15人到40人,Git协作开始失控

2023年Q3,我们团队从15人扩张到40人,产品迭代节奏从双周发版加速到每日发版。最初的master直接开发模式彻底崩溃——高峰期每天有30+个提交直接推到master,代码冲突像俄罗斯方块一样堆叠。

具体痛点:
- 每周平均发生23次合并冲突,解决耗时约4.2小时/人周
- master分支随时处于不可部署状态,发布前需要冻结代码2天
- Code Review形同虚设,因为所有人都在赶工,PR平均存活时间超过3天

我意识到必须重构Git工作流。核心目标是:让每个提交都经过审查,让主分支永远可部署

二、环境与版本:我们用的具体工具链

  • Git:2.39.2(重点用到了git switchgit restore的交互式提示)
  • GitLab:15.11.3(Community Edition,我们没用EE的高级功能)
  • Jenkins:2.414.2,搭配Blue Ocean插件
  • 语言栈:Java 17 + Spring Boot 3.1,但工作流本身语言无关
  • 团队规模:40人,分为8个特性小组(每个组5人)

三、方案设计:Trunk-based + 短生命周期分支

我们最终采用了Trunk-based Development变体,但保留Pull Request用于代码审查——这是GitHub Flow和GitLab Flow的混合体。

核心规则:
1. main分支永远是稳定可部署的,任何时刻都可以一键发布
2. 每个功能/修复从main切出短生命周期分支,命名规范:feature/【JIRA单号】-【简要描述】fix/【JIRA单号】-【修复内容】
3. 分支存活时间严格限制在2个工作日内(超时自动告警)
4. 必须通过CI流水线+至少1人Code Review才能合并
5. 合并采用--squash策略,保持main历史线性

我们特意没有develop分支——那会增加合并层级,让问题延迟暴露。

# 分支命名示例
feature/PLAT-2345-add-rate-limiter
fix/PLAT-2389-fix-npe-on-user-login

四、核心实现:钩子脚本 + CI流水线 + 合并规则

4.1 pre-push钩子:强制本地检查

团队每个成员必须安装这个钩子(放在.git/hooks/pre-push),它会在git push前运行本地测试和lint:

#!/bin/bash
# .git/hooks/pre-push - 本地质量门禁
echo "🔍 Running pre-push checks..."

# 1. 检查分支命名
BRANCH_NAME=$(git branch --show-current)
if [[ ! $BRANCH_NAME =~ ^(feature|fix|hotfix)/[A-Z]+-[0-9]+- ]]; then
    echo "❌ 分支命名不符合规范: $BRANCH_NAME"
    echo "   请使用格式: feature/PLAT-1234-short-desc"
    exit 1
fi

# 2. 运行单元测试(跳过集成测试,CI会做)
echo "🧪 Running unit tests..."
./mvnw test -Dtest=*UnitTest -DfailIfNoTests=false
if [ $? -ne 0 ]; then
    echo "❌ 单元测试失败,禁止推送"
    exit 1
fi

# 3. 检查是否有未解决的冲突标记
if grep -rn '^<<<<<<< HEAD' --include="*.java" --include="*.xml" . | head -5; then
    echo "❌ 发现未解决的冲突标记"
    exit 1
fi

echo "✅ Pre-push checks passed. Ready to push."

4.2 Jenkins CI配置:自动化验证

我们在Jenkinsfile中定义了流水线,每次push都会触发构建。关键点是并行执行单元测试和静态代码分析:

pipeline {
    agent { label 'docker' }

    options {
        timeout(time: 15, unit: 'MINUTES')
        buildDiscarder(logRotator(numToKeepStr: '20'))
    }

    triggers {
        // GitLab webhook 自动触发
        gitlab(triggerOnPush: true, triggerOnMergeRequest: true)
    }

    stages {
        stage('Parallel Verification') {
            parallel {
                stage('Unit Tests') {
                    steps {
                        sh './mvnw test -DskipITs'
                    }
                }
                stage('SonarQube Scan') {
                    steps {
                        sh './mvnw sonar:sonar \
                            -Dsonar.projectKey=${PROJECT_KEY} \
                            -Dsonar.host.url=${SONAR_HOST} \
                            -Dsonar.qualitygate.wait=true'
                    }
                }
                stage('Dependency Check') {
                    steps {
                        sh './mvnw org.owasp:dependency-check-maven:check \
                            -DfailBuildOnCVSS=7'
                    }
                }
            }
        }
        stage('Integration Tests') {
            when { branch 'main' }  // 只有合并到main才跑完整集成测试
            steps {
                sh './mvnw verify -DskipUnitTests'
            }
        }
    }

    post {
        success { 
            // 给GitLab MR发成功评论
            updateGitlabCommitStatus(name: 'build', state: 'success')
        }
        failure {
            updateGitlabCommitStatus(name: 'build', state: 'failed')
        }
    }
}

4.3 GitLab Code Review规则

在GitLab项目设置中,我们配置了以下审批规则:

审批规则:
- 至少1人审批(非作者本人)
- 审批人必须是指定CODEOWNERS中的成员
- 禁用“通过合并按钮直接推送”权限
- 合并前必须通过所有CI流水线
- 合并且删除源分支(强制)

.gitlab/CODEOWNERS文件示例:

# 核心模块需要架构师审批
/src/main/java/com/platform/core/** @backend-architect
# 数据库迁移文件需要DBA审批
/db/migrations/** @dba-team
# 其他默认团队负责人
* @backend-lead

五、踩坑与优化:我们经历过的真实问题

坑1:Squash合并的副作用
用了--squash后,git bisect几乎失效——因为每个提交都是一个巨大的变更。优化方案:要求PR描述中必须包含测试步骤和回滚方案,并在MR模板中加入固定格式。

坑2:Code Review阻塞
早期要求3人审批,导致PR排队严重。我们分析数据发现,40%的审批都是“LGTM”式走过场。优化方案:
- 改为1人审批(必须是CODEOWNERS)
- 设置SLA:24小时内未审批自动提醒,36小时未审批自动指派给备份审批人
- 引入“小PR原则”:单个PR变更文件不超过15个,代码行数不超过800行

坑3:CI并发山洪
40人同时push,Jenkins瞬间启动80+个构建任务,导致资源耗尽。优化方案:
- 限制并发构建为4个
- 使用--build-once-per-commit参数
- 将集成测试拆分为独立流水线,只在合并到main时运行

优化效果数据(对比重构前后3个月)

指标 重构前 重构后
平均合并等待时间 47分钟 8分钟
每天成功合并次数 12次 19次
合并冲突率 23次/周 5次/周
线上紧急修复次数 4次/月 1次/月
平均代码审查覆盖 60% 97%

六、总结:Git工作流是持续演进的过程

这套方案我们运行了6个月,效果显著。但要注意:没有银弹。我们团队下一步计划引入git worktree来优化分支切换开销,并尝试用AI代码审查工具辅助人工审查。

最后给三个实用建议:
1. 分支生命周期是你的第一道防线——超过3天的分支必须拆解
2. CI不通过就不允许合并,这条要写进团队契约
3. 定期分析Git仓库指标(用git log --format和自建脚本),用数据驱动流程改进

如果你也在为团队协作头疼,建议先从“减少分支存活时间”和“强制Code Review”开始,这两点收益最大,改动最小。