第 6 章 · 05 合并与 packfile 本节摘要:这是第 6 章的收尾节,讲两个进阶话题。一是合并(merge):快进合并(目标分支是当前的后代,只移指针)与三方合并(两分支都有独立提交,需找共同祖先做新合并 commit,可能产生冲突)。二是 packfile:海量小对象的压缩方案,Git 用 delta 编码把许多对象合并成一个文件。理解这两点,你的迷你 Git 就接近完整,而真正的 Git 在你眼里也不再神秘。 内容来源:基于 Git 内部模型整理的导读。 学习目标 阅读完本节,你应当能够: 区分快进合并(fast-forward)与三方合并(three-way)。 解释三方合并的「共同祖先」机制与冲突的产生。 说清 packfile 解决的问题与 delta 编码原理。
本节摘要:这是第 6 章的收尾节,讲两个进阶话题。一是合并(merge):快进合并(目标分支是当前的后代,只移指针)与三方合并(两分支都有独立提交,需找共同祖先做新合并 commit,可能产生冲突)。二是 packfile:海量小对象的压缩方案,Git 用 delta 编码把许多对象合并成一个文件。理解这两点,你的迷你 Git 就接近完整,而真正的 Git 在你眼里也不再神秘。
内容来源:基于 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)
若两个分支都有对方没有的独立提交,需做三方合并:
base / \ ours theirs \ / merge(两个 parent: ours, theirs)
当两分支改了「同一文件的同一区域」,三方合并不知道用谁——标记为冲突,在文件里写 <<<<<<<、=======、>>>>>>> 包围两个版本,让人手动选/合并,删标记后 add+commit。
每个 commit 产生一批 blob/tree 对象。时间久了,对象库里有海量小文件(每个 blob 一个文件),既慢(打开许多文件)又占空间(每个文件占整数块)。packfile 解决:
.pack 文件。.idx 文件记录「对象哈希 → 在 pack 里的位置」。git gc(垃圾回收)就是把松散对象打包成 packfile。大仓库经过 gc 后,存储与访问都高效得多。
git gc 触发。至此第 6 章「造 Git 版本控制」全部讲完。本章带你实现 init/add/commit/log/branch/checkout/merge 的核心,从此 Git 在你眼里是「内容寻址的对象数据库 + 指针组织」,不再是玄学命令。下一章造 Web 服务器——从「管理文件历史」转到「提供网络服务」。