1. 背景:为什么我们要重写Git工作流
2024年初,团队从10人膨胀到50人,项目从单个微服务裂变为8个独立服务。最初“所有人直接推master + 手动部署”的模式彻底崩溃:
- 一周内平均发生6次合并冲突,修复耗时超过2小时。
- 一次紧急hotfix因分支混乱,导致线上回滚了3次。
- Code Review形同虚设:PR平均挂3天无人review,合并后才发现代码质量参差不齐。
我们意识到:不是工具不行,是流程没设计好。于是我们基于Git 2.35.1环境,设计了一套以“feature分支开发 + develop分支集成 + master分支发布”为核心的工作流,并配套了Code Review和CI/CD自动化。
2. 环境与版本:我们用到的具体工具
- 操作系统:Ubuntu 20.04 LTS(开发机)、macOS Ventura(部分前端同学)
- Git版本:2.35.1(2022年发布,支持
git switch和git restore) - 代码托管:GitLab EE 15.10(自建)
- CI/CD:GitLab CI Runner(版本16.0)+ Jenkins 2.387(用于老旧服务)
- 代码质量:ESLint 8.56.0 + Prettier 3.1.0 + Husky 9.0.6
- 自动部署:Docker 24.0.5 + Kubernetes 1.28(开发环境用Minikube)
3. 方案设计:分支策略与Code Review流程
3.1 分支策略:三主干 + 短期特性分支
我们采用了“Git Flow”的简化变体,放弃繁琐的release分支,保留核心分支:
master:生产就绪分支,只允许merge request(MR)合并,合并后自动触发CI/CD部署到生产环境。develop:集成分支,所有feature分支从此拉出,合并时自动触发CI/CD部署到测试环境。feature/xxx:从develop拉出,命名规则:feature/-,例如feature/1234-add-user-profile。hotfix/xxx:从master拉出,修复后合并回master和develop。
关键规则:
- 不允许直接push到master和develop;
- 每个feature分支的生命周期不超过5个工作日;
- 合并前必须通过CI/CD流水线和至少1个Code Review批准。
3.2 Code Review流程:从“无门槛”到“硬性门槛”
我们设定了以下强制步骤:
- 本地预检查:通过Husky + lint-staged在
pre-commit阶段自动运行ESLint和Prettier,不符合规范直接拒绝提交。配置如下(.husky/pre-commit):
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
配套的lint-staged.config.js:
module.exports = {
'*.{js,ts,jsx,tsx}': ['eslint --fix', 'prettier --write'],
'*.json': ['prettier --write'],
'*.md': ['markdownlint'],
};
-
提交后CI触发:GitLab CI自动运行单元测试、集成测试、代码扫描(SonarQube)。任何阶段失败,MR会被自动锁定,禁止合并。
-
人工Review:要求至少2个Approval,且Reviewer必须在24小时内响应。我们设置了一个“LGTM”按钮,但仅当所有自动化检查通过后,Reviewer才能点击。
4. 核心实现:CI/CD集成配置(真实可运行)
4.1 GitLab CI流水线配置
我们的.gitlab-ci.yml片段,支持多环境部署和分支策略:
stages:
- test
- build
- deploy
variables:
DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
K8S_NAMESPACE: "development"
cache:
paths:
- node_modules/
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
unit-test:
stage: test
image: node:18-alpine
script:
- npm ci
- npm run test:coverage
only:
- develop
- /^feature\/.*$/
- /^hotfix\/.*$/
build-image:
stage: build
script:
- docker build -t $DOCKER_IMAGE .
- docker push $DOCKER_IMAGE
only:
- develop
- master
deploy-dev:
stage: deploy
image: bitnami/kubectl:1.28
script:
- kubectl set image deployment/my-app my-app=$DOCKER_IMAGE -n $K8S_NAMESPACE
only:
- develop
environment:
name: development
url: https://dev.example.com
deploy-prod:
stage: deploy
image: bitnami/kubectl:1.28
script:
- kubectl set image deployment/my-app my-app=$DOCKER_IMAGE -n production
only:
- master
when: manual
environment:
name: production
url: https://example.com
说明:
- unit-test阶段只对develop、feature/*、hotfix/*分支运行,避免浪费master分支的资源。
- deploy-prod设置为手动触发(when: manual),防止误操作直接上线。
4.2 基于Jenkins的旧服务CI配置(兼容性方案)
对于遗留的Java服务(Spring Boot 2.7),我们保留Jenkins流水线。关键配置(Jenkinsfile):
pipeline {
agent any
stages {
stage('Checkout') {
steps {
checkout scm
script {
env.BRANCH_NAME = scm.getBranches().first().name
}
}
}
stage('Build & Test') {
steps {
sh 'mvn clean package -DskipTests=false'
junit '**/target/surefire-reports/*.xml'
}
}
stage('Docker Build') {
steps {
sh 'docker build -t my-java-app:${BRANCH_NAME}-${BUILD_NUMBER} .'
}
}
stage('Deploy to Dev') {
when { branch 'develop' }
steps {
sh 'kubectl set image deployment/java-app java-app=my-java-app:${BRANCH_NAME}-${BUILD_NUMBER} -n dev'
}
}
}
post {
failure {
emailext (
subject: "Build Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "Build failed for branch ${env.BRANCH_NAME}. Check logs: ${env.BUILD_URL}",
to: 'team@example.com'
)
}
}
}
5. 踩坑与优化:我们犯过的错误和修复
5.1 分支命名混乱导致CI混乱
问题:早期有人将feature分支命名为fix-bug,导致CI的only规则匹配不到,测试和部署全跳过。
优化:在GitLab上设置分支命名规则(Settings → Repository → Protected branches),强制要求feature/、hotfix/前缀。同时,在.gitlab-ci.yml中使用正则:/^feature\/.*$/。
5.2 Code Review形同虚设:Reviewer只看格式
问题:Reviewer只看Lint通过就点Approval,没有深入审查逻辑。
优化:我们引入“Review Checklist”模板,在MR描述中自动生成(GitLab MR模板)。例如:
### 检查清单
- [ ] 代码是否添加了单元测试?
- [ ] 是否有未处理的异常?
- [ ] 数据库迁移是否向后兼容?
- [ ] 日志是否包含足够上下文?
同时,设置MR必须包含至少1个非格式性评论(代码逻辑相关),否则不能合并。这虽然有点“暴力”,但确实提高了Review质量。
5.3 CI/CD耗时过长:从15分钟降到3分钟
问题:初始CI在每个分支上都跑全量测试(包括E2E),耗时15分钟。
优化:
- 使用GitLab CI的needs关键字,让单元测试和构建并行执行(needs: [])。
- 将E2E测试移至develop分支的单独阶段,且只在MR合并后触发。
- 缓存node_modules和Maven的.m2目录,减少依赖安装时间。优化后,feature分支的CI平均耗时3分钟。
6. 效果数据:一个季度后的成效
- 合并冲突:从每周6次降至每月1-2次,下降78%。
- PR合并周期:从平均3天降至4小时(包括Review和CI时间)。
- 线上故障:因代码质量问题导致的回滚从每月3次降为0。
- CI通过率:从75%提升至95%,因为本地pre-commit提前拦截了格式和语法错误。
- 开发效率:团队成员反馈“不再害怕合并代码”,新功能上线速度提升约40%。
7. 总结与建议
这套工作流的核心不是技术多炫酷,而是强制自动化 + 温和的规则约束。如果你团队也面临类似问题,我的建议是:
- 不要照搬Git Flow,根据团队规模裁剪(比如我们砍掉了release分支)。
- CI/CD要尽早集成,哪怕只有单元测试和lint,也能挡掉80%的低级错误。
- Code Review要有制度,但别变成形式主义——模板 + 自动化检查 + 有限的人工审核才是平衡点。
- 记录数据:冲突率、CI通过率、合并时间,用数字说服团队调整流程。
最后推荐一本书:《Git in Practice》和《持续交付2.0》。工具是死的,流程是活的,关键是大家愿意遵守。
作者注:文中配置均来自我们团队的真实仓库(已脱敏)。如果你对某个环节有疑问,欢迎评论区交流。