Git快照(snapshot)到底该如何理解?或者说如何从文件系统的角度理解快照(snapshot)?

使用git也有一段时间了,不过最近突然冒出一个问题——在git中快照究竟是什么?使用过git的人应该都会发现在使用git管理自己的代码仓库时,随着代码更改次数的增加,代码仓库的实际大小并不会有太大变化,这的确是很神奇。而魔法师git所用的“道具”就是快照(snapshot),不过这一名词过于抽象,真的很让人费解!
我在网上看过很多人的解释,有些人虽然解释得很通俗,不过还是让人摸不着头脑:

想象一下,给一张桌子拍一张照片,纪录了桌子上所有物品的位置、状态,这样就可以称之为快照了。
我们不必存储所有的物品,只需存储这个照片就可以了,下一次想恢复以前的状态的时候,只需要翻出当时的那张照片,再把物品按照那张照片里的位置摆放一下就OK了。

--来自V2EX:https://www.v2ex.com/t/124019

当然,还有一些错误的认识。有不少人认为git存储的是每次文件相对于基本文件的差异,其实这是很多人的误区。的确有不少版本控制软件就是这样做的,如CVS、Subversion等等,但git不是,它记录的是快照。但重点是这快照究竟是什么呢?

在Progit的第一章中,对记录差异和记录快照进行了对比,下图是记录差异的原理图:

difference

这类版本控制软件记录的是一组基本文件和每个文件随时间逐步积累的差异,这个看起来非常好理解。但是git记录快照就稍微有点儿抽象了:

图片描述

在英文版中这样描述:

Every time you commit, or save the state of your project in Git, it basically takes a picture of what all your files look like at that moment and stores a reference to that snapshot.

通俗的翻译就是:每次你提交或者要在git中保存项目的状态时,git仅仅是制作快照(像拍照一样记录所有文件在那个时刻的样子),并保存指向这个快照的引用。之所以把英文引用过来,是想让大家注意一下里面的这个描述:

takes a picture of what all your files look like at that moment

这一句的意味在中文版Progit中是感受不出来的。

虽然上面的解释让我有了大致的理解,但是还是很有些似懂非懂,模棱两可。

另外,在书中的这样一句话好像点醒了我,虽然并没有直接解释快照(snapshot),但是为我指明了方向:

Git更像是把数据看作是对小型文件系统的一组快照

也许快照是文件系统中的概念或者技术,而git只是利用这一技术,所以Progit中才没有详细介绍这一技术,虽然其中频繁提到快照(snapshot)这个词。我在提问之前,我在维基百科上看过这一概念,但所能知道也就是:来自照相领域的概念,是指特定时间点的一个状态。

如果我的猜想是正确的话,我希望那些懂文件系统或者对操作系统十分了解的大神可以为我们通俗地解释下这个概念或者技术

note:为了使更多的人可以看到我的问题,我将可能涉及到的标签类型都加了上去。

阅读 17.2k
6 个回答

你把progit繼續讀到blob的部分, 可能就會覺得那個快照的圖示不抽象了.

git做的事大致是這樣的:

----------
在創建Version 1時:

計算A B C的sha1, 然後在.git/object中, 創建以下文件 (即"快照")
- 文件名 sha1(A), 內容A
- 文件名 sha1(B), 內容B
- 文件名 sha1(C), 內容C

最後把這幾個sha1寫到一個新文件, 這個新文件就是Version 1.
----------
在創建Version 2時

計算A1 B C1的sha1, 然後發現sha1(B)這個文件名已經有了, 所以只需要再創建兩個文件:
- 文件名 sha1(A1), 內容A1
- 文件名 sha1(C1), 內容C1

最後把A1 B C1的sha1寫到新文件, 這個新文件就是Version 2
----------
以下略

(這不是一個精確例子, 實際上git還要在文件中保存一些元數據, 這不影響你的理解)


"快照" 經常指概念上的複製 + 物理上的引用.而不是真的有兩份.

這種快照往往和 copy-on-write 機制共用來減少數據存儲. 比如 btrfs/zfs 可以給一個文件系統快照.

有此概念的不止文件系統, 比如virtualbox可以給一個虛擬機快照, Linux的內存管理也有 copy-on-write.

除了节约空间外, 快照可以用來實現回滾等功能.


另外多說一句

有不少人认为git存储的是每次文件相对于基本文件的差异,其实这是很多人的误区。

git 可能 會使用diff來存儲 ("delta compression"), 比如把多個blob壓縮成git pack的時候.

這是git底層存儲系統的細節, 不影響其原理. 既然每個blob是不變的, 而且可以隨時還原出來, 邏輯上認為這些快照同時 "存在" 沒有什麼問題.

简单来说,(简化的结构,详细的慢慢补充)

当保存一个文件的时候,git 把这个文件的信息和这个文件的内容存储在一个文件里,然后求得这个文件的 sha1 作为文件名(sha1的前两位/sha1剩余部分,见.git/objects)。
保存一个目录,就是把目录信息,以及其中文件通过上述方式求得的 sha1 保存在一个文件里,同样求得这个文件的sha1 作为文件名。
一个提交就是,把提交的信息(比如父提交的sha1等),以及此次提交所包含的目录/文件的上述方式求得的 sha1 放在一个文件里,然后把其 sha1 作为文件名。

当提取某次提交的时候,需要给出某次提交的 sha1 然后 git 就可以通过sha1 找到这个保存这个提交所在的文件,然后这个文件里有其所有包含文件的 sha1 ,然后就可以找到所有文件。
分支的名称其实就是某次提交 sha1 的一个别名而已(见./git/refs/heads

如果提交 a 和提交 b 都包含同一个文件file,那么,file 以及其信息放在一个文件里,然后 a 和 b 里面其实仅仅保存了这个文件的 sha1

在 git 里面,如果仅仅改变一个文件的文件名(包括改变目录),那么 git add ; git status 是可以看到 git 是可以检测到这个重命名的。 因为他们的 sha1 是相同的。

新手上路,请多包涵

好像都没有给出快照的术语解释啊

clipboard.png
某次修改,你修改了哪个文件,比如修改了A,就会物理产生一个A1文件(真正在磁盘上多出一个A1文件,这时磁盘上有A和A1,2个实际的文件)。该次修改没有真正修改的文件,比如B,自然不会傻到在磁盘上存储一个B1文件,这是磁盘上只有B这1个文件。

这个图幼儿园的都能读懂吧

git是通过快照来管理版本的,快照就是git的文件系统,就是我们说的汉语词典的索引,每次commit就是创建一份快照,并给快照起一个编号,这个编号就是HEAD。

新手上路,请多包涵

快照相对于一个文件来说就是在硬盘里把这一块内存锁住,后续的人不能再动这一块空间了,需要用的时候把这一块拿出来就行了,所以当一个文件A被修改时,理论上不是动了这个文件本身,而是生成了一个文件A1,A1和A又构成了一个空间,把这个空间再次锁住,也就是第二个快照,以此类推。所以Gitpro里面说“Git Generally Only Adds Data”

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Microsoft
子站问答
访问
宣传栏