出事了,先看这张表
最后一章,把全书压成一张表。但真正该被记住的不是这张表——是它最后一列背后那个你现在已经能自己推的判据。
那张表
按「你想干什么」查,不是按命令名查。红色的那几条,敲之前先想三秒。
它其实只有一列是重要的
那就是最后一列,风险。而那一列的判据,你现在已经能自己推了——问一句:这个操作动的是什么?
动贴纸(branch、switch、reset --soft、tag)
→ 随便来。贴纸是 41 个字节(第 6 章),reflog 记着它去过哪儿(第 9 章)。没有任何东西会丢。
造对象(add、commit、stash、merge、cherry-pick、revert)
→ 安全。对象只增不改(第 5 章)。最坏的结果是多了几个没人要的对象,等着被 gc。
动工作区(restore、reset --hard、checkout -f、clean)
→ 停三秒。那是整个系统里唯一一个 Git 没有替你备份的地方(第 12、20 章)。
而「停三秒」之后该做的事,永远是同一句:
$ git stash --include-untracked
它把你的工作区写成提交对象——那一瞬间,你的改动从第三类跳到了第二类。成本一条命令,收益是把「可能永久丢失」变成「一定找得回来」。
三条能救命的命令
如果这本书你只记住三条命令,应该是这三条:
# 1. 我在哪、发生了什么(90% 的「出事了」在这里就有答案) $ git reflog # 2. 那个东西到底是什么(绕过一切美化,直接读对象) $ git cat-file -p <哈希或引用> # 3. 图上是什么关系(分叉了没有、能不能快进) $ git log --graph --oneline --all
它们的共同点:都在问「客观状态是什么」,而不是「我该敲哪条命令」。
这也是这本书想给你的那个转变——从「记住怎么做」变成「看得懂发生了什么」。后者一旦有了,前者自己就来了。
回到开头那六个问题
首页问过六个「你能说出为什么吗」。现在逐条对答案,每条一句话:
| 问题 | 答案 | 章 |
|---|---|---|
为什么 checkout 一个分支是瞬间的? | 切分支只改一个 41 字节的文件;工作区靠 Merkle 剪枝,只写真正有差异的那些路径,代价与仓库大小无关 | 3、6 |
为什么 rebase 之后提交号全变了? | 哈希由内容决定,而 parent 就在内容里——改一个,后面全部连锁改名 | 2、4、18 |
| 为什么删掉大文件仓库没变小? | 提交是快照,删除只是「下一张快照里没有它」;那个 blob 还被之前每个提交的树指着,完全可达 | 1、5、21 |
为什么 reset --hard 后提交能救、改动不能? | 提交被写成过对象,reflog 引用着它;没 add 过的改动从没进过对象库,Git 手上没有那份数据 | 9、12、20 |
| 为什么改了不同文件也会冲突? | 目录本身也是对象。两边都动了同一棵 TREE 的结构,冲突发生在树这一层 | 3、17 |
git status 那两列字母在比什么? | 三棵树之间的两道缝:左列 = 索引↔HEAD,右列 = 工作区↔索引 | 10 |
六个看起来分属六个话题的问题,答案全都落在同样那几句话上。这就是「有模型」和「靠背命令」的区别。
Git 不存 diff,存快照。
每个提交指向那一刻整棵目录树的完整样子。而对象的名字就是它内容的哈希,所以没改过的东西天然只存一份——去重不是一个功能,是一个不可违反的性质。
分支只是一张贴纸。
41 个字节,写着一个哈希。你怕的所有命令——reset、rebase、checkout、强推——都只是在撕贴纸、贴贴纸,外加往那座只增不改的对象库里再扔几个新对象。
合起来就是 Git 的全部形状:一座只增不改的对象库,加上一把随手可以撕来贴去的贴纸。
接下来读什么
这本书讲的是模型。想再往下走,三个方向:
- 《Pro Git》第 10 章 Git Internals——官方的内部原理章节。你现在读它会觉得轻松得多,因为所有概念都已经有画面了。
- Git 源码里的
Documentation/technical/——packfile 格式、index 格式、协议规范的一手文档。比想象中好读。 - 自己写一个——用任何语言实现
hash-object、cat-file、write-tree、commit-tree。这本书里那台迷你 Git 就是这么来的,加起来不到三百行。当你写出的哈希第一次和真 git 对上时,那种「原来就这样」的感觉很值得体验一次。
《独占》——第 11 章讲 git status 为什么慢,答案是几万次 stat() 系统调用。那本书第 3 章有一张完整的价目表,告诉你每次跨越用户态/内核态那道线要付多少钱。
《密语》——这本书从头到尾靠 SHA-1 给对象命名,但用的是它的确定性而非抗碰撞性(第 2 章)。那本书会讲清楚这两者的区别,以及 SHA-1 到底是怎么被攻破的。
《当真》——PostgreSQL 的 MVCC:旧版本不删除、新版本另存一份、靠可见性规则决定你看到哪个。那和 Git 的对象库是同一种思路的两个实现——只增不改,读的时候再拼装。两本书对照着读会很有意思。
把这八行配置贴进终端,然后就可以合上这本书了:
git config --global merge.conflictStyle zdiff3 git config --global rerere.enabled true git config --global diff.algorithm histogram git config --global diff.colorMoved zebra git config --global pull.rebase true git config --global fetch.prune true git config --global rebase.autosquash true git config --global alias.pushf 'push --force-with-lease'
然后养成两个动作:
危险操作之前——
git stash --include-untracked git branch backup-$(date +%m%d-%H%M)
不知道发生了什么的时候——
git reflog
这两个动作加起来,能覆盖你未来 95% 的 Git 惊魂时刻。
这本书的最后一句话
Git 从来没打算吓唬你。它底下只有一座按内容寻址的键值库,和几张 41 字节的贴纸——那个「一不小心就完蛋」的名声,绝大部分是模型不清楚带来的错觉。现在模型有了:看见任何一条命令,先问它动的是贴纸、是对象、还是工作区。
剩下的,去仓库里试就行了。反正只要 commit 过,它就几乎不可能真的消失。