造轮子工程 · 第 6 章 造 Git 版本控制


文档摘要

造轮子工程 · 第 6 章 造 Git 版本控制 章节摘要:Git 是全世界程序员天天用、却又最让人「玄学恐惧」的工具——「为什么 add 之后还要 commit」「分支到底是个什么东西」「rebase 为什么会改历史」。本章要用一个项目治好这个恐惧症:从零造一个迷你 Git。当你亲手实现 、 、 、 、 这些命令,你会发现 Git 的内部模型出奇地优雅——它本质上是一个「内容寻址的对象数据库」,用 SHA1 哈希做键,存四类对象(blob 文件内容、tree 目录结构、commit 快照、tag 标签)。

造轮子工程 · 第 6 章 造 Git 版本控制

章节摘要:Git 是全世界程序员天天用、却又最让人「玄学恐惧」的工具——「为什么 add 之后还要 commit」「分支到底是个什么东西」「rebase 为什么会改历史」。本章要用一个项目治好这个恐惧症:从零造一个迷你 Git。当你亲手实现 initaddcommitlogbranch 这些命令,你会发现 Git 的内部模型出奇地优雅——它本质上是一个「内容寻址的对象数据库」,用 SHA1 哈希做键,存四类对象(blob 文件内容、tree 目录结构、commit 快照、tag 标签)。本章导读 Git 的五个核心:对象模型(四类对象的内容寻址存储)、索引与 staging(add 到底干了什么)、提交与快照(commit 是给当前树打一个带父指针的快照)、分支与合并(分支只是个指向 commit 的指针)、packfile(海量小对象的压缩方案)。造完之后,Git 在你眼里将不再是「一堆命令」,而是一个「不可变快照图」——所有命令都只是这张图上的操作。这是治好 Git 恐惧症最彻底的方式。

学习目标

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

  1. 说清 Git 的核心数据模型:内容寻址存储(用 SHA1 哈希做键)+ 四类对象(blob/tree/commit/tag)。
  2. 区分「blob」(文件内容)、「tree」(目录结构)、「commit」(带父指针的快照)三者,理解为什么 Git 用快照而非差异(diff)存历史。
  3. 解释 git add 干的事:把工作区的文件变成 blob 存进对象库,并把「这个文件现在对应这个 blob」记进索引(index)。
  4. 解释 git commit 干的事:把当前索引建成一棵 tree,再创建一个带父指针(指向上一个 commit)的 commit 对象。
  5. 说清分支的本质:一个指针,指向某个 commit;HEAD 是个特殊指针,指向「你当前在哪个分支」。
  6. 实现基础的 git log(沿父指针回溯历史)、git branch(创建分支指针)、git checkout(移动 HEAD 与工作区)。
  7. 理解 packfile 解决的问题:海量小对象(每次 commit 一堆 blob)如何压缩合并存储。

核心概念速览

Git 的全部魔法浓缩成一句话:它是一个「内容寻址的对象数据库」,所有操作都是在这个数据库里读写对象,并用指针(commit、分支、HEAD)把它们组织成一张不可变快照图。理解了这点,add/commit/branch/log 都不再是玄学命令,而是这张图上的自然操作。

子章节导航

01 对象模型:内容寻址存储

Git 的地基是「内容寻址存储」:每个对象的内容算 SHA1,哈希即键、内容即值。讲清四类对象——blob(文件内容,不含文件名)、tree(目录,含「文件名→blob 哈希」的映射)、commit(指向一棵 tree + 父 commit + 作者信息)、tag(带标注的指向)。这是理解 Git 的第一块基石。

02 索引与 staging:git add 在干什么

git add 不是「把文件标记为待提交」,而是「把文件内容存成一个 blob,并在索引里记下这个文件名对应这个 blob」。索引(index)是「下一次 commit 的草稿树」。讲清这个机制,add 之后还要 commit 就不再费解了。

03 提交与快照:git commit 在干什么

git commit 把当前索引建成一棵 tree,再创建一个 commit 对象(指向这棵 tree + 指向上一个 commit + 作者时间信息)。重点是:Git 存的是「每次提交时的完整快照」,而非「相对上次的差异」——这解释了为什么 Git 切分支这么快。

04 分支与 HEAD:本质都是指针

分支只是一个「指向某个 commit 的可变指针」,HEAD 是「指向当前分支」的特殊指针。讲清这一点,你就明白「创建分支」有多廉价(只是加一个指针)、checkout 干的事是「移动 HEAD + 把工作区恢复到那个 commit」。

05 历史:git log 与回溯

git log 的实现:从当前 commit 出发,沿父指针不断回溯,打印每个 commit 的信息。讲清线性历史、分叉历史(一个 commit 可能有多个父——合并产生)、可达性,这为理解 merge/rebase 打基础。

06 合并:快进合并 vs 三方合并

两种合并:快进合并(目标分支是当前分支的直接后代,只需移动指针)、三方合并(两分支都有独立提交,需找共同祖先做一次新的合并提交)。讲清合并冲突的产生与解决原理——这是 Git 最让新手头疼的部分,理解原理就不再恐惧。

07 packfile:海量对象的压缩

每次 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。

前置知识与后续延伸

前置知识:

  • 姊妹篇《Linux 命令实战》第 2 章(文件与目录——Git 的对象库就是一堆文件)
  • 姊妹篇《Linux 命令实战》第 3 章(文本处理——读 Git 对象内容时用得到)
  • 基本的数据结构(哈希表、图、树)
  • 会用 Git(这是「再造你已经会用的工具」的最佳案例)

本章为后续章节奠定的基础:

  • 「内容寻址存储」是一种通用模式,延伸到区块链(哈希链)、IPFS 等领域
  • 「不可变数据 + 指针组织」是函数式编程与持久化数据结构的核心思想
  • 造完 Git,你彻底治好了 Git 玄学恐惧症——从此 rebase、cherry-pick、reflog 都是这张图上的自然操作
  • 哈希链的思想会在第 8 章(数据库的 WAL)、区块链等场景再次出现

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