Jan Hudec添加了此重要评论: 虽然这在概念层面上是正确的和重要的,但在存储层面上却不是这样。
Git使用增量存储。
不仅如此,它比任何其他系统都更有效,因为它不保留每个文件的历史记录,when it wants to do delta compression,它获取每个blob,选择一些可能相似的blob(使用包括先前版本和其他版本的最接近近似值的算法),尝试生成增量并选择最小的增量。(通常,取决于配置)利用其他类似的文件或比以前更相似的旧版本.“包窗口”参数允许以delta压缩质量换取性能.默认值(10)一般会给出不错的结果,但当空间有限或为了加快网络传输时,git gc --aggressive使用值250,这使得它运行得非常慢,但为历史数据提供了额外的压缩。
3条答案
按热度按时间koaltpgm1#
Git确实为每个提交包含了所有文件的完整副本,除了对于Git存储库中已经存在的内容,快照将简单地指向所述内容而不是复制它。
这也意味着具有相同内容的多个文件仅存储一次。
所以快照基本上是一个提交,指的是目录结构的内容。
一些好的参考资料是:
你告诉Git你想用git commit命令保存一个项目的快照,它基本上会记录一个清单,显示项目中所有文件在那个时候的样子
Lab 12说明如何获取以前的快照
这本书对快照有更全面的描述:
Git与其他任何工具包(包括Subversion和朋友)的主要区别在于Git处理数据的方式。
从概念上讲,大多数其他系统将信息存储为基于文件的更改列表。
x1c 0d1x的数据
Git并不是这样看待或存储数据的,相反,Git认为数据更像是一组迷你文件系统的快照。
每次你在Git中提交或保存项目的状态时,它基本上会拍摄一张照片,显示所有文件在那一刻的样子,并存储对该快照的引用。
为了提高效率,如果文件没有更改,Git不会再次存储该文件--只是一个指向它已经存储的前一个相同文件的链接。
Git认为它的数据更像下面这样:
的
这是Git和几乎所有其他VCS的一个重要区别,它让Git重新考虑了大多数其他系统从上一代复制的版本控制的几乎每一个方面,这让Git更像是一个迷你文件系统,上面构建了一些非常强大的工具,而不仅仅是一个文件系统。
另请参阅:
.git/
become huge over time?“Jan Hudec添加了此重要评论:
虽然这在概念层面上是正确的和重要的,但在存储层面上却不是这样。
Git使用增量存储。
不仅如此,它比任何其他系统都更有效,因为它不保留每个文件的历史记录,when it wants to do delta compression,它获取每个blob,选择一些可能相似的blob(使用包括先前版本和其他版本的最接近近似值的算法),尝试生成增量并选择最小的增量。(通常,取决于配置)利用其他类似的文件或比以前更相似的旧版本.“包窗口”参数允许以delta压缩质量换取性能.默认值(10)一般会给出不错的结果,但当空间有限或为了加快网络传输时,
git gc --aggressive
使用值250,这使得它运行得非常慢,但为历史数据提供了额外的压缩。clj7thdc2#
Git逻辑上会将每个文件存储在其SHA-1哈希值下。这意味着如果你在一个仓库中有两个内容完全相同的文件(或者你重命名了一个文件),那么只会存储一个副本。
但这也意味着,当你修改文件的一小部分并提交时,会存储文件的另一个副本。Git解决这个问题的方法是使用打包文件。(实际上,不仅仅是文件,但也包含提交和目录信息的对象)从仓库中收集并压缩到一个包文件中。包文件使用zlib压缩。类似的文件也是delta压缩的。
拉或推时也使用相同的格式(至少在某些协议中),因此这些文件不必再次重新压缩。
这样做的结果是,包含整个未压缩的工作副本、未压缩的最近文件和压缩的旧文件的Git仓库通常相对较小,比工作副本的大小小两倍。这意味着它比包含相同文件的SVN仓库小,即使SVN没有在本地存储历史。
gpnt7bae3#
OP:Git中的 *snapshot * 是什么意思?Git在每次提交中都会复制所有文件,这是真的吗?
* Git中的snapshot是什么意思 *
在Git中,所有提交都是项目的 * 不可变快照 *(忽略的文件除外)。这意味着每个提交都包含整个项目的唯一表示,**而不仅仅是提交时修改或添加的文件(增量)。除了对实际文件的引用外,每个提交还注入了相关的元数据,例如提交消息,作者(包括时间戳),提交者(包括时间戳),以及对父提交的引用;所有这些都是不可变的!
由于commit(或commit对象,因为它的正式名称)是不可变的整体,试图修改它的任何内容是不可能的。提交一旦创建,就永远不会被篡改或修改!
* Git如何在内部存储文件 *
从Pro Git这本书中,我们了解到:
Git是一个内容可寻址的文件系统。很好。这意味着什么?这意味着Git的核心是一个简单的键值数据存储。这意味着你可以将任何类型的内容插入到Git存储库中,Git将返回一个唯一的密钥,你可以稍后使用它来检索该内容。
所以让我们看看下面的插图来弄清楚上面的语句到底是什么意思,以及Git如何在内部存储数据(特别是文件)。
x1c 0d1x * 包含三次提交的简单提交历史,包括实际数据(文件和目录)在Git内部存储的概述。左手显示实际快照,与上一次提交相比的“增量变化”以绿色突出显示。最右边是用于存储的内部对象。*
Git在其内部存储中使用了三个主要的 * 对象 *:
*Commitobject(高级快照容器)
*Tree对象(底层文件名/目录容器)
*Blobobject(底层文件内容容器)
在一般意义上将文件存储在Git中(例如内容+文件名/目录)需要一个 blob 和一个 tree; blob只存储文件内容,而tree存储引用blob的文件名/目录。因此,一个树可以同时引用blob和tree。从高层的Angular 来看,你不必担心 * blob * 和 * tree *,因为Git会在提交过程中自动创建它们。
**注意:**Git自下而上计算所有的哈希(键),从blob开始,移动通过任何子树,最终到达根树-将键作为其直接父树的输入。这个过程产生了上面可视化的结构,在数学和计算机科学中称为directed ascyclical graph(DAG),例如所有引用只在一个方向上移动,没有任何循环依赖。
进一步分析可视化示例
通过仔细查看上面的历史记录,我们可以看到在最初的 * C 0 * 提交中添加了两个空文件,
src/index.js
和.gitignore
--但是只创建了一个 blob!这是因为Git只存储唯一的内容,而且这两个空文件的内容显然产生了相同的哈希:e69de
-只需要一个条目。然而,由于它们的文件名和路径不同,因此创建了两棵树来跟踪这一点。每棵树返回一个基于其引用的路径和blob计算的唯一散列(键)。继续向上到第二次提交 C1,我们看到只有
.gitignore
文件得到更新,生成了一个新的blob(e51ac
)包含该数据。就根树而言,它仍然使用src/index.js
文件的相同子树引用。但是,根树也是具有新散列(键)的全新对象,这仅仅是因为底层.gitignore
引用改变了。在最后的 C2 提交中,只有
src/index.js
文件得到了更新,并出现了一个新的blob(257cc
)-强制创建一个新的子树(5de32
),并最终创建一个新的根树(07eff
)。总结
每次创建一个新的提交时,整个项目的快照都会被记录下来,并按照DAG数据结构存储到内部数据库中。每次 checkout 一个提交时,您的 * 工作树 * 都会被重新构建,以反映通过根树引用的基础快照的相同状态。