第 6 章 · 05 合并与 packfile


文档摘要

第 6 章 · 05 合并与 packfile 本节摘要:这是第 6 章的收尾节,讲两个进阶话题。一是合并(merge):快进合并(目标分支是当前的后代,只移指针)与三方合并(两分支都有独立提交,需找共同祖先做新合并 commit,可能产生冲突)。二是 packfile:海量小对象的压缩方案,Git 用 delta 编码把许多对象合并成一个文件。理解这两点,你的迷你 Git 就接近完整,而真正的 Git 在你眼里也不再神秘。 内容来源:基于 Git 内部模型整理的导读。 学习目标 阅读完本节,你应当能够: 区分快进合并(fast-forward)与三方合并(three-way)。 解释三方合并的「共同祖先」机制与冲突的产生。 说清 packfile 解决的问题与 delta 编码原理。

第 6 章 · 05 合并与 packfile

本节摘要:这是第 6 章的收尾节,讲两个进阶话题。一是合并(merge):快进合并(目标分支是当前的后代,只移指针)与三方合并(两分支都有独立提交,需找共同祖先做新合并 commit,可能产生冲突)。二是 packfile:海量小对象的压缩方案,Git 用 delta 编码把许多对象合并成一个文件。理解这两点,你的迷你 Git 就接近完整,而真正的 Git 在你眼里也不再神秘。

内容来源:基于 Git 内部模型整理的导读。

学习目标

阅读完本节,你应当能够:

  1. 区分快进合并(fast-forward)与三方合并(three-way)。
  2. 解释三方合并的「共同祖先」机制与冲突的产生。
  3. 说清 packfile 解决的问题与 delta 编码原理。
  4. 理解至此你的迷你 Git 已覆盖 init/add/commit/log/branch/checkout/merge 核心。

一、学习价值:合并与压缩是 Git 的工程深度

前四节是 Git 的核心模型,本节是它的工程深度。合并解决「多分支协作如何收束」,packfile 解决「海量小对象的存储效率」。理解它们,你看到 Git 不只是「对象数据库」,更是一个经过工程打磨的系统——这也解释了为什么 Git 能在大仓库(如 Linux 内核几十万 commit)上仍高效。

二、子系统拆解

合并:两种情况

情况 1:快进合并(fast-forward)

若目标分支是当前分支的直接后代(当前分支没新提交,目标分支往前走了),合并只是「把当前分支指针快进到目标」:

合并前: A → B → C(main 在这), A → B → C → D → E(dev 在这) ↑ main ↑ dev 合并后(ff): A → B → C → D → E(main 与 dev 都在 E)

无需新 commit,只是移指针。这是最简单的合并。

情况 2:三方合并(three-way merge)

若两个分支都有对方没有的独立提交,需做三方合并:

  • 找「共同祖先」(两分支最近的共同 commit,base)。
  • 对比 base → 当前分支(ours)、base → 目标分支(theirs)的改动。
  • 若两边的改动不冲突,合并应用;若改了同一处,冲突——Git 标记冲突,让人手动解决。
  • 产生一个新的「合并 commit」,它有两个 parent(ours 与 theirs)。
base / \ ours theirs \ / merge(两个 parent: ours, theirs)

冲突的产生与解决

当两分支改了「同一文件的同一区域」,三方合并不知道用谁——标记为冲突,在文件里写 <<<<<<<=======>>>>>>> 包围两个版本,让人手动选/合并,删标记后 add+commit。

packfile:海量对象的压缩

每个 commit 产生一批 blob/tree 对象。时间久了,对象库里有海量小文件(每个 blob 一个文件),既慢(打开许多文件)又占空间(每个文件占整数块)。packfile 解决:

  1. 合并:把许多对象放进一个 .pack 文件。
  2. delta 编码:很多对象彼此相似(一个文件的多个版本),只存「相对某基准对象的变化」(delta),而非完整内容。
  3. 索引:.idx 文件记录「对象哈希 → 在 pack 里的位置」。

git gc(垃圾回收)就是把松散对象打包成 packfile。大仓库经过 gc 后,存储与访问都高效得多。

三、上手第一步

  1. 合并:先实现快进合并(检测目标是否是当前后代,是则移指针)。
  2. 再实现基础三方合并:找共同祖先(从两分支沿 parent 回溯找交集)、对比改动、产生双 parent 的合并 commit。
  3. 冲突处理:检测到同区域改动时,在文件里标记冲突标记,提示用户解决。
  4. packfile(可选,进阶):把多个松散对象合并成一个文件 + delta 编码,配索引。第一个迷你 Git 可不做 packfile,理解原理即可。

本节要点回顾

  1. 快进合并:目标分支是当前后代时,只移指针,不产生新 commit。
  2. 三方合并:两分支都有独立提交时,找共同祖先(base),对比 base→ours、base→theirs,产生双 parent 合并 commit。
  3. 冲突:两边改了同区域时产生,用冲突标记让人手动解决。
  4. 合并 commit 有两个 parent(ours + theirs),这是它区别于普通 commit 的特征。
  5. packfile:把松散对象合并成一个文件 + delta 编码,解决海量小对象的效率问题;git gc 触发。

推荐上手顺序

  1. 实现快进合并(检测后代关系 + 移指针)。
  2. 实现共同祖先查找(两分支沿 parent 回溯求交集)。
  3. 实现三方合并(对比改动,产生双 parent commit)。
  4. 实现冲突检测与标记。
  5. (进阶,可选)实现 packfile,理解 Git 的存储优化。

至此第 6 章「造 Git 版本控制」全部讲完。本章带你实现 init/add/commit/log/branch/checkout/merge 的核心,从此 Git 在你眼里是「内容寻址的对象数据库 + 指针组织」,不再是玄学命令。下一章造 Web 服务器——从「管理文件历史」转到「提供网络服务」。


发布者: 作者: 灏天文库 转发
评论区 (0)
U