git如何存储文件?

mte*_*aha 204 git

我刚开始学习git并且这样做我开始阅读Git社区书,在本书中他们说SVN和CVS存储文件之间的差异,而git存储所有文件的快照.

但我并没有真正了解快照的含义.git是否真的复制了每个提交中的所有文件,因为这是我从他们的解释中理解的.

PS:如果有任何人有更好的学习git的来源我会很感激.

Von*_*onC 250

Git确实为每个提交包含了所有文件的完整副本,除了对于已经存在于Git存储库中的内容,快照将简单地指向所述内容而不是复制它.
这也意味着具有相同内容的多个文件仅存储一次.

因此,快照基本上是一个提交,指的是目录结构的内容.

一些很好的参考是:

你告诉Git你想用git commit命令保存项目的快照,它基本上记录了项目中所有文件在这一点上的样子的清单

实验12说明了如何获取以前的快照


progit书具有更全面的快照描述:

Git和任何其他VCS(包括Subversion和朋友)之间的主要区别在于Git对其数据的思考方式.
从概念上讲,大多数其他系统将信息存储为基于文件的更改列表.这些系统(CVS,Subversion,Perforce,Bazaar等)将它们保存的信息视为一组文件以及随着时间的推移对每个文件所做的更改

基于delta的VCS

Git没有想到或以这种方式存储数据.相反,Git认为其数据更像是一组迷你文件系统的快照.
每次你在Git中提交或保存项目的状态时,它基本上都会记录当时所有文件的外观,并存储对该快照的引用.
为了提高效率,如果文件没有改变,Git不会再次存储文件 - 只是指向它已存储的先前相同文件的链接.
Git认为其数据更像如下:

基于快照的VCS

这是Git和几乎所有其他VCS之间的重要区别.它让Git几乎重新考虑了大多数其他系统从上一代复制的版本控制的每个方面.这使得Git更像是一个迷你文件系统,其上构建了一些非常强大的工具,而不仅仅是一个VCS.


贾恩·哈德克增加了这个重要的评论:

虽然这在概念层面上是真实而重要的,但在存储层面却并非如此.
Git确实使用增量来存储.
不仅如此,它比其他任何系统都更有效率.因为它不保留每个文件的历史记录,所以当它想要进行增量压缩时,它需要每个blob,选择一些可能相似的blob(使用包含最接近的先前版本和其他一些的启发法),尝试生成增量并选择最小的增量.通过这种方式,它可以(通常取决于启发式方法)利用其他类似文件或比以前更相似的旧版本."pack window"参数允许delta压缩质量的交易性能.默认值(10)通常会提供不错的结果,但是当空间有限或加速网络传输时,git gc --aggressive使用值250,这会使其运行速度非常慢,但会为历史数据提供额外的压缩.

  • 在OP的实际问题的背景下,第一段似乎真的具有误导性.直到你到达最后一段,我们才知道,哦,是的,事实Git**确实**"存储文件之间的差异.真的希望信息被标记为顶部而不是埋没得那么深.说,至少在你的答案的某处包括真实的故事;) (27认同)
  • @JanHudec好点.我已将您的评论包含在答案中以获得更多可见性. (3认同)
  • “Git 确实为每次提交存储所有文件的完整副本”。鉴于最后一段,这是完全错误的! (2认同)
  • @VonC 你说“每次提交确实有所有文件的完整副本”,但下一句话与这一点相矛盾......我认为对于使用 Git 的任何人来说,Git 可以在给定提交中重建文件的副本是显而易见的。 (2认同)

svi*_*ick 42

Git在逻辑上将每个文件存储在其SHA1下.这意味着如果您在存储库中有两个文件具有完全相同的内容(或者重命名文件),则只存储一个副本.

但这也意味着当您修改文件的一小部分并提交时,会存储该文件的另一个副本.git解决这个问题的方法是使用pack文件.偶尔,收集来自repo的所有"松散"文件(实际上,不仅仅是文件,还包含提交和目录信息的对象),并将其压缩到包文件中.使用zlib压缩包文件.类似的文件也是增量压缩的.

在拉或推(至少使用某些协议)时也使用相同的格式,因此不必再次重新压缩这些文件.

