如何真正理解"Git 保存的是文件快照"这一句话?

2014-07-04 16:54:26 +08:00
 kzing
在<Pro Git>里有这么句话:
"Git 保存的不是文件差异或者变化量,而只是一系列文件快照"

文件快照是 Git 脱颖而出于版本管理的绝活, 但看过了许多文章, 但还是将懂未懂的感觉.

大家是怎么理解Git 的"文件快照"这一含义呢?
13710 次点击
所在节点    git
32 条回复
chloerei
2014-07-04 16:55:34 +08:00
就是每次修改都是整个文件保存。
chloerei
2014-07-04 16:57:18 +08:00
于此对比,有些版本管理工具是保存每个版本之间的变化,这样虽然总文件体积小,但是每检出一个文件都要从最开始的版本一个个修改叠加上去,很慢。
kzing
2014-07-04 17:07:04 +08:00
@chloerei 这么说的话, 那就算是一个非常非常小的改动, 提交会也会有一个新的完整的文件诞生咯? 那这样, 随着版本的增多, 或者对于文件特别大的项目, Git 不会很吃力吗?
chloerei
2014-07-04 17:16:01 +08:00
@kzinglzy 是的,如果项目很大,git 库会更大。git 设计出来是管理 linux 内核的,很多巨型项目(例如 Android)也是用 git,跟着走应该没问题。

Facebook's git repo is 54GB https://news.ycombinator.com/item?id=7648237

另外 git clone 的时候可以指定拷贝深度,减少拷贝体积。
jsonline
2014-07-04 17:17:13 +08:00
@kzinglzy 本地复制文件能有多吃力。分析差异才吃力。
xujialiang
2014-07-04 17:18:13 +08:00
貌似错了吧?
blacktulip
2014-07-04 17:18:40 +08:00
Smaller Space Requirements

Git's repository and working directory sizes are extremely small when compared to SVN.

For example the Mozilla repository is reported to be almost 12 Gb when stored in SVN using the fsfs backend. Previously, the fsfs backend also required over 240,000 files in one directory to record all 240,000 commits made over the 10 year project history. This was fixed in SVN 1.5, where every 1000 revisions are placed in a separate directory. The exact same history is stored in Git by only two files totaling just over 420 Mb. This means that SVN requires 30x the disk space to store the same history.

One of the reasons for the smaller repo size is that an SVN working directory always contains two copies of each file: one for the user to actually work with and another hidden in .svn/ to aid operations such as status, diff and commit. In contrast a Git working directory requires only one small index file that stores about 100 bytes of data per tracked file. On projects with a large number of files this can be a substantial difference in the disk space required per working copy.

As a full Git clone is often smaller than a full checkout, Git working directories (including the repositories) are typically smaller than the corresponding SVN working directories. There are even ways in Git to share one repository across many working directories, but in contrast to SVN, this requires the working directories to be colocated.
dorentus
2014-07-04 17:19:17 +08:00
dorentus
2014-07-04 17:27:01 +08:00
@dorentus 这段:

While that's true and important on the conceptual level, it is NOT true at the storage level. Git does use deltas for storage. Not only that, but it's more efficient in it than any other system. Because it does not keep per-file history, when it wants to do delta-compression, it takes each blob, selects some blobs that are likely to be similar (using heuristics that includes the closest approximation of previous version and some others), tries to generate the deltas and picks the smallest one. – Jan Hudec Jan 3 '13 at 13:51
est
2014-07-04 17:27:06 +08:00
@chloerei 就是不能指定子路径clone。。。。
akfish
2014-07-04 17:30:15 +08:00
要真正理解的话,了解一下Git内部实现,并且亲自从Git最底层的命令撸一次常见命令:
http://git-scm.com/book/en/Git-Internals

简单的说,Git把文件的每一个版本存储为blob文件,并不做diff,只有在pack的时候,才会有算法按一定的策略计算delta。
akfish
2014-07-04 17:31:53 +08:00
akfish
2014-07-04 17:34:53 +08:00
Git唯一会计算差异的地方就是pack,关于pack算法,参见:
https://github.com/git/git/blob/master/Documentation/technical/pack-heuristics.txt
dorentus
2014-07-04 17:38:11 +08:00
@dorentus 这评论里面说的应该是 git-gc 所做的事情吧。

按我的理解,git gc 压缩完之后,历史 commit 就不一定每个都是完整的快照了。然后由评论里所说,由于 git 保存的不是每个文件的差异,而就是一个个文件,它压缩的时候可以方便地找出相似的文件来一起压缩,不少情况下反而会更省空间。
generic
2014-07-04 17:39:05 +08:00
@chloerei 你的理解是错误的。

git的底层(plumbing layer)*接口*处理的单位是整个文件(以及整个目录树等等),这并不意味着底层*实现*直接在磁盘上存快照而不使用delta压缩。

进入任何一个git项目的.git/objects目录,那些以两位十六进制数命名的子目录里保存的就是未压缩的原始对象(文件快照),但这只是最近创建的还没来得及压缩的一小部分对象。在pack目录中你会看见以压缩形态存储的大部分对象。

通过隐藏“文件的版本之间的delta”这一细节而只暴露“文件快照”这一概念,git底层反而实现了更高的存储效率。因为delta不再局限于一个文件的相邻版本之间。如果你的项目里有两个文件内容相同,git只会存储一份对象;如果两个文件内容相似,git也可以对它们作delta压缩。这都是暴露了delta机制的系统,如svn, bzr没有做到的。
chloerei
2014-07-04 17:44:09 +08:00
@generic 提问,如果由 A 改为 A',两个文件不同了,git 储存了两份文件,这个有误吗?我知道文件如果没变化,就不会产生新的文件。
generic
2014-07-04 17:50:01 +08:00
@chloerei 取决于你把说呢么叫做“储存了两份文件”。

概念上,git底层(实际上是一个CAS: content-addressed storage)存储了两个对象。

实际上,在你磁盘文件系统上则未必有两份文件。在你刚刚commit的时候A'很可能以独立的文件存在;在你手动运行git repack或者由git自动运行之后,则以pack的形式压缩在一起。
dorentus
2014-07-04 17:51:43 +08:00
看来我把 git repack 和 git gc 搞混了..
chloerei
2014-07-04 17:54:25 +08:00
@generic ……两个数据块。因为直觉上 A + A' 的体积要比 A + ' 大,还是说 pack 压缩后并不占用 A + A' 的体积,相当于 A + '?
generic
2014-07-04 17:56:18 +08:00
@dorentus git gc除了删除垃圾文件,也会自动调用git repack,所以你没搞混。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/120979

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX