一、问题背景:从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 switch和git 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 |
这套设计把“写权限”和“合并权限”分离,杜绝了直接往develop或main上git 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秒。
- 构建阶段只在develop和release分支执行,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就能保证代码质量,说明这套流程已经内化成代码习惯本身了。