一、问题背景:从5人到25人,主干开发模式崩了

去年Q3,我们团队从5人扩张到25人,直接沿用之前的trunk-based单分支开发模式。结果第一个Sprint就翻了车:4个功能并行开发,所有人都往master上推代码。合并冲突成了家常便饭,平均每天要花1.5小时解决冲突,更别提有一次把未完成的半成品代码合到了master,直接导致生产环境热修复补丁打不上——因为master已经被污染了。

我们复盘时发现三个核心痛点:
1. 无隔离性:任何人的本地未验证代码都可能被推送到公共分支;
2. 无审查节点:代码合并前没有任何强制Review机制;
3. 无发布节奏master既是开发分支又是发布分支,发布前需要人工挑选提交(cherry-pick),耗时且易漏。

二、环境与版本:技术栈与工具链

  • Git版本:2.39.1(支持git switchgit restore现代语法)
  • 代码托管:GitLab 15.11(免费版支持CODEOWNERS和Merge Request规则)
  • CI/CD:Jenkins 2.414.2 + Pipeline插件
  • 仓库规模:单仓约1200个文件,Java/Kotlin混合,Gradle构建,单次全量构建耗时6分30秒

三、方案设计:四级分支流与三态保护规则

我们结合GitFlow的稳定性与Trunk-Based的流动性,设计了四级分支:

main(生产) ← release/1.x(预发布) ← develop(集成分支) ← feature/*(功能分支)

关键策略:
- feature分支:从develop拉出,命名feature/需求ID-简述,生命周期不超过3天。
- develop分支:唯一允许合并feature的分支,必须通过MR + 2人Review。
- release分支:从develop按版本拉出,只允许修复bug(fix/*),禁止新功能。
- main分支:只接受release的合并,且必须打tag(v1.2.0格式)。

分支保护规则(GitLab Project Settings → Protected Branches):

分支 允许推送 允许合并
main 仅Maintainer 仅Maintainer,需审批
develop 仅Developer(禁止直接push,需MR) Developer需2个Approval
release/* 仅Maintainer Maintainer

这套设计把“写权限”和“合并权限”分离,杜绝了直接往developmaingit push的野蛮行为。

四、核心实现:分支流转命令与CODEOWNERS配置

4.1 标准特性开发流程

开发者日常操作如下(基于Git 2.39语法):

# 1. 同步最新develop并拉取新分支
git switch develop
git pull origin develop --rebase
git switch -c feature/LOGIN-234-sso-refactor

# 2. 开发提交(使用conventional commits规范)
git add -A
git commit -m "feat(auth): add OAuth2.0 SSO flow

- Implement AuthorizationCodeGrant
- Add refresh_token rotation
- Closes #234"

# 3. 推送并创建MR(GitLab CLI)
git push -u origin feature/LOGIN-234-sso-refactor
glab mr create --source feature/LOGIN-234-sso-refactor --target develop --title "feat: SSO重构" --assignee @reviewer1 --reviewer @reviewer2

这里有个坑:git pull --rebase 必须成为肌肉记忆。团队里新手用git pull(merge模式),导致本地产生无意义的merge commit,污染了提交历史。我们在.gitconfig里设置了pull.rebase=true强制。

4.2 CODEOWNERS文件:强制代码审查

在仓库根目录创建.gitlab/CODEOWNERS

# 全局默认审查人
* @tech-lead

# 核心模块强制双重审查
/src/core/*.java @tech-lead @senior-dev-1
/security/** @security-owner @tech-lead

# 配置文件修改必须CTO审批
/Jenkinsfile @devops-lead
/.gitlab/CODEOWNERS @cto

当MR修改了/security/auth目录下的文件,GitLab会自动要求@security-owner必须审批,其他文件至少需要1个非作者审查。我们设置合并检查规则:必须有2个Approval且流水线通过才能点Merge按钮。

4.3 CI/CD集成:Jenkins Pipeline全自动门禁

创建Jenkinsfile(声明式Pipeline),关键阶段如下:

pipeline {
    agent { docker { image 'gradle:7.6-jdk17' } }
    triggers {
        gitlab(triggerOnPush: true, triggerOnMergeRequest: true, branchFilterType: 'All')
    }
    stages {
        stage('单元测试') {
            steps {
                sh './gradlew test --tests "*UnitTest*" --max-workers=4'
            }
            post { success { junit 'build/test-results/**/*.xml' } }
        }
        stage('静态检查') {
            steps { sh './gradlew spotbugsMain detekt' }
        }
        stage('构建制品') {
            when { branch 'develop' }  // 仅develop构建镜像
            steps { sh './gradlew bootJar' }
        }
        stage('部署到预发') {
            when { branch 'release/*' }  // release分支自动部署到预发环境
            steps { sh 'kubectl set image deployment/app app=$REGISTRY_URL/app:$TAG' }
        }
    }
    post {
        failure { notifySlack(channel: '#ci-fail', color: 'danger') }
    }
}

我们优化了关键参数:
- 单元测试开启--max-workers=4,单测耗时从7分钟降到4分20秒。
- 构建阶段只在developrelease分支执行,feature分支仅跑测试和静态检查,省去了40%的构建时间
- 发布到预发环境在release/*分支上自动进行,开发环境不自动部署。

五、踩坑与优化:三个血泪教训

教训1:Release分支修bug后忘合回develop。我们release/v1.1修了个hotfix,但没合回develop,导致v1.2发布时那个bug又出现了。解决:在Jenkins Pipeline里加了自动合并步骤——release分支打tag后,自动创建MR到develop并请求Maintainer审批。

教训2:CODEOWNERS权限过严阻塞开发。最初我们把所有/src下的文件都设为必须CTO审批,结果CTO出差三天,整个团队阻塞。优化:只对/security/Jenkinsfile设强制审批,其余自动降级为“非阻塞建议”。

教训3:rebase后强制推送导致MR重复提交。有人对feature分支做git pull --rebase后,本地commit hash变了,直接git push --force导致GitLab MR里的评论全部丢失。解决:MR合并策略改为“Squash合并”,且要求开发者使用git push --force-with-lease(安全强制推送)。

性能数据:优化后,单次MR从提交到合并的平均时间为1小时47分钟(含Review等待),分支集成冲突率从每周11次降到4次(-62%)。

六、效果数据与团队反馈

推行4个月后的核心指标对比:

指标 改造前 改造后
平均解决冲突耗时/周 7.5小时 2.8小时
发布准备时间 2小时 15分钟
线上事故数(月) 3 1
新人上手成本 1周 3天

团队满意度调查中,22/25人明确表示“愿意继续使用”,反对的3人集中在希望“更自由地推送代码”的老员工。我们开了个专题会,最终通过数据说明——自由推送导致的回滚成本远高于约束成本,才达成一致。

七、总结与适用性建议

这套策略的核心不是“分支多”,而是“每个分支有明确的生命周期和所有权”。它适合10-50人的中大型业务团队,尤其是需要固定发版节奏(月度/季度)的产品。如果团队只有5人且做ToB定制项目,请直接砍掉release/*分支层,保留feature→develop→main三层即可。

最后给个铁律:任何分支的合并必须能回溯到需求单号(如MR标题含Jira号),这是除机器审批外最重要的人文约束。如果有一天你的团队不再需要人工Review就能保证代码质量,说明这套流程已经内化成代码习惯本身了。