造轮子工程 · 第 6 章 造 Git 版本控制 章节摘要:Git 是全世界程序员天天用、却又最让人「玄学恐惧」的工具——「为什么 add 之后还要 commit」「分支到底是个什么东西」「rebase 为什么会改历史」。本章要用一个项目治好这个恐惧症:从零造一个迷你 Git。当你亲手实现 、 、 、 、 这些命令,你会发现 Git 的内部模型出奇地优雅——它本质上是一个「内容寻址的对象数据库」,用 SHA1 哈希做键,存四类对象(blob 文件内容、tree 目录结构、commit 快照、tag 标签)。
章节摘要:Git 是全世界程序员天天用、却又最让人「玄学恐惧」的工具——「为什么 add 之后还要 commit」「分支到底是个什么东西」「rebase 为什么会改历史」。本章要用一个项目治好这个恐惧症:从零造一个迷你 Git。当你亲手实现
init、add、commit、log、branch这些命令,你会发现 Git 的内部模型出奇地优雅——它本质上是一个「内容寻址的对象数据库」,用 SHA1 哈希做键,存四类对象(blob 文件内容、tree 目录结构、commit 快照、tag 标签)。本章导读 Git 的五个核心:对象模型(四类对象的内容寻址存储)、索引与 staging(add 到底干了什么)、提交与快照(commit 是给当前树打一个带父指针的快照)、分支与合并(分支只是个指向 commit 的指针)、packfile(海量小对象的压缩方案)。造完之后,Git 在你眼里将不再是「一堆命令」,而是一个「不可变快照图」——所有命令都只是这张图上的操作。这是治好 Git 恐惧症最彻底的方式。
阅读完本章,你应当能够:
git add 干的事:把工作区的文件变成 blob 存进对象库,并把「这个文件现在对应这个 blob」记进索引(index)。git commit 干的事:把当前索引建成一棵 tree,再创建一个带父指针(指向上一个 commit)的 commit 对象。git log(沿父指针回溯历史)、git branch(创建分支指针)、git checkout(移动 HEAD 与工作区)。Git 的全部魔法浓缩成一句话:它是一个「内容寻址的对象数据库」,所有操作都是在这个数据库里读写对象,并用指针(commit、分支、HEAD)把它们组织成一张不可变快照图。理解了这点,add/commit/branch/log 都不再是玄学命令,而是这张图上的自然操作。
Git 的地基是「内容寻址存储」:每个对象的内容算 SHA1,哈希即键、内容即值。讲清四类对象——blob(文件内容,不含文件名)、tree(目录,含「文件名→blob 哈希」的映射)、commit(指向一棵 tree + 父 commit + 作者信息)、tag(带标注的指向)。这是理解 Git 的第一块基石。
git add 在干什么git add 不是「把文件标记为待提交」,而是「把文件内容存成一个 blob,并在索引里记下这个文件名对应这个 blob」。索引(index)是「下一次 commit 的草稿树」。讲清这个机制,add 之后还要 commit 就不再费解了。
git commit 在干什么git commit 把当前索引建成一棵 tree,再创建一个 commit 对象(指向这棵 tree + 指向上一个 commit + 作者时间信息)。重点是:Git 存的是「每次提交时的完整快照」,而非「相对上次的差异」——这解释了为什么 Git 切分支这么快。
分支只是一个「指向某个 commit 的可变指针」,HEAD 是「指向当前分支」的特殊指针。讲清这一点,你就明白「创建分支」有多廉价(只是加一个指针)、checkout 干的事是「移动 HEAD + 把工作区恢复到那个 commit」。
git log 与回溯git log 的实现:从当前 commit 出发,沿父指针不断回溯,打印每个 commit 的信息。讲清线性历史、分叉历史(一个 commit 可能有多个父——合并产生)、可达性,这为理解 merge/rebase 打基础。
两种合并:快进合并(目标分支是当前分支的直接后代,只需移动指针)、三方合并(两分支都有独立提交,需找共同祖先做一次新的合并提交)。讲清合并冲突的产生与解决原理——这是 Git 最让新手头疼的部分,理解原理就不再恐惧。
每次 commit 都产生一批 blob/tree 对象,海量小对象既慢又占空间。讲清 packfile:把多个对象合并成一个文件,并用 delta 编码(只存相对某个基准对象的变化)压缩。这是 Git 在大仓库上仍快仍省空间的工程秘诀。
本章遵循「地基 → 暂存 → 提交 → 分支 → 历史 → 合并 → 优化」的递进路径,前五节是「单分支线性历史」,后两节是「多分支与工程优化」:
对象模型 (01) ── 内容寻址 + 四类对象(地基) │ ▼ 索引/staging (02) ── git add 的真相 │ ▼ 提交/快照 (03) ── git commit 的真相 │ ▼ 分支/HEAD (04) ── 分支只是指针 │ ▼ git log 回溯 (05) ── 历史是沿父指针的链 │ ▼ 合并 (06) ── 快进 vs 三方, 冲突怎么来 │ ▼ packfile (07) ── 海量对象的工程压缩 │ ▼ 第 7 章:从"管理文件历史"到"提供文件服务"——造 Web 服务器
01 是地基(不学这个,后面都是黑箱);02-03 是「单次提交的完整闭环」;04-05 是「分支与历史」;06 是「多分支协作」;07 是「工程优化」。建议你的迷你 Git 实现分阶段:先做到 05(能在单分支上 init/add/commit/log),再做到 06(branch/checkout/merge),最后了解 07。
前置知识:
本章为后续章节奠定的基础: