git如何存储文件?

我刚开始学习git,为了做到这一点,我开始阅读Git社区手册,在这本书中,他们说SVN和CVS存储文件之间的差异,而git存储所有文件的快照。

但我不太明白他们说的快照是什么意思。git真的会在每次提交时复制所有文件吗?因为这是我从他们的解释中了解到的。

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

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

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

一些很好的参考资料是:

  • < a href = " http://git.github。io / git-reference noreferrer“rel = > git.github.io / git-reference < / >

你告诉Git你想用Git commit命令保存一个项目的快照,它基本上记录了项目中所有文件当时的样子

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


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

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

delta-based VCS

Git不会以这种方式考虑或存储数据。相反,Git认为它的数据更像是一个迷你文件系统的快照集。
每次在Git中提交或保存项目状态时,它基本上都会拍摄当时所有文件的照片,并存储对该快照的引用。
为了提高效率,如果文件没有更改,Git不会再次存储文件——只是存储到之前已经存储的相同文件的链接。
Git认为它的数据是这样的:

snapshot-based VCS

这是Git和几乎所有其他vcs之间的一个重要区别。它使Git重新考虑版本控制的几乎所有方面,大多数其他系统都是从上一代复制的。这使得Git更像一个迷你文件系统,并在其之上构建了一些令人难以置信的强大工具,而不仅仅是一个VCS。

参见:


Jan Hudec添加这个重要的评论:

虽然这在概念层面上是正确的,而且很重要,但在存储层面上却不是这样。
Git确实使用增量进行存储
不仅如此,它比其他任何系统都更有效率。因为它不保存每个文件的历史,当它想做压缩的时候,它取每个blob,选择一些可能相似的blob(使用启发式,包括之前版本和其他一些最接近的近似),尝试生成增量,并选择最小的一个。通过这种方式,它可以(通常取决于启发式)利用其他类似的文件或比以前更相似的旧版本。“打包窗口”;参数允许delta压缩质量的交易性能。默认值(10)通常会给出不错的结果,但当空间有限或为了加快网络传输时,git gc --aggressive使用值250,这使得它运行非常慢,但会为历史数据提供额外的压缩

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

但这也意味着当您修改文件的一小部分并提交时,将存储该文件的另一个副本。git解决这个问题的方法是使用包文件。每隔一段时间,就会收集repo中的所有“松散”文件(实际上不只是文件,还包括包含提交和目录信息的对象)并将其压缩到一个包文件中。使用zlib压缩包文件。类似的文件也是delta压缩的。

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

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

OP: Git中的< >强快照< / >强是什么意思?Git会在每次提交时复制所有文件吗?

Git中的快照是什么意思?

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

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

Git如何在内部存储文件

箴Git书中我们了解到:

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

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

内部文件存储的说明 包含三次提交的简单提交历史,包括如何在Git中存储实际数据(文件和目录)的概述。在左侧显示实际快照,使用"delta change"与之前绿色突出显示的提交相比。最右边是用于存储的内部对象。

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

  • 提交对象(高级快照容器)
  • 对象(低级的文件名/目录容器)
  • 对象(低级文件内容容器)

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

注意: Git自底向上计算所有哈希(键),从blob开始,移动通过任何子树,最终到达根树-将键作为输入提供给它的直接父树。这个过程产生了如图所示的结构,在数学和计算机科学中被称为有向顺循环图(DAG),例如,所有的引用只在一个方向上移动,没有任何循环依赖。

进一步分析可视化示例

通过仔细检查上面的历史,我们可以看到,对于最初的< >强C0 < / >强提交,添加了两个空文件,src/index.js.gitignore——但只创建了一个 !这是因为Git只存储唯一的内容,并且由于两个空文件的内容显然产生了相同的散列:e69de—只需要一个条目。然而,由于它们的文件名和路径不同,创建了两棵树来跟踪这一点。每个树返回一个基于它引用的路径和blob计算的唯一哈希(键)。

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

在最后的< >强C2 < / >强提交中,只有src/index.js文件被更新,并出现了一个新的blob (257cc) -强制创建一个新的子树(5de32),并最终创建一个新的根树(07eff)。

总之

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

以上节选自这篇关于这个主题的完整文章:不可变快照——Git的核心概念之一