合并引擎¶
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 逻辑散落在 checkout、commit、status 或 CLI 里,会很快失控。
merge 需要独立模块的原因在于:
- 图遍历和文件内容处理都足够复杂;
- 需要反复测试;
- 需要统一冲突和 fast-forward 语义;
- 需要和 index、refs、working tree 协同。
它不是某个命令的小附属功能,而是系统级能力。