读书导读 · CC BY-NC-SA 3.0 · 非商业

《Pro Git(精通 Git)》全书摘要与逐章导读

CC BY-NC-SA 3.0 · 非商业

非商业许可:按许可,本页不引用原书文字,只链接回官方免费版本的对应小节。免费阅读原书。

《Pro Git(精通 Git)》讲了什么AI 辅助

一个核心问题,把全书从存储到协作串了起来

整体来看,这本书只问一个问题——基于快照的分布式历史,怎样改变人们协作的方式?——并以层层建立的几个层次回答。它从 Git 的存储模型开始:每次克隆都镜像完整历史,每个提交是一份带校验和的快照,数据几乎只增不减;然后把这个模型转成日常的本地循环:跟踪、暂存、提交,以及读取状态;因为分支只是一个 41 字节的指针,合并要么是指针移动,要么是一个多父提交,这就是使频繁的主题分支切实可行、而不只是可能的原因。推广到他人,它涵盖远程仓库和跟踪分支,共享的裸仓库和暴露它们的传输协议,工作流的菜单——集中式、集成管理员、独裁者与副手——以及 GitHub 的复刻加拉取请求约定。高级内容随后提供手艺:提交的命名和范围,补丁级的暂存,变基及其风险,分层的配置和逐文件的属性,以及通往 Subversion、Mercurial 和 Perforce 的桥。它最后深入命令之下,把 Git 呈现为一个按内容寻址的对象数据库,高层命令不过是把底层操作自动化,同时概览各种图形界面和 shell 集成,这样,从存储模型到本地习惯、到团队协议、再回落到内部原理的这条弧线,使每一层都由下一层来证成。

一则讲透全书的寓言AI 辅助

用一个故事装下全书最核心的想法,并老实交代故事在哪里失真。

塔楼着火时,他们靠分布式解决了备份问题

从前有个织工村,把花样都存在一座上了锁的塔里。只有塔的看守人手里有那本账簿,每个想要昨天花样的织工都得爬上去问。一个冬天,塔烧了,连同村子是怎么学会织布的全部记录。

织工们用不同的办法重建。一个叫萨罗的年轻染匠提议:什么东西都不再按名字或架子上的位置来存放。取而代之,每一块织好的布都浸进一个“读取浴”,从布自己的纱线里得出一个独一无二的印记;这个印记就是这块布的地址。同样的布得到同样的印记,所以村里从不把同一种织法存两遍——只是指向已经有的那块。村里保管三类东西:没有名字的染色纱团,给纱团起名字、定位置的索引板,以及注明日期的证书:它指明一块索引板,指明在它之前的那些证书,还写明是谁织的、谁归档的。什么东西都从不被刮掉;那堆东西只增不减。

然后萨罗做了更奇怪的事:每个织工都得到整堆东西。不是借条,是那一整堆。一百户人家,一百份完整的历史,每一份都是备份,每一份在暴风雪里、哪儿也去不了的时候都能用。

随之而来两个习惯。归档一张证书之前,织工只把她打算记录的纱线摆在织机旁一张低矮的桌上——不是整台织机,只是那张桌子,而且如果她把一根纱线摆上去之后又碰了它,就得再摆一遍。要标记一条工作线索,她不去复制那堆东西,而是写一张短短的纸条,上面只有某张证书的印记和一个名字,轻得一撕就破,所以开启一条新的工作线索不花任何代价。

麻烦还是来了,却不是出在机制上。村子摆脱了塔,却立刻选出了一座新的:一个中央谷仓,大家都同意把证书送到那里,因为一百个对等的人家、一百堆东西,没法就真正的布在哪里达成一致。老坦苏抱怨说,他们把烧掉的东西又重建了。萨罗回答说,谷仓握的权力,各户并不缺——把它烧了,任何一户都能把它重新装满——这与一座塔是不同的。织工们还学到了这堆东西增长时的严厉规则:一张纸条所连的那串证书,只要还在你自己家里,你想怎么改都行,但一旦邻居抄走了它,重写它就逼得每个邻居去理顺他们本以为已经完成的工作。而那个走捷径的“跳桌者”——为省一步,把整台织机一股脑扫到桌上的织工——发现她的证书记下了她从未打算记录的改动,事后也说不清哪根纱线才是重点。

有一样损失是永久的。因为一块布是按它自己的纱线、而不是按它怎样移动的故事来编址的,当一个织工把一个图案从边上挪到中央时,那堆东西只记录两幅画:图案在边上,图案在中央。意图——“我把它挪了”——从未被写下来。后来的织工只能靠注意到纱线吻合来猜它。

故事里的每样东西代表什么

