**

一、问题背景:为什么我们需要规范Git工作流?

三年前,我所在团队只有5个人,大家都在master分支上直接提交,偶尔拉个dev分支。那时候发布靠手动打Tag,冲突了就本地Merge一下,日子还能过。

但随着团队扩张到22人,微服务拆分出6个仓库,问题爆发了:
1. 环境错乱:A同学把未测试完的代码合并到master,B同学直接基于master打包发到了预发环境,导致预发环境挂了3小时。
2. Code Review形同虚设:虽然规定要MR(Merge Request),但为了赶进度,大家习惯性自己点“Merge”,导致线上代码风格五花八门,SQL注入漏洞在第4次迭代才被发现。
3. CI/CD掉链子:每次合并到master触发Jenkins构建,平均耗时12分钟,且经常因为环境变量冲突导致构建失败,部署成功率只有72%。

痛定思痛,我们决定重新设计Git工作流。目标很明确:既要保证发布稳定性,又不能把开发效率拖垮。

二、环境与版本

在开始配置前,先明确我们的技术栈版本(不同版本配置语法差异较大):
- Git: 2.40.1 (启用了 feature.manyFiles 和 index.version=4 以加速大仓库操作)
- 代码托管: GitLab CE 16.8.2 (自带CI/CD,但我们也接入了外部Jenkins)
- CI/CD: Jenkins 2.426.1 + GitLab Plugin 1.7.16 + SonarQube Scanner 5.0
- 制品库: Nexus 3.58
- 代码质量: SonarQube 10.3

三、方案设计:混合分支模型

纯Git Flow太重,纯Trunk-Based对测试覆盖率要求极高(需>80%)。我们采用了 “简化版Git Flow + 短生命周期Feature分支” 的混合模式:

分支名 生命周期 来源 合并目标 说明
main 永久 - - 与生产环境完全一致,每次合并打Tag
release/* 2周 main main 发布候选,只修Bug不加Feature
feature/* sonar -> build。注意这里使用了rules替代过时的only/except。
# .gitlab-ci.yml
stages:
  - test
  - sonar
  - build

variables:
  MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
  SONAR_HOST_URL: "http://sonarqube.internal:9000"

# 缓存加速
cache:
  key: "$CI_COMMIT_REF_SLUG"
  paths:
    - .m2/repository/

unit-test:
  stage: test
  image: maven:3.9.6-eclipse-temurin-17
  script:
    - mvn clean test -Dspring.profiles.active=test
  artifacts:
    when: always
    reports:
      junit:
        - target/surefire-reports/TEST-*.xml
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_BRANCH == "main"'

sonar-scan:
  stage: sonar
  image: sonarsource/sonar-scanner-cli:5.0
  dependencies:
    - unit-test
  script:
    - sonar-scanner
      -Dsonar.projectKey=$CI_PROJECT_PATH_SLUG
      -Dsonar.qualitygate.wait=true
      -Dsonar.gitlab.commit_sha=$CI_COMMIT_SHA
      -Dsonar.gitlab.ref_name=$CI_COMMIT_REF_NAME
  allow_failure: false
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

build-image:
  stage: build
  image: docker:24.0.7
  services:
    - docker:24.0.7-dind
  script:
    - docker build -t registry.internal/$CI_PROJECT_PATH:$CI_COMMIT_SHORT_SHA .
    - docker push registry.internal/$CI_PROJECT_PATH:$CI_COMMIT_SHORT_SHA
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      when: manual  # 需要手动点击部署,防止误触发

关键点:sonar.qualitygate.wait=true 让流水线同步等待SonarQube结果,如果不达标,Pipeline直接失败,MR无法合并。

3. Jenkins 集成配置(针对老旧系统)

部分遗留系统无法迁移GitLab CI,我们通过Jenkins的Multibranch Pipeline实现类似逻辑。以下是Jenkinsfile片段:

// Jenkinsfile
pipeline {
    agent any
    tools {
        maven 'Maven-3.9.6'
        jdk 'JDK-17'
    }
    triggers {
        // 每分钟轮询GitLab,实际生产建议用Webhook
        pollSCM('* * * * *')
    }
    stages {
        stage('Checkout & Build') {
            steps {
                checkout scm
                sh 'mvn clean compile -DskipTests'
            }
        }
        stage('Code Review Gate') {
            steps {
                // 调用GitLab API检查MR的Approve状态
                script {
                    def response = sh(
                        script: "curl -s --header 'PRIVATE-TOKEN: ${GITLAB_TOKEN}' '${GITLAB_URL}/api/v4/projects/${PROJECT_ID}/merge_requests/${CHANGE_ID}/approvals'",
                        returnStdout: true
                    )
                    def json = readJSON text: response
                    if (json.approved_by.size() < 1) {
                        error "Code Review未通过,至少需要1人Approve!当前Approve数: ${json.approved_by.size()}"
                    }
                }
            }
        }
        stage('SonarQube Analysis') {
            steps {
                withSonarQubeEnv('SonarQube-Server') {
                    sh 'mvn sonar:sonar -Dsonar.qualitygate.wait=true'
                }
            }
        }
        stage('Deploy to Staging') {
            when {
                branch 'main'
            }
            steps {
                sh 'kubectl apply -f k8s/staging/ --record'
            }
        }
    }
    post {
        failure {
            // 钉钉通知
            sh "curl -X POST -H 'Content-Type: application/json' -d '{\"msgtype\": \"text\", \"text\": {\"content\": \"构建失败: ${env.JOB_NAME} - ${env.BUILD_NUMBER}\"}}' ${DINGTALK_WEBHOOK}"
        }
    }
}

五、踩坑与优化

坑1:SonarQube扫描太慢导致开发等待
初期每次MR扫描耗时6-8分钟,开发抱怨“喝杯咖啡回来还没好”。优化方案:将Sonar扫描从全量改为增量扫描(只扫描变更文件),并开启sonar.scm.disabled=true(因为GitLab插件已提供变更集),时间降至2分10秒。

坑2:CI缓存导致构建不一致
Maven的.m2缓存偶尔导致依赖冲突。解决方案:每周一凌晨定时清理缓存,并在cache配置中增加policy: pull-push。

坑3:Hotfix分支合并冲突
紧急修复时,hotfix分支从main拉出,修复后合回main,但忘了合回release,导致下次发布代码丢失。解决方案:编写了一个GitLab Webhook脚本,当hotfix/*合并到main时,自动创建MR合并到最新的release/*分支。

六、效果数据

实施该工作流6个月后,我们统计了以下数据(对比实施前3个月):

指标 实施前 实施后 变化
生产环境事故数 7次/季 2.8次/季 -60%
MR平均合并耗时 4.2小时 2.3小时 -45%
CI流水线成功率 72% 94% +22%
代码覆盖率 51% 68% +17%
回滚次数 5次/季 1次/季 -80%

总结

Git工作流不是越复杂越好,关键是平衡控制与效率。我们的混合分支策略核心在于:main分支绝对干净,feature分支短平快,用自动化工具(SonarQube + Jenkins/GitLab CI)代替人工检查。如果你也在为团队协作混乱头疼,不妨从配置CODEOWNERS和强制流水线开始,先跑起来,再迭代。记住,工具是死的,流程是活的,适合团队的才是最好的。