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 没法替你做决定,就报冲突。

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


相关推荐
云闲不收22 分钟前
git rebase
git
江上清风山间明月31 分钟前
git pull和git checkout在恢复文件的区别
git·pull·checkout
海鸥812 小时前
in argocd ‘/tmp/_argocd-repo/../.git/index.lock‘: No space left on
git·argocd
尔嵘3 小时前
git操作
大数据·git·elasticsearch
大柏怎么被偷了5 小时前
【Git】企业级开发模型
git
Garfield20055 小时前
Git 分支拓扑实践
git·拓扑
DKNG5 小时前
【Windows Host】 hosts配置增加访问github流畅度
人工智能·git·github
一个很帅的帅哥7 小时前
git命令大全
大数据·git·elasticsearch
凯子坚持 c8 小时前
Git 远程仓库操作与深度进阶指南
git
勇敢牛牛_8 小时前
RustRover 2025.3 在WSL中GIT操作十分缓慢的问题
git·rust·rustrover