故事里书里
读取浴:从布自己的纱线里,给每块布一个地址按内容寻址的对象数据库:一个以内容本身的哈希为键的键-值存储,所以相同的内容只存一份,被指向而不是被复制
没有名字的纱团,加上名字的索引板,注明日期的证书——指明一块索引板,加上它的前驱、它的织者和归档者三种层层叠加的对象类型——blob 存内容,tree 加上名字和模式,commit 加上身份、时间和祖先——存储的是快照,而不是差异
每户人家都持有整堆东西,暴风雪里哪儿也去不了也能用分布式版本控制:每次克隆都镜像完整的历史,所以每一份副本都是备份,大多数操作又快又能离线进行
织机旁那张低矮的桌子,只放这张证书打算记录的纱线,任何后来的触碰之后都要重新摆放暂存区:git add 在特定的时刻捕获特定的内容,而不是一个文件,所以之后的修改必须重新 add,提交是经过刻意塑造的
那张轻轻一撕就破的、带着一张证书的印记和一个名字的纸条分支是一个存着提交校验和的 41 字节文件,这就是为什么分支操作几乎是瞬间完成、频繁使用主题分支是切实可行的
被选出的中央谷仓,和坦苏抱怨塔被重建了这里的张力是:分布式承诺没有必须的中心,但协作又重新立起一个权威的共享仓库——一个任何克隆都能重新装满的裸仓库,而不是一个集中的单点
在家里可以随意改写纸条,但在邻居抄走之后就不行变基及其风险:重写别人已经持有的提交,逼得他们重新合并,并造成历史重复,这就是为什么对拉取请求的评审,是靠添加提交而不是强制推送来回应的
为省一步,把整台织机扫到桌上的织工-a 这个捷径悄悄地重新引入了暂存区本来要取代的、整个工作树式的提交,代价是可评审性

寓言在哪里失真

这则寓言的扭曲之处,在于把整套机制写得像一个村子里一种固定下来的安排,而书的主张是分层的、多元的,故事装不下。它装不下让一堆东西变得可读的查询词汇——status 对 diff,短哈希,引用日志名,祖先运算符,双点与三点范围——所以寓言里的织工们似乎凭直觉就了解自己的那堆东西,而这恰恰是书所否认的。它把合并压扁成一个动作,而不是两个不同的动作(历史线性时是指针滑动,历史分叉时是一个新的多父提交)。它把协作模型的整份菜单——集中式的中心、集成管理员、独裁者与副手、复刻加拉取请求、邮寄补丁——塌缩成一个谷仓,丢掉了这一点:没有哪种模型是事先正确的,而复刻加拉取请求是用命名空间的所有权取代了推送权限。它略去了:暴露共享仓库是一项没有通则的安全权衡(最快的协议是不认证的;智能 HTTP 在同一个 URL 上提供匿名读取和认证推送;完整性要靠服务器端来守卫)。它没有空间容纳分层的配置和逐文件的属性——就近的值悄悄获胜,不透明的二进制格式只有在比较之前先转成文本才可评审——也没有空间容纳通往外部系统的桥,每座桥都强加自己的纪律。最重要的是,这则寓言把它的读取浴说成是显然的事实,而书的论证大部分来自设计上的推理和小小的玩具仓库,而不是测量:速度是靠同复制目录的老工具作对比而断言的,短哈希的安全性靠的是一个项目的一份快照,而不是碰撞的界,而书承认,教人理解模型的那种手工操作对象的功夫,作为习惯是不切实际的。

《Pro Git(精通 Git)》的核心论点

全书捍卫的 10 个论点,按论证需要的顺序排列。

  1. Git 最好被理解为一个按内容寻址的文件系统,上面叠了一个版本控制的界面:区别于其他系统的,是它的快照存储,而不是命令集;它的三种对象类型层层叠加(blob 存内容,tree 加上名字,commit 加上身份、时间和祖先);日常命令不过是把底层操作自动化;因此没有哪个 GUI 或 shell 集成在能力上超过命令行。

    §10.2 Git Objects阅读这一节 →

  2. 因为每次克隆都在本地镜像完整历史,Git 又对所有内容计算校验和、几乎只往里添加数据,大多数操作又快又能离线,已提交的工作很难丢失,这就去掉了集中式系统所强加的历史的单点集中。
  3. 暂存区意味着 git add 捕获的是特定时刻的特定内容,而不是登记一个文件,所以之后的修改必须重新 add,提交应当被有意地塑造——包括逐个修改块(hunk)地——成逻辑上分开的、可评审的改动。
  4. 要知道一个仓库里实际有什么,需要的是它的查询词汇,而不是直觉:status 说出哪些文件变了,而 diff 显示行并隐藏已暂存的改动,短哈希、引用日志与祖先名字、双点/三点范围,则回答诸如什么还没合并、一次推送会发出什么这类实际的问题。
  5. 分支操作几乎是瞬间完成的,因为分支只是一个存着提交校验和的 41 字节文件,这使频繁的主题分支切实可行;因此合并不是一个操作,而是两个:历史线性时是指针移动,历史分叉时是一个新的多父提交。
  6. 协作没有唯一正确的模型,而是一份按团队规模和访问规则选择、可以混用的菜单,所以你该怎样贡献,无法事先规定——它取决于贡献者的数量、项目的工作流,以及你是否有提交权限。
  7. 复刻加拉取请求式的协作,用命名空间的所有权取代推送权限,所以维护者从不把规范仓库的写权限交给别人;又因为持久的记录是讨论加最终的合并,评审应当靠添加提交、把目标分支合并进来去回应,而不是靠强制推送一个重写过的系列。
  8. 协作应当流经一个可靠的共享中间仓库,而不是个人机器之间的直接推送,而选择怎样暴露它是一项安全决定——最快的协议是不认证的,而智能 HTTP 胜出,是因为它在同一个 URL 上提供匿名读取和认证推送——历史的完整性靠服务器端来守卫,而不是信赖客户端的自律。
  9. Git 的行为应当在合适的范围内配置——分层的配置文件设置默认值,最近的文件获胜——以及在无法逐仓库决定的地方使用逐文件的属性,这就是不透明的二进制格式怎样通过在做差异之前先转成文本而变得可评审,而不是去比较字节。
  10. 通往外部版本控制系统的桥,只能在那个系统的约束之内工作,所以每座桥都强加它自己在 Git 一侧的纪律:Subversion 和 Perforce 要线性历史,Mercurial 可以自由合并却不能重写历史。

逐章导读

每一章都在本页:论证怎样展开、关键论点、常见误读和核心概念。从目录里选一章。

第 1 章:起步

暂存区怎样改变了一个提交可以含有什么?

第 1 章是怎样展开的AI 辅助

开场:一个按历史提出的问题。 这一章根本不是从 Git 开始的。它从一个一般的问题开始:人们怎样追踪变化中的文件,并按逐步升级的顺序把这个问题回答三遍——本地版本控制,然后是集中式,然后是分布式。这个顺序是论证性的,而不仅仅是时间上的。每个系统都被引入,作为对前一个的修补,而集中式模型得到最长的陈述,恰恰因为本章在这里埋下它的核心不满:把全部项目历史放在一台服务器上,把风险集中在一个地方。作者具体地算出了这要付出什么代价——服务器停机一小时,就是一小时里没人能协作或保存有版本的改动,而一块损坏的磁盘,如果没有妥善备份,会带走整个历史。分布式模型随后到来,不是作为新奇事物,而是作为对读者已被引导去感受的一个问题的回答。只有在这番铺垫之后,Git 才以名字出现,带着它 2005 年的起源故事,这个故事与其说是为历史本身,不如说是证明这个设计是在真实的约束下锻造出来的。

展开:从架构到数据模型。 确立了分布式设置里每个客户端都镜像整个仓库之后,本章把这一事实兑现成行为。因为克隆是一份完整的备份,历史和与旧版本的比较,是从本地磁盘读出的;读日志或对照一个月前的文件做差异,不需要一次网络往返,服务器被降为一个共享机制。这就是为什么提交在飞机上也能进行。从这里起,本章提出它最尖锐的主张:使 Git 与众不同的,不是它的命令,而是它怎样思考数据。Subversion 风格的系统保存一组文件,加上随时间对每个文件所做的变更流;Git 在每次提交时记录所有文件的一份快照。作者小心地在同一口气里化解了那个显而易见的反对——没有改变的文件不会被再次存储,而是链接到数据库里已有的相同内容——这样,画面的完整和字节的重复,保持为两个不同的想法。完整性随后以同样的口吻到来:内容先计算校验和,再以该校验和来引用,存在一个四十个字符的 SHA-1 哈希之下,而不是一个文件名之下,而且几乎每个操作都只往数据库里添加东西。这两个事实合起来,就是本章许诺“已提交的工作很难在 Git 不察觉的情况下丢失或损坏”的凭据。三种状态和项目的三个区域,结束了这一块概念内容,给读者一套词汇,带进后面的命令。

转折:从理解到动手。 铰链出现在本章不再解释、开始指示的时候。安装和第一次 git config,标志着从模型转向可用的工具,口气相应地改变——简短、规定性的、机器层面的。身份设置先来,因为没有它们,别的什么也用不了。然后是 init.defaultBranch,本章严格地限定它的范围:它从 Git 2.28 起才有,它命名的是仓库初始化时创建的第一个分支。已经存在的仓库原封不动。这种谨慎是本章方法的特征——它一再把一种能力同其作用范围的确切边缘配成对。

