**
1. 问题背景:当小团队的“随意合并”撞上20人协作
我们团队早期只有5个人,Git用法极其随意:所有人往master推,冲突了当面吼一声。后来产品线扩张,前端、后端、测试、运维加起来20人,代码库从一个拆成四个。问题集中爆发:
- 分支污染:
master上经常出现未完成的功能代码,导致预发环境部署失败。 - Code Review形同虚设:MR(Merge Request)平均只有0.7条评论,很多直接self-merge。
- CI失败率高:单元测试和构建脚本在本地能过,推到远端就挂,失败率37%,大家开始忽略CI红叉。
- 回滚困难:一次上线出问题,发现
master里混了三个功能的提交,无法单独revert。
我们决定不再“先污染后治理”,而是设计一套可强制执行的Git工作流。
2. 环境与版本
- GitLab CE 16.8(自托管)
- Git 2.40.1
- GitLab Runner 16.8,Docker executor
- SonarQube 10.3(代码质量门禁)
- 项目语言:Java 17 + Maven 3.9.6,前端Vue 3 + Vite 5
- 分支保护规则:GitLab push rules + merge request approvals
3. 方案设计:改良Git Flow + 短周期Release分支
我们参考了Git Flow和Trunk-Based,最终选择改良Git Flow,因为团队需要同时维护线上热修复和多个功能并行。
分支类型与生命周期:
| 分支 | 命名 | 来源 | 合并目标 | 生命周期 |
|---|---|---|---|---|
| main | main |
- | - | 永久 |
| develop | develop |
main | - | 永久 |
| feature | feature/JIRA-123-xxx |
develop | develop | ≤5天 |
| release | release/v1.4.0 |
develop | main + develop | 3-7天 |
| hotfix | hotfix/v1.3.1 |
main | main + develop | ≤1天 |
关键约束:
- main和develop禁止直接push,必须通过MR。
- MR必须满足:至少1个Approval、CI流水线全绿、SonarQube质量门禁通过、无冲突。
- feature分支超过5天未合并则强制rebase develop,避免大爆炸冲突。
4. 核心实现:配置与代码
4.1 GitLab分支保护与MR审批配置
在GitLab项目 → Settings → Repository → Protected branches:
main:Allowed to merge = Maintainers,Allowed to push = No onedevelop:Allowed to merge = Developers + Maintainers,Allowed to push = No one
Merge request approvals:
- Approvals required:1
- 勾选“Prevent approval by author”
- 勾选“Prevent editing approval rules in merge requests”
- 勾选“Remove all approvals when commits are added”
4.2 .gitlab-ci.yml 核心片段
我们采用分阶段流水线:validate → build → test → sonar → deploy-review。以下为可运行的精简版:
# .gitlab-ci.yml
stages:
- validate
- build
- test
- sonar
- deploy
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
SONAR_HOST_URL: "https://sonar.internal.com"
SONAR_TOKEN: $SONAR_TOKEN_CI
cache:
key: "$CI_COMMIT_REF_SLUG"
paths:
- .m2/repository/
validate:branch-name:
stage: validate
script:
- |
if [[ ! "$CI_COMMIT_REF_NAME" =~ ^(feature|release|hotfix)/[A-Z]+-[0-9]+-.*$ ]] && \
[[ "$CI_COMMIT_REF_NAME" != "develop" ]] && [[ "$CI_COMMIT_REF_NAME" != "main" ]]; then
echo "分支名不符合规范: $CI_COMMIT_REF_NAME"
exit 1
fi
only:
- merge_requests
- branches
build:java:
stage: build
image: maven:3.9.6-eclipse-temurin-17
script:
- mvn clean compile -DskipTests
artifacts:
paths:
- target/classes/
expire_in: 1 day
test:unit:
stage: test
image: maven:3.9.6-eclipse-temurin-17
script:
- mvn test -Dtest=*UnitTest
coverage: '/Total.*?([0-9]{1,3})%/'
artifacts:
when: always
reports:
junit:
- target/surefire-reports/TEST-*.xml
sonar:scan:
stage: sonar
image: maven:3.9.6-eclipse-temurin-17
script:
- mvn sonar:sonar
-Dsonar.projectKey=my-team-app
-Dsonar.host.url=$SONAR_HOST_URL
-Dsonar.login=$SONAR_TOKEN
-Dsonar.qualitygate.wait=true
allow_failure: false
only:
- merge_requests
- develop
- main
deploy:review:
stage: deploy
image: bitnami/kubectl:1.29
script:
- kubectl set image deployment/app-review app=registry.internal.com/app:$CI_COMMIT_SHORT_SHA -n review
- kubectl rollout status deployment/app-review -n review --timeout=120s
environment:
name: review/$CI_COMMIT_REF_SLUG
url: https://$CI_COMMIT_REF_SLUG.review.internal.com
only:
- merge_requests
关键参数说明:
- sonar.qualitygate.wait=true:强制阻塞流水线直到质量门禁返回结果,避免“先合并后修复”。
- coverage正则提取覆盖率,低于80%时SonarQube门禁失败。
- deploy:review只在MR时触发,为每个MR创建独立预览环境。
4.3 MR模板与自动检查
在仓库根目录创建.gitlab/merge_request_templates/Default.md:
## 变更类型
- [ ] Feature
- [ ] Bugfix
- [ ] Hotfix
- [ ] Refactor
## 关联JIRA
JIRA-XXX
## 自测清单
- [ ] 本地单元测试通过
- [ ] 新增代码覆盖率 ≥ 80%
- [ ] 无SonarQube blocker/critical
- [ ] 已更新API文档(如涉及)
## 影响范围
## 回滚方案
同时配置GitLab push rule:Commit message必须匹配^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,50},否则拒绝push。
5. 踩坑与优化
坑1:SonarQube质量门禁导致MR长时间挂起。
我们最初设置sonar.qualitygate.wait=true,但Sonar扫描排队时MR会卡住。优化:将Sonar扫描拆到独立stage,并设置timeout: 15m,超时后标记为warning而非失败(但主分支仍强制阻塞)。
坑2:feature分支rebase后CI缓存失效。
因为cache key用了$CI_COMMIT_REF_SLUG,rebase后slug不变但commit变了,Maven依赖缓存命中率下降。改为key: "$CI_PROJECT_ID-$CI_COMMIT_REF_SLUG"并加上policy: pull-push,命中率从62%提升到91%。
坑3:hotfix合并回develop时冲突频繁。
原因是hotfix从main拉出,而develop已经前进了很多。我们强制hotfix合并到main后,立即用git cherry-pick将hotfix commit应用到develop,而不是直接merge,减少冲突面。
坑4:Reviewer只看代码不看CI结果。
在MR描述顶部加了一行自动评论:CI Pipeline: $CI_PIPELINE_URL | Sonar: $SONAR_URL,Reviewer点开就能看到质量报告,Review时长从4.2h降到1.1h。
6. 效果数据
运行三个月后(统计周期:2024-06 ~ 2024-08):
| 指标 | 改造前 | 改造后 |
|---|---|---|
| MR平均Review时长 | 4.2小时 | 1.1小时 |
| CI流水线失败率 | 37% | 8% |
| 生产环境回滚次数 | 5次/月 | 1次/月 |
| 单元测试覆盖率 | 54% | 82% |
| 分支平均生命周期 | 11天 | 3.5天 |
| SonarQube blocker数 | 23 | 0 |
最明显的变化是:以前上线前夜全员加班修CI,现在MR一提交,流水线自动跑,Reviewer只关注业务逻辑,部署预览环境直接给产品经理验收。
7. 总结
Git工作流不是越复杂越好,而是约束要能自动执行。我们这套方案的核心就三件事:
- 分支命名和生命周期用CI强制检查,不靠自觉。
- Code Review必须绑定CI结果和Sonar门禁,否则Reviewer没有依据。
- 每个MR都有独立预览环境,让测试和产品提前介入,减少合并后返工。
如果你团队也在20人左右,建议先从main+develop保护 + MR模板 + 基础CI流水线开始,跑通后再加Sonar和预览环境。别一上来就搞全套,容易把团队逼疯。