跳转至

合并引擎

merge engine 是 Git 复现项目里最容易“看起来能跑、其实很多边界没做好”的部分之一。

为什么 merge engine 难

因为它不是单一问题,而是几类问题叠在一起:

  • commit 图遍历;
  • 最近公共祖先搜索;
  • fast-forward 判断;
  • tree / 文件差异比较;
  • 三方内容合并;
  • 冲突标记;
  • index stage 写入;
  • 冲突状态下禁止继续 commit。

任何一层没处理好,merge 行为都会偏离 Git 语义。

第一层:图问题

merge 首先不是文本问题,而是图问题。
你必须先搞清楚:

  • ours 是谁;
  • theirs 是谁;
  • 最近公共祖先在哪里。

只有这三点明确之后,三方比较才有基础。

第二层:快进判断

如果当前分支只是落后于目标分支,而没有自己额外的提交,那 merge 根本不需要真正三方合并。

这时最正确的行为通常是 fast-forward:
直接把当前 ref 推进到目标提交。

所以 merge engine 的一个职责是识别“什么时候不用做复杂工作”。

第三层:三方内容合并

真正进入三方合并时,系统需要判断:

  • 哪些文件只在一边改动;
  • 哪些文件两边都改了但不冲突;
  • 哪些文件两边对同一区域做了不兼容修改。

最后一种情况才需要进入冲突状态。

第四层:冲突状态表达

冲突不能只靠工作区文本来表达。
一个更接近 Git 的实现还需要:

  • 往文件里写 <<<<<<< / ======= / >>>>>>> 隔离带;
  • 在 index 中写 stage 1 / 2 / 3;
  • 阻止用户在未解决冲突时继续 commit。

也就是说,merge conflict 不是“给用户看一眼文本差异”这么简单,而是一个仓库状态。

为什么 merge 应该独立成模块

如果把 merge 逻辑散落在 checkoutcommitstatus 或 CLI 里,会很快失控。
merge 需要独立模块的原因在于:

  • 图遍历和文件内容处理都足够复杂;
  • 需要反复测试;
  • 需要统一冲突和 fast-forward 语义;
  • 需要和 index、refs、working tree 协同。

它不是某个命令的小附属功能,而是系统级能力。