收束:学会盘问,而不是猜测。 最后一个运动是关于自给自足。配置可以整体用 --list 检视,缩小到单个键,或者——最有启发的选项——用 --show-origin 追溯。作者解释为什么这要紧:同一个键可以合法地住在好几个配置文件里,Git 使用最后读到的那个,所以列表里的重复是意料之中的,而不是坏了。比如,询问 rerere.autoUpdate 的来源,会同时打印出决定它的文件路径和值 false,径直说出获胜的文件。这个教训超出了配置之外:当一个值让你意外时,追溯它。本章随后以指引读者去看内置的帮助收尾——git help <动词>、--help、man 手册页,或者用 -h 看简短的选项表——全都可以离线使用。这个结尾优雅地呼应了开头。一章开头是在论证不要依赖远程服务器,结束时表明连文档也已经在你的磁盘上了。

关键论点,以及支撑它们的原文

论点 1

把全部项目历史保存在一台中央服务器上,把风险集中在一个地方。

作者推理单点故障的代价:如果服务器停机一小时,这一小时里没人能协作,或保存任何有版本的改动,而如果它的磁盘损坏又没有妥善的备份,整个历史就没了。这是本章最先布下的问题,这样分布式版本控制就可以作为对它的回答被引入。

§1.1 About Version Control阅读这一节 →

论点 2

因为每次克隆都镜像整个仓库,大多数 Git 操作都很快,并且无需联网。

紧接着对集中式问题的修补,作者指出一些具体的操作:浏览项目的历史,或把当前的文件同它一个月前的状态做差异,都是直接从本地磁盘读出的,而不是从服务器取来的,提交在飞机上、不用 VPN 时也能进行,分享推迟到连上网络之后。

§1.3 What is Git?阅读这一节 →

论点 3

真正把 Git 同其他版本控制系统区别开的,是它怎样思考数据,而不是它的命令。

作者对比两种存储模型:Subversion 风格的系统保存一组文件,加上随时间对每个文件所做的改动,而 Git 在每次提交时记录所有文件的快照,对没有改变的文件,只存储一个指向已存储的相同内容的链接。主张是:Git 之所以这样行事,是这个模型的区别,而不是表面界面的区别。

§1.3 What is Git?阅读这一节 →

另外 2 个论点
论点 4

Git 被造成这样:你已提交的工作很难丢失,或在不被察觉的情况下损坏。

两个设计事实支撑这一点:内容先计算校验和,再以该校验和来引用,所以 Git 在数据库里以 40 个字符的 SHA-1 哈希而不是文件名存东西,而几乎所有的动作都只往数据库里添加数据。合起来,它们意味着一旦有了提交,未被察觉的损坏和意外的毁灭都不太可能。

§1.3 What is Git?阅读这一节 →

论点 5

当一个配置值让你意外时,追溯是哪个文件设置了它,而不是猜。

作者指出,同一个键可以合法地出现在几个配置文件里,Git 实际使用的值是最后读到的那个,这就是为什么列表里会出现重复。你不必猜,可以向 Git 询问来源:查询 rerere.autoUpdate 的来源,会同时打印出决定它的文件路径和值 false,所以获胜的文件被明确地说了出来。

§1.6 First-Time Git Setup阅读这一节 →

它是怎样运作的

本章论证所依靠的 3 个机制。

机制 1

每次克隆都是完整的备份

在分布式设置里,每个客户端都把整个仓库,包括历史,复制到自己的磁盘上。因此读日志或对照一个月前的文件做差异,都是在本地计算的,没有网络往返,服务器只在分享工作时才需要——这就是为什么在飞机上也能提交。

机制 2

复用文件的快照

Git 的每个提交不是记录逐文件的变更清单,而是保存那一刻每个文件的一幅画面。为保持高效,没有改变的文件不会再次存储;快照只是链接到数据库里已有的相同副本,所以完整性花的空间很少。

机制 3

校验和加上只增不减的写入

一切在存储之前都计算校验和,并以它的 40 个字符的 SHA-1 哈希而不是文件名来编址,所以内容的任何悄悄改变,都不再与它的名字吻合。因为几乎每个动作都只添加数据,从不丢弃,已提交的工作很难在 Git 不察觉的情况下丢失或损坏。

常见误读

你可能以为

每次提交都存一份完整的快照,一定意味着 Git 保存了从不改变的文件的重复副本,所以比只记录差异的系统浪费得多的空间。

其实

快照在它所描述的东西上是完整的,而不是在它重新存储的东西上:当一个文件没有改变时,Git 不会再次存储它,而是记录一个指向数据库中已有的相同内容的链接。画面的完整和字节的重复,是两回事。

§1.3 What is Git?阅读这一节 →

你可能以为

一旦你全局设置了默认的初始分支名,你的仓库就会用这个分支名。

其实

这个设置只在它能起作用的地方起作用:从 Git 2.28 起才有,它命名的是新仓库初始化时创建的第一个分支。已经存在的仓库不受影响,所以它们的分支名保持原样。

