Git篇(7):Git 检测差异的原理——为什么合并会出现冲突

Git 在检测差异时,不是像 Word 那样按"字节对比",也不是按"行号硬比对",而是用 最长公共子序列(LCS, Longest Common Subsequence)算法 来找出改动区域(diff hunk)。

流程可以这样理解:

  1. 找到两个版本的文件(来自不同 commit 的 blob)。
  2. 用 LCS 算法比对两份文本,定位哪些行相同、哪些行不同。
  3. 把不同的地方划分成"差异块 hunk"
  4. 输出成 diff 格式,再用于 merge 或显示。

👉 所以,Git 的差异检测是基于"行内容 + 相邻上下文",不是行号。


可视化:差异检测的例子

假设共同祖先版本(C2):

css 复制代码
A
B
C
D
E

远程提交(C3)改了 B → B'

css 复制代码
A
B'
C
D
E

本地提交(C4)改了 D → D'

css 复制代码
A
B
C
D'
E

第一步:找共同祖先

C2 就是共同祖先(A-B-C-D-E)。


第二步:算 diff

  • C2 → C3 的 diff:
css 复制代码
- B
+ B'
  • C2 → C4 的 diff:
diff 复制代码
- D
+ D'

第三步:定位差异块

Git 会生成两个差异块(hunk):

  • hunk1:涉及 B 的改动
  • hunk2:涉及 D 的改动

第四步:合并

Git 尝试把 hunk1 和 hunk2 应用到 C2:

  • hunk1 改 BB'
  • hunk2 改 DD'

结果合并后:

css 复制代码
A
B'
C
D'
E

👉 自动合并成功,因为两个 hunk 不重叠。


冲突的例子

共同祖先(C2):

css 复制代码
A
B
C

远程提交(C3):B → X

css 复制代码
A
X
C

本地提交(C4):B → Y

css 复制代码
A
Y
C

diff

  • C2 → C3 的 diff: - B + X
  • C2 → C4 的 diff: - B + Y

hunk 对比

Git 发现两个 diff 都作用在同一个位置(B),修改冲突,无法自动决定保留 X 还是 Y

于是标记冲突:

markdown 复制代码
A
<<<<<<< HEAD
Y
=======
X
>>>>>>> origin/main
C

Git 的差异检测原理总结

  1. 基于 LCS 算法,找出文件两版本的最长公共子序列。

  2. 把"非公共部分"标记为差异块(hunk)。

  3. 合并时:

    • 如果 hunk 不重叠 → 自动合并
    • 如果 hunk 重叠 → 冲突

总结与比喻

Git 检测差异就像两个人修改一本书:

  • Git 会先找到书里大家都没改过的段落(LCS)("行"是比较的最小单位);
  • 把大家改动的部分标记出来(hunk)(连续的差异行);
  • 如果改动在不同段落,直接合并;
  • 如果改动在同一行文字,Git 没法替你做决定,就报冲突。

在此强调,从头到尾,都不涉及"行号"的比较,这是误解的根源。


相关推荐
和你看星星2 天前
Git rerere:让重复冲突只解决一次
git
嘻嘻仙人6 天前
Ubuntu中 git上传自己的项目和二次上传一般流程
git·github
Patrick_Wilson6 天前
Squash Merge 的血缘陷阱:为什么删掉的代码又活了过来
前端·git·程序员
沉浸学习的匿名网友6 天前
什么是 .gitignore?为什么每个 Git 项目几乎都离不开它?
前端·git
深海鱼在掘金7 天前
Git 完全指南 —— 第3章:理解工作区、暂存区、版本库三个核心
git
江华森7 天前
Git 基础筑基:从原理到团队协作的全栈实战
git
JakeJiang7 天前
Git 必备命令指南:从日常高频到项目开发实战
git
叫我少年8 天前
Windows 中安装 git
git
深海鱼在掘金13 天前
Git 完全指南 —— 第1章:Git 概览与版本控制演进
git
noravinsc14 天前
关于Git Flow
git