背景是最近我开了一个新仓库,从界面上merge的时候,发现生成了一个新commit,但是我预期是不生成新commit的,所以我调整了一下,调整方法是:

setting -> general -> Merge request -> merge method

在这里插入图片描述

选择“Fast-forward merge” 就好。


  1. Merge commit (创建合并提交)

    • 含义: 这是最标准的合并方式。
    • 历史影响: 无论源分支是否领先于目标分支,总是会创建一个新的、特殊的提交记录,称为“合并提交”。这个提交有两个父提交:一个是目标分支的最新提交(例如 mainC2),另一个是源分支的最新提交(例如 featureC3)。
    • 提交图示例:
      *   C4 (Merge branch 'feature' into 'main')  <-- 新的合并提交 (HEAD -> main)
      |\
      | * C3 (Feature commit 2)                    <-- feature 分支的最新提交
      | * C2 (Feature commit 1)
      * | C1 (Main commit)                         <-- main 分支的原始最新提交
      |/
      * C0 (Initial commit)
      
    • 优点: 清晰记录了合并发生的时间和意图(通过提交信息),保留了分支结构的完整历史。
    • 缺点: 历史图可能看起来更复杂,有分叉和汇合。
  2. Merge commit with semi-linear history (创建合并提交 + 半线性历史)

    • 含义: 这是“创建合并提交”策略的一个更严格的变种,旨在强制历史图看起来更接近线性(减少并行线的交叉)。
    • 历史影响:
      • 总是会创建一个合并提交。
      • 关键限制: 只允许在源分支的基底(base)严格等于目标分支当前最新提交时进行合并。也就是说,源分支必须是从目标分支的最新提交直接分叉出来的,并且目标分支在源分支开发期间没有新的提交。
      • 提交图示例 (允许合并时):
        * C3 (Feature commit 2)                  <-- feature 分支的最新提交
        * C2 (Feature commit 1)                  <-- 从 C1 分叉出来
        | * C4 (Merge branch 'feature')          <-- 新的合并提交 (HEAD -> main)
        |/|
        | * ... (假设 main 在分叉后没有新提交)
        |/
        * C1 (Main commit before fork)           <-- feature 分支的起点 (也是 main 的最新提交)
        * C0 (Initial commit)
        
        合并后,main 指向 C4C4 有两个父:C1 (原 main) 和 C3 (原 feature 最新提交)。历史在 C4 处汇合,看起来像 C0 -> C1 -> C3 -> C4 的一条线(忽略了 C2C4 的第二个父链接),所以称为“半线性”。
    • 行为:
      • 如果目标分支在源分支开发期间有了新提交(main 前进了),那么源分支的基底就落后于目标分支的最新提交。此时平台(如 GitLab)不允许直接合并
      • 用户操作: 平台会提示用户先对源分支执行 变基,将其基底更新到目标分支的最新提交上。变基操作会重写源分支的提交历史,使其看起来像是从目标分支最新点开始的。变基成功后,基底就匹配了,此时再合并就能满足“半线性”的要求。
    • 优点: 强制历史图更整洁、更线性化,便于阅读和追溯。保留了合并意图。
    • 缺点: 要求更严格,可能需要额外的变基步骤,变基会重写提交历史(对已推送的共享分支需谨慎)。
  3. Fast-forward merge (快进合并)

    • 含义: 这是最简单、最直接的合并方式,但只在特定条件下可用。
    • 历史影响:
      • 不会创建任何新的合并提交。
      • 条件: 只有当目标分支的最新提交是源分支最新提交的直接祖先时才能进行。也就是说,目标分支在源分支分叉出去后,自己没有任何新的提交
    • 提交图示例:
      * C3 (Feature commit 2)                      <-- feature 分支的最新提交
      * C2 (Feature commit 1)
      | * ... (main 分支在 C1 之后没有新提交!)
      |/
      * C1 (Main commit before fork)               <-- (HEAD -> main, feature) 合并前 main 指向这里
      * C0 (Initial commit)
      
      执行快进合并后:
      * C3 (Feature commit 2)                      <-- (HEAD -> main, feature)
      * C2 (Feature commit 1)
      * C1 (Main commit before fork)
      * C0 (Initial commit)
      
      main 分支的指针直接向前移动(“快进”)到 feature 分支的最新提交 C3mainfeature 现在指向同一个提交。历史是一条完美的直线。
    • 行为:
      • 如果满足快进条件(目标分支无新提交),则自动进行快进合并。
      • 冲突处理: 如果合并过程中发生冲突(即使满足快进条件,理论上快进不应该冲突,但可能配置或操作有误),平台会阻止快进。
      • 用户操作 (冲突时): 平台会提示用户先解决冲突。通常建议的方式是对源分支执行 变基 到目标分支上,在变基过程中解决冲突。解决冲突并完成变基后,源分支的基底就更新了,此时可能又满足快进条件了(如果目标分支在此期间没再更新),或者可以尝试其他合并方式。
    • 优点: 历史图绝对线性、简洁。没有多余的合并提交。
    • 缺点: 完全丢失了分支曾经存在过的信息(合并后历史图上看不出有过分支)。只在目标分支未更新时可用。
  4. 什么是合并列车?

    • 含义: 合并列车是一种高级的持续集成/持续部署策略,常用于要求高稳定性的主干开发模式中。
    • 目的: 确保多个依次合并到目标分支(如 main)的合并请求,在合并前都基于最新的目标分支成功通过所有测试。
    • 工作原理:
      1. 当多个合并请求排队等待合并到 main 时,它们不会立即合并。
      2. 平台(如 GitLab)会自动创建一个临时的“列车轨道”。
      3. 第一个合并请求(MR1)被“放入列车”。平台会创建一个临时分支,尝试将 MR1 变基到当前最新的 main 上,然后在这个临时分支上运行完整的 CI/CD 流水线(编译、测试等)。
      4. 如果 MR1 的测试通过,它就被标记为“准备合并”,但不会立即合并
      5. 下一个合并请求(MR2)被“放入列车”。平台会创建一个新的临时分支:先将 MR1 变基到最新 main 的结果作为基底,再将 MR2 变基到这个基底上,然后运行 MR2 的 CI/CD 流水线(同时包含 MR1 和 MR2 的更改!)。
      6. 如果 MR2 的测试也通过,它也被标记为“准备合并”。
      7. 这个过程继续,后续的 MR 会依次变基到包含了前面所有已通过测试的 MR 更改的临时分支上,并运行测试。
      8. 只有当列车上的所有 MR 都成功通过它们基于最新更改(包含前面所有 MR)的测试后,平台才会按顺序将它们合并到 main 分支。
    • 与快进合并策略的关系:
      • 你描述中提到 “When merge trains are enabled, merging is only possible if the branch can be rebased without conflicts.
      • 这是因为合并列车的核心机制就是自动对每个入列的 MR 进行变基。如果一个 MR 在自动变基过程中出现冲突,说明它无法干净地应用到当前的目标分支(或包含了前面 MR 的临时分支)上。
      • 结果: 这个 MR 无法加入列车(或会导致列车失败),因此无法通过合并列车的方式被合并。用户必须先手动解决冲突(通常是在本地变基并解决冲突,然后推送)。
    • 优点: 极大提高主干稳定性。确保合并后的代码是多个功能组合后仍然可工作的。防止后合并的 MR
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