§1.6 First-Time Git Setup阅读这一节 →

第 1 章的核心概念

版本控制
一个随时间记录文件改动、使以前的版本可以被取回的系统。
适用处: 这里用源代码来说明,但几乎适用于任何文件类型。
分布式版本控制
每个客户端都镜像整个仓库,包括它的完整历史,所以每次克隆都是一份备份。
适用处: 消除了单点故障,但分享工作仍然依赖服务器。
快照而非差异
每个提交保存所有文件的一幅画面,对没有改动的文件只链接而不重复存储。
适用处: 与 CVS、Subversion 和 Perforce 这类基于差分的系统形成对照。
三种状态
一个文件是已修改、已暂存或已提交,分别对应工作树、暂存区和 Git 目录。
适用处: 暂存这一步可以跳过,如“Git 基础”所解释。
git config 的层级
系统、全局和本地的配置文件,每一层都覆盖前一层。
适用处: 本地是默认层级,要求处在一个仓库之内。
init.defaultBranch
一个全局设置,命名 git init 创建的第一个分支,取代默认的 master。
适用处: 只有 Git 2.28 以后才有,并且只影响新创建的仓库。
另外 2 个概念
后值覆盖前值的配置
同一个键可以出现在几个配置文件里,Git 使用的值,是它最后读到的那个。
适用处: 解释了 --list 输出里键的重复;要看是哪个文件决定的,用 --show-origin。
Git 手册页帮助
打开任何 Git 命令的完整手册的三种等价方式,外加用 -h 看简短的选项摘要。
适用处: 随处可离线使用;说到人工帮助,书则指向 IRC 频道。

读后感AI 陪读

Git 的手册,是先转化人,再教人

当作修辞而不是教学来读,这一章是一篇穿着手册衣服的皈依叙事。前三分之一根本没有在教 Git——它造了一道梯子(复制目录→RCS→CVS/Subversion→Git),每一级的弱点,就是下一级的理由,所以当 Git 到来时,它让人觉得与其说是被发明的,不如说是被推演出来的。枢纽是这样一个主张:Git 存的是快照而不是差异,这在概念上多于在物理上(文本自己承认有去重,而包文件确实做差分压缩),但这是正确的“给孩子的善意谎言”:它为以后的分支模型买单。这一章做得格外好的,是把先前的知识点名为一种隐患——“清空你的头脑”是这里最有用的一句话,承认了 Git 熟悉的动词,坐在陌生的机制之上。它做得较差的,是对它的安全和完整性许诺留有余地:“很难让系统做出任何不可撤销的事”,对对象数据库是真的,对一个初学者的下午却是假的,而 SHA-1 被说成一种保证,其实它是一个事故探测器。年龄在两个地方显现——坚持只用命令行,以及关于默认分支名的那句一笔带过,在那里,本章最平淡的文字,覆盖了最有争议的改变。还有一处书从未提及的结构性反讽:它很用力地论证分布消除了单点故障,然后把读者送入一个每个人的单点故障都是同一个网站的世界。不过,三种状态那一节配得上它抬高的嗓门——它是唯一一个跳过之后、其后的一切就变得不连贯的部分。

在 FeedLitra 里读第 1 章,每段旁边都有这些卡片。

开始读书

论证在哪里吃力

不是反驳,而是细心的读者会想追问的地方。

  1. 廉价的分支解决了机械层面的问题,却把代价转嫁到人的协调上:重命名默认分支,成了跨越依赖项目、CI 配置、构建脚本、主机、文档和未决拉取请求的一份检查清单。

    §3.3 Switching branches changes files in your working directory阅读这一节 →

  2. 本书把重写历史赞美为一门手艺——补丁级暂存、变基、整洁的逻辑提交——然而在只增不减的持久性、拉取请求文化和被桥接的仓库里,它又禁止重写,因为不论社会性的记录还是外部服务器,都吸收不了它。

    §3.12 Rebasing阅读这一节 →

  3. 有意的暂存换来精确,却增加了一个步骤,用户随后又用 -a 这个捷径绕开它,而它悄悄地重新引入了暂存本来要取代的、整个工作树式的提交。

    §2.3 Git Diff in an External Tool阅读这一节 →

  4. “分布式而没有必须的中心”的承诺,在实践中被这样的协作抵消了:它重新立起一个权威的托管仓库,而 GitHub 的约定,靠把分布式 Git 重新集中到一个枢纽上,才使它能大规模使用。

    Git on the Server阅读这一节 →

