3. 版本控制基石:Git与GitHub/GitLab的协作流程,为CI/CD打下基础

说实话,很多团队做CI/CD翻车,根子不在流水线脚本上,而在版本控制流程上。我见过太多人把Git当网盘用——谁改了什么、为什么改、能不能合并,一概不管。结果CI一跑就崩,一崩就找不着北。

这一章,咱们就把Git的协作流程理清楚。说白了,CI/CD的根基就是版本控制。你想想看,如果代码版本都管不明白,自动化测试和持续集成从哪谈起?

3.1 为什么Git是CI/CD的基石

CI/CD的核心逻辑是「代码一变,自动触发」。这个「变」靠什么感知?靠Git的提交和推送。没有Git,CI/CD就是个空壳。

我个人习惯把Git在CI/CD中的作用拆成三层:

  • 触发层:每次push或PR(Pull Request)触发流水线
  • 隔离层:分支机制让不同功能、不同环境的代码互不干扰
  • 追溯层:每次构建都能对应到具体的commit,出了问题能快速回滚

我在项目中遇到过一种情况:团队用SVN,每次合并代码都要手动通知CI跑构建。后来切到Git + GitLab CI,push即触发,效率翻了一倍不止。这就是版本控制工具本身带来的自动化红利。

核心观点:CI/CD的自动化程度,取决于版本控制流程的规范程度。流程越乱,CI/CD越容易变成「持续修锅」。

3.2 分支策略:选对模型,少踩一半坑

分支策略是协作流程的灵魂。选错了,合并冲突能让你怀疑人生。我见过最惨的团队,一个release分支上同时有5个人在改,CI/CD跑出来的构建永远不知道是谁的代码。

目前主流的分支模型有三种,我列个表对比一下:

模型 适用场景 CI/CD配合度 我踩过的坑
Git Flow 大型项目、固定发布周期 中等(分支多,流水线配置复杂) hotfix分支忘了触发CI,线上bug修了没测
GitHub Flow 持续部署、快速迭代 高(PR即触发,简单直接) master分支保护没开,有人直接push跳过CI
GitLab Flow 环境分支、多环境部署 高(环境分支与CI环境一一对应) 环境分支合并顺序搞反,导致staging覆盖了production

我个人推荐中小团队直接上GitHub Flow或GitLab Flow。为什么?因为分支越少,CI/CD流水线越容易维护。Git Flow那套develop、release、hotfix、feature全上,流水线得配4套触发规则,出错的概率翻倍。

3.3 分支保护与CI/CD的联动

分支保护不是用来限制开发的,是用来「强制CI/CD必须跑过才能合并」的。我见过最蠢的配置:分支保护开了,但CI/CD没配进保护规则里。结果呢?代码review通过了,CI没跑,合并后直接炸了生产环境。

正确的做法是这样的:

  1. 开启分支保护:禁止直接push到master/main
  2. 设置状态检查:要求CI/CD流水线必须通过
  3. 要求线性历史:避免乱七八糟的merge commit

在GitHub上,你可以在Settings → Branches → Branch protection rules里配。GitLab类似,在Settings → Repository → Protected branches里。

小技巧:我习惯在保护规则里加上「要求最新代码」——也就是说,PR合并前必须把目标分支的最新代码拉进来再跑一次CI。这样能避免「合并时冲突但CI没测到」的情况。

3.4 提交规范:让CI/CD读懂你的commit

CI/CD不光要跑测试,还要根据commit信息做决策。比如:commit message里带[skip ci],流水线就不跑;带fix:前缀,自动触发发布到测试环境。

我推荐用Conventional Commits规范,格式很简单:

<type>(<scope>): <description>

[optional body]

[optional footer]

常用的type有:

  • feat:新功能
  • fix:修复bug
  • chore:构建、依赖等杂项
  • docs:文档变更
  • test:测试相关

为什么这个对CI/CD重要?因为你可以根据type决定流水线的行为。举个例子:

# 如果commit是feat或fix,跑完整流水线(lint + test + build + deploy)
# 如果commit是docs,只跑lint,不部署
# 如果commit message包含[skip ci],直接跳过

我在项目中遇到过一种情况:有人提交了chore: update dependencies,结果触发了完整的部署流水线,把还在开发中的功能推到了生产环境。后来我们加了一条规则——只有feat和fix才触发部署,其他类型只跑测试。

注意:commit规范一定要靠工具强制,不能靠自觉。用commitlint + husky在本地拦截不规范提交,或者在CI里加一步commit message检查。我曾经吃过「团队约定好了但没人执行」的亏,最后流水线里加了个校验步骤才解决。

3.5 PR/MR流程:CI/CD的触发点

PR(GitHub)或MR(GitLab)是CI/CD最常见的触发点。我个人习惯把PR/MR流程设计成三段:

  1. 创建PR/MR时:触发CI跑单元测试和代码检查
  2. 更新PR/MR时:重新触发CI,确保新提交没问题
  3. 合并前:再跑一次完整流水线(包括集成测试)

这里有个细节:很多团队只在PR创建时跑CI,更新时不跑。结果呢?reviewer看到的是旧结果,合并时才发现新代码有问题。我建议在流水线配置里加上trigger: on_update,确保每次push都重新跑。

GitLab CI的配置示例:

test:
  script: npm test
  only:
    - merge_requests
  when: on_success

GitHub Actions的配置示例:

on:
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: npm test

3.6 协作流程全景图

说了这么多,咱们用一张图把整个协作流程串起来。从开发者本地提交,到CI/CD自动触发,再到合并部署,每一步都环环相扣。

Git协作与CI/CD流程全景图 Git协作与CI/CD流程全景图 开发者本地 git commit + git push 远程仓库 GitHub / GitLab CI/CD触发 Webhook / Polling 分支保护规则 禁止直接push + 状态检查 PR / MR 创建 代码审查 + 自动触发CI CI流水线执行 Lint → Test → Build 合并到目标分支 master / main 自动部署 CD流水线触发 关键要点: 1. 每次push都触发CI,但合并前必须再跑一次完整流水线 2. 分支保护强制CI通过才能合并,杜绝「跳过检查」 3. commit规范让CI/CD能根据提交类型做不同决策

3.7 避坑指南:我踩过的那些坑

最后分享几个实战中容易翻车的地方,都是我亲身经历过的:

坑1:分支保护没配状态检查

我曾经在一个项目里开了分支保护,但忘了把CI/CD的状态检查加进去。结果有人提交了代码,CI跑挂了,但合并按钮还是绿的。等代码合并到master,生产环境直接崩了。从那以后,我每次配分支保护都会确认「Require status checks」是勾上的。

坑2:CI/CD触发条件太宽

有次我把流水线配成了「任何分支push都触发」。结果有人开了几十个feature分支,每个分支push一下,CI队列直接爆了。后来我改成「只有PR和master分支push才触发」,其他分支只跑lint,省了不少资源。

坑3:忽略commit规范

团队刚开始用Conventional Commits时,全靠口头约定。结果呢?有人写fix bug,有人写修复了一个问题,还有人写asdf。CI/CD根本没法根据commit做决策。后来我在CI里加了一步commitlint检查,不规范直接拒绝。虽然一开始有人抱怨,但两周后大家都习惯了。

嗯,版本控制这块就聊到这儿。记住一句话:CI/CD的稳定性,取决于你Git流程的严谨程度。流程规范了,自动化才能跑得稳。