结果是git存储库包含整个未压缩的工作副本,未压缩的最新文件和压缩的旧文件通常相对较小,比工作副本的大小小两倍.这意味着它比具有相同文件的SVN repo小,即使SVN不在本地存储历史记录.


Ale*_*ler 9

\n

OP: Git 中的快照是什么意思?Git 在每次提交中都会生成所有文件的副本,这是真的吗?

\n
\n

Git 中的快照是什么意思?

\n

在 Git 中,所有提交都是项目在特定时间点的不可变快照(忽略的文件除外)。这意味着每次提交都包含整个项目的唯一表示,而不仅仅是提交时修改或添加的文件(增量)。除了对实际文件的引用之外,每个提交还注入了相关的元数据,例如提交消息、作者(包括时间戳)、提交者(包括时间戳)以及对父提交的引用;所有这些都是不可改变的!

\n

由于提交(或正式称为提交对象)整体上是不可变的,因此尝试修改其任何内容都是不可能的。提交一旦创建就永远不能被篡改或修改!

\n

Git内部如何存储文件

\n

Pro Git书中我们了解到:

\n
\n

Git 是一个内容可寻址的文件系统。伟大的。这意味着什么?这意味着 Git 的核心是一个简单的键值数据存储。这意味着您可以将任何类型的内容插入到 Git 存储库中,Git 将为您返回一个唯一的密钥,您稍后可以使用它来检索该内容。

\n
\n

因此,让我们看一下下面的插图,以了解上述语句的真正含义,以及 Git 如何在内部存储数据(特别是文件)。

\n

内部文件存储示意图 包含三个提交的简单提交历史记录,包括实际数据(文件和目录)如何存储在 Git 中的概述。左侧显示实际快照,与先前提交相比的“增量更改”以绿色突出显示。最右边是用于存储的内部对象。

\n

Git 使用三个主要对象Git在其内部存储中

\n
    \n
  • 犯罪对象(高级快照容器)
  • \n
  • 对象(低级文件名/目录容器)
  • \n
  • 斑点对象(低级文件内容容器)
  • \n
\n

要在 Git 中存储一般意义上的文件(例如内容 + 文件名/目录),需要一个blob和一棵树;blob 仅存储文件内容,树存储引用 blob 的文件名/目录。为了构造嵌套目录,需要使用多棵树;因此,一棵树可以同时引用 blob 和树。从高层次的角度来看,您不必担心blob,因为 Git 会在提交过程中自动创建它们。

\n

注意: Git 自下而上计算所有哈希值(键),从 blob 开始,移动经过任何子树,最终到达根树 \xe2\x80\x93,将键作为输入提供给其直接父级。这一过程产生了上面可视化的结构,该结构在数学和计算机科学中被称为有向非循环图(DAG),例如,所有引用仅沿一个方向移动,而没有任何循环依赖性。

\n

进一步分析可视化示例

\n

通过仔细检查上述历史记录,我们发现对于初始C0提交,添加了两个空文件src/index.js.gitignore\xe2\x80\x93,但只创建了一个blob !这是因为 Git 只存储唯一的内容,而且两个空文件的内容显然会产生相同的哈希值:e69de\xe2\x80\x93 只需要一个条目。然而,由于它们的文件名和路径不同,因此创建了两棵树来跟踪这一点。每棵树都会返回一个基于其引用的路径和 blob 计算的唯一哈希(键)。

\n

继续向上到第二次提交C1,我们看到只有.gitignore文件被更新,生成包含该数据的新 blob ( e51ac)。就根树而言,它仍然使用文件的相同子树引用src/index.js。然而,根树也是一个具有新哈希(键)的全新对象,仅仅是因为底层.gitignore引用发生了变化。

\n

在最终的C2提交中,仅src/index.js更新了文件,并257cc出现了一个新的 blob ( ) \xe2\x80\x93 ,强制创建新的子树 ( 5de32),并最终创建新的根树 ( 07eff)。

\n

总之

\n

每次创建新的提交时,都会记录整个项目的快照,并按照 DAG 数据结构存储到内部数据库中。每当检出提交时,都会重建工作树以反映与通过根树引用的底层快照相同的状态。

\n

资料来源:以上摘录自这篇关于该主题的完整文章(发布在我自己的博客上):不可变快照 - Git 的核心概念之一

\n


归档时间:

查看次数:

73868 次

最近记录:

6 年,8 月 前