另外 4 处张力,以及 8 条证据上的局限
  1. 协议和服务器的选择,在速度与安全之间权衡,没有通则:最快的协议,恰恰是允许路由器层面的攻击者注入代码的那个,而推送时的校验和验证默认是关着的,因为在大型推送上代价昂贵。

    §4.1 The Protocols阅读这一节 →

  2. 快照存储靠放弃显式的历史换来持久性,所以重命名只能事后从内容推断,而不能作为意图被记录。

    §2.3 Git Diff in an External Tool阅读这一节 →

  3. 分层配置和逐文件属性,使团队的工作流顺畅,同时使正确性变得不可预料,因为最近的或最后读到的值悄悄获胜,而当一个方便的设置让你提交到错误的分支时,没有任何东西警告你。

    §1.6 First-Time Git Setup阅读这一节 →

  4. 本书敦促掌握底层命令,同时又认可那些有意把大部分底层隐藏起来的精选工具,而它“工具只是适合程度不同”的说法,同它把命令行排在所有工具之上的做法,别扭地并存。

    §10.1 Plumbing and Porcelain阅读这一节 →

证据上的局限

  1. 大部分论证建立在对设计的推理和示意性的命令记录上,而不是测量:速度的说法,是靠同复制目录的老工具对比来断言的,一种工作流优于另一种,是被论证的,从未被基准测试。

    §3.3 Switching branches changes files in your working directory阅读这一节 →

  2. 演示用的是小小的、刚初始化的玩具仓库,所以大规模、巨大文件或很长历史下的行为,从未展示过。

    §2.1 Getting a Git Repository阅读这一节 →

  3. 用来教内部原理的手工操作对象的工作流,被承认是不切实际的,所以它只是说明模型,而不是值得建议的做法。

    §10.2 Git Objects阅读这一节 →

  4. 关于服务器的指导是留有余地的、针对具体工具的:关于 Git 协议的警告,承认 HTTPS 除非证书被伪造,可以避免注入,留下无法量化的剩余风险,也没有哪种配置被说成对所有团队都正确。

    §4.1 The Protocols阅读这一节 →

  5. 协作建议是从稀薄的样本里概括出来的:靠添加提交来评审,只用一个单行、单文件的改动演示,而短哈希的安全,只靠一个异常庞大的项目的一份快照,而不是碰撞的界。

    §6.4 Not Only Forks阅读这一节 →

  6. 文本转换式的差异,被表明能抓住新增的文字,却承认会漏掉格式上的改动,所以它对二进制文件给出的是部分而不是完整的可见性。

    §8.2 Git Attributes阅读这一节 →

  7. 关于桥的指导是以习惯的形式陈述的,主要靠强制推送之后什么会坏来说明,并收窄为通过外部服务器协作,没有顾及需要把 Git 和旧服务器都当作活端点的团队。

    §9.1 Git as a Client阅读这一节 →

  8. 周边的材料是一份过时的快照:对特定 GUI、IDE 和 shell 的判断,随这些产品的变化而过时;正文只标注了一个开源项目的某个提交哈希;而“一章就足以让读者有用”的说法,靠的是作者们自己的玩笑。

    §A.1 Graphical Interfaces阅读这一节 →

全书概念导图

5 组概念:通俗的叫法、一句话解释,以及原书自己的用语。

1 · Git 保存的是快照,不是差异
每个提交都保存项目的完整画面
每个提交保存所有文件的一幅画面,对没有改动的只链接而不重复存储:blob 保存没有名字的内容,tree 把模式、名字和哈希配对,commit 指名一棵树,加上父提交、作者和提交者。因为不存储重命名的元数据,重命名是事后从内容推断出来的。原书用语: Snapshots, not differences (blobs, trees, commits)
底层是一个按自己的哈希来归档内容的存储
Git 命令之下的存储机制:一个以内容本身的哈希为键的键-值存储,每个对象是一个“类型加大小”的头部再加内容,经 SHA-1 哈希、zlib 压缩,存到 .git 里一条“2/38”拆分的路径下。原书用语: Content-addressable object database
每次克隆都持有整个历史,所以工作可以离线
版本控制随时间记录文件的改动,这样以前的版本可以被取回;在 Git 的分布式设计中,每次克隆都镜像整个仓库及其完整历史,所以每一份副本都是备份,大多数工作不需要服务器,代码可以在许多对等方之间流动,而不只是经过一个枢纽。原书用语: Distributed version control
给脚本用的低层命令,给人用的友好命令
底层命令是适合写脚本的低层构件;高层命令是叠在它们之上的、友好的日常命令。原书用语: Plumbing and porcelain
2 · 有意地构造每一个提交
文件处在三个地方之一:已编辑、已排队、已保存
一个文件是已修改、已暂存或已提交,分别对应工作树、暂存区和 Git 目录;已跟踪的文件,是 Git 从上一次快照或从暂存中得知的文件,而 .gitignore 的通配符模式,告诉 Git 其余的文件里,哪些要完全跳过。原书用语: Three states and file tracking
挑出你想要放进下一次保存的确切几行
一个存放区,你在那里确切地组装下一次提交所记录的内容;文件或个别的差异块可以一次暂存一个,而 -a 则为已跟踪的已修改文件省去这一步。原书用语: Staging area and patch-level staging
一个提交一个想法,分享之前先修好上一个
使改动可评审的习惯——每个提交一个逻辑变更集,没有空白错误,一个约 50 个字符的祈使句式摘要加正文——连同一个提交所携带的两种身份(作者和提交者),以及修补,它会整个儿替换最后一个提交。原书用语: Commit hygiene and amending
用短名字或范围指向过去的提交,而不用完整哈希
不用完整哈希就指向提交的办法——SHA-1 的短前缀,像 HEAD@{5} 这样的引用日志名,用 ^ 和 ~ 表示的祖先——以及双点范围(从一个引用可达、但从另一个引用不可达)和三点范围(从其中任何一个可达、但不是两者都可达)。原书用语: Commit naming and range syntax
3 · 分支很廉价,合并各不相同
分支只是一个指名某个提交的小文件
用一个可读的名字,保存一个提交校验和的普通文件——分支是一个 41 字节的可移动指针,HEAD 指明你所在的分支,而附注标签是一个真正的对象,带着打标签者、日期、信息和签名。原书用语: References: branches, HEAD, and tags
有时指针滑过去;有时 Git 把工作缝起来
当被合并的提交正好在前面时,Git 只是把指针往前滑;当历史分叉时,它把两个顶端同它们的共同祖先,合并成一个有多个父提交的提交。原书用语: Fast-forward and three-way merge
重放提交以得到整齐的一条线,决不在共享的历史上这样做
把一个分支的提交重放到另一个分支上,以得到线性的历史;重写别人已经持有的提交,逼得他们重新合并,并造成历史重复。原书用语: Rebase and its perils
保留稳定的线,再为每个新想法开短命分支
一个团队的分支安排:稳定的长期的线,加上持有一个工作主题的短命分支,它们可以被单独评审、重写或丢弃。原书用语: Long-running and topic branches
4 · 和团队共享工作
挑一种适合你团队规模的传递代码的办法
在人与人之间传递代码的安排菜单:每个人都往里推送的集中枢纽,从开发者的公开克隆里拉取的集成管理员,向仁慈的独裁者提供东西的副手,GitHub 的“复刻-分支-推送-拉取请求”循环,以及没有共享服务器时邮寄的补丁系列。原书用语: Collaboration workflow models
请维护者拉取你的工作,并在那里讨论它
请维护者合并你克隆或复刻里的工作的一个请求——要么是带着差异、逐行评审和讨论的网页讨论串,要么是 git request-pull 的输出。原书用语: Pull request
大家共享的一份服务器上的裸副本,小心地开放
裸仓库只含有 .git 的数据,没有工作目录,纯粹充当接收推送的协作点;本地、HTTP、SSH 和 Git 协议以不同的权衡到达它——SSH 密钥标识用户,git-shell 限制登录,不认证的守护进程服务 9418 端口,而智能 HTTP 把认证交给网络服务器。原书用语: Shared repository and transfer protocols
本地的昵称,以及追踪托管副本的指针
远程是别处托管的项目版本;远程跟踪分支记录它的分支在上一次联络时的位置,跟踪分支把本地分支同上游接起来,而“源:目的”形式的引用规格说明哪个本地分支更新哪个远程分支。原书用语: Remotes, tracking branches, and refspecs
5 · 调校 Git,桥接别的工具
把默认值设在它该在的地方;最近的设置获胜
系统、全局和仓库的配置文件按顺序读入,最近的文件(以及最后读到的值)决定生效的设置——包括 init.defaultBranch——而逐路径的 .gitattributes 条目,则对特定文件覆盖仓库范围的行为:怎样做差异、合并、过滤,或处理行尾。原书用语: Layered configuration and Git attributes
一个让你在另一个工具的仓库里工作的适配器
一个适配器——git svn、Mercurial 远程助手、Git Fusion 或 git-p4——让开发者用 Git 工作,同时另一个系统的服务器仍是事实的来源。原书用语: VCS bridge
把 Git 放到你工作之处的编辑器、客户端和 shell
为一件事而造的面向任务的工具,暴露精选功能子集的面向工作流的客户端,以及在 Bash、Zsh 或 PowerShell 里显示分支和状态信息的补全或提示符脚本。原书用语: Git tooling and shell integration

读后感AI 陪读

关于分支的一句话,让 Pro Git 的螺旋穿过所有层次

从头读到尾,Pro Git 是一本关于一个主张和它的后果的书:Git 是一个按内容寻址的键-值存储,上面写了一个版本控制的界面,一旦这个底层可见,几乎一切让用户困惑的东西就都消解了。脊梁沿着简介自己宣布的一道不断放宽的螺旋走——一个自足的核心(基础),你单靠它就能生存,然后是一章转换(分支),其中的一句话“分支是一个轻量的可移动指针”,是整本书的铰链,然后是可选的深度,书一直告诉你哪些部分可以跳过。运动是从对象,到约定,到政治,再回落到内部原理:廉价的分支使短命的主题分支切实可行,这把困难推向外部,推向远程(远程状态只是缓存的快照)和重写(变基以替换提交为代价换得更干净的历史,因此只对未共享的工作是安全的);服务器一章随后把基础设施泄成一个裸仓库加一个选定的传输协议,是一份菜单,而不是正确答案;分布式 Git 承认,一旦 Git 去掉了通往任何拓扑的技术障碍,填满真空的就是社会性的东西——决定贡献方式的是写权限,而不是架构;GitHub 展示了叠在普通 Git 之上的约定,直到这个网站成了可编写脚本的平台;Git 工具和自定义 Git 把一切重新框定为对三棵树和三层策略的操作,其中最局部的设置获胜,钩子把约定变成强制;桥接一章承认每个外部系统都强加它自己在 Git 一侧的纪律;而内部原理最终展示了这一切之下的地板。最尖锐的判断是:Git 与众不同的特点是它的数据模型,而不是界面,它的命令表面是叠在一个干净存储之上的历史偶然——这就是为什么书必须不断地教这个模型,好让这些命令可以忍受;自定义 Git 里章节的安排本身就是一个论证,一条单一的覆盖规则展开成一套词汇;策略的强制,只有在服务器端的拒绝由客户端钩子来呼应时才行得通,这使用户免于痛苦的历史手术;以及附录里诚实的、略微自我拆台的承认:工具的差别不在能力,而在它们让 Git 的哪个子集成为可想象的,命令行仍然胜出。我想对读者说的是:不要把它当作需要时查阅的参考。把基础和分支当作一个连续的论证来读,直到指针模型显得显而易见,然后早点读内部原理,而不要留到最后——第二遍读,书要容易得多,因为最后一章解释了第一章。这两极之间的一切是菜单,不是课程:按团队的规模和访问规则来选择你的工作流、服务器和工具,而不是按读起来像最佳实践的东西。并带上书挣得、而不是断言的那条持久的规则——Git 几乎从不丢失你提交过的东西,所以唯一真正的危险,是重写别人已经拥有的历史。

关于作者:斯科特·沙孔

GitHub 和 GitButler 的联合创始人;Pro Git 的合著者

  • GitButler 的联合创始人,这是一家总部在柏林的版本控制客户端公司2
  • 联合创办 GitHub,并在8年里帮助它从4位联合创始人成长到450名员工2
  • 在 Logical Awesome 担任 Git 布道者和 Ruby 开发者,为 GitHub.com 工作3
  • Pro Git,第2版(2014),与本·斯特劳布合著,Apress——即本书1
  • 通过他的孵化器 SCNE 做投资人4
  1. Pro Git 书籍(git-scm.com)
  2. 关于——斯科特·沙孔
  3. 用 Git 和 GitHub 把开发加速一百万倍——RubyKaigi 2009
  4. GitButler,与斯科特·沙孔和基里尔·维德洛夫对谈——Rust in Production 播客(corrode.dev)

网络书评怎么说

各英文博客和豆瓣上的评论者看法趋同:Pro Git 写得清楚、节奏得当、插图丰富,分支各章和结尾的内部原理一章被单独点出来作为亮点。读者说它带他们从基础走到自信的日常使用,至今仍是一本方便的参考书。有一位评论者提醒说,它的节奏适合有经验的开发者,不适合新手。

综合已发表书评的看法;不展示评分,完整书评请点来源链接。

  1. 书评:Pro Git · 道格·赫尔曼
  2. 书评:斯科特·沙孔的 Pro Git
  3. 书评——Pro Git
  4. 我书架上的书:Pro Git
  5. 最好的Git进阶材料(Pro Git)书评

来源与授权

CC BY-NC-SA 3.0 · 非商业原书

  • 《Pro Git》第二版,作者 Scott Chacon、Ben Straub,Apress 出版。采用知识共享 署名-非商业性使用-相同方式共享 3.0 许可协议(CC BY-NC-SA 3.0)。可在 git-scm.com/book 免费阅读。免费阅读原书 · 许可证
  • 摘要、论点、批注和读后感由 FeedLitra 根据全书原文 AI 辅助生成,引文已对照原文核对。最近更新:2026 年 10 月 21 日。

非商业许可:按许可,本页不引用原书文字,只有 FeedLitra 自己写的说明,每处都只链接回官方免费版本的对应小节。

在 FeedLitra 里一边读《Pro Git(精通 Git)》,一边看这些卡片。

开始读书