【问题标题】:Git's blob data and diff informationGit的blob数据和diff信息
【发布时间】:2010-09-18 21:09:39
【问题描述】:

据我所知,Git 的 blob 使用 SHA1 哈希作为文件名,以免在存储库中复制文件。

例如,如果文件 A 的内容为“abc”且 SHA1 哈希为“12345”,只要内容不变,提交/分支就可以指向相同的 SHA1。

但是,如果将文件 A 修改为“def”以具有 SHA 哈希“23456”,会发生什么? Git是否存储文件A和修改后的文件A(不仅仅是区别,而是整个文件)?

  • 如果是这样,为什么会这样?存储差异信息不是更好吗?
  • 如果没有,diff 如何跟踪文件中的更改?
  • 其他 VCS 系统 - CVS/SVN/Perforce...怎么样?

添加

“Git Community Book”中的以下内容回答了我的大部分问题。

需要注意的是,这与您可能熟悉的大多数 SCM 系统非常不同。 Subversion、CVS、Perforce、Mercurial 等都使用 Delta 存储系统——它们存储一个提交和下一个提交之间的差异。 Git 不会这样做 - 它会在您每次提交时存储项目中所有文件在此树结构中的样子的快照。这是使用 Git 时要理解的一个非常重要的概念。

【问题讨论】:

    标签: git diff


    【解决方案1】:

    git 按内容而不是差异存储文件,因此在您的示例中,A 的两个版本(“abc”和“def”)都将存储在对象数据库中。

    • 存储整个对象效果更好,因为通过比较它们的 SHA 很容易看出文件的两个版本是否相同。查看git-book 了解有关如何存储对象的详细信息。这会更好,因为如果使用差异跟踪文件,您将需要文件的整个历史来重建它。在集中式系统中很容易做到,但在一个文件可以有许多不同更改的分布式系统中则不然。

    • Git 直接从对象执行差异。

    【讨论】:

    • 对象模型是一回事,但 Git 如何实际存储这些对象是一个独立的问题。 Git 可以并且确实以类似差异的方式存储对象(包文件中的“增量压缩”);请参阅前面链接中的后面章节Git Community Book:How Git Stores Objects,和The Packfile。
    • 没错,但这只是对实现的优化,不是吗? Git 仍然会考虑单个对象,但对于较旧或较少使用的对象,它会存储差异。我认为 OP 的问题更多地是关于 Git 对象模型的哲学。
    • Chris Johnsen 所说的工作链接,因为我相信这本书经过了轻微的重组(链接已损坏)。 git-scm.com/book/en/v2/Git-Internals-Packfiles
    • It turns out that it can. The initial format in which Git saves objects on disk is called a “loose” object format. However, occasionally Git packs up several of these objects into a single binary file called a “packfile” in order to save space and be more efficient. Git does this if you have too many loose objects around, if you run the git gc command manually, or if you push to a remote server. To see what happens, you can manually ask Git to pack up the objects by calling the git gc command: git gc Abizern 谈论的是“松散”的对象格式。
    • What is also interesting is that the second version of the file is the one that is stored intact, whereas the original version is stored as a delta — this is because you’re most likely to need faster access to the most recent version of the file. 再次来自git-scm.com/book/en/v2/Git-Internals-Packfiles
    【解决方案2】:

    git 的设计目标之一是速度。考虑将 git 中的对象存储为增量而不是唯一对象。

    如果您通过 SHA1 哈希存储每个唯一的 blob,则从该 SHA1 哈希中检索内容只需要固定计算。如果您开始存储增量,您将不得不重建对象,并且计算将不再是固定的,并且可能会根据实现无限制地增加。

    了解设计的一个好方法是查看实际的存储库(注意:电子邮件):

    $ git cat-file commit HEAD
    tree 21f9601e608cf62360fca43cd7f0bf05bb65bd23
    parent 11507e17a7c823c379202ae344aa59fe5370a4fd
    author John Doe <jd@example.com> 1273816361 -0400
    committer John Doe <jd@example.com> 1273816361 -0400
    
    Important Work
    
    $ git ls-tree HEAD
    100644 blob 2f6d9912344c299670551c9e9684a7cae800ec5d    .gitignore
    ...
    100644 blob a3ddeb9dd0541b80981f2f78bbc500579a13459a    COPYING
    040000 tree f1ac0acae2a4ab31c2a79b71f08ebd651136d706    contrib
    ...
    

    您可以从这两个命令中看到,提交只是一些元数据、一个或多个父级和一棵树。一棵树包含一个或多个 blob 和树。

    知道了,你就可以开始考虑各种存储库操作的复杂性了。分支的尖端只是指向提交哈希的指针。因此,从那开始,列出历史只是遍历父母的问题。列出树的内容,只是意味着遍历树和所有子树。获取文件内容如上。

    当然,总要进行权衡,而且这种模型的空间效率非常低,尽管它确实提供了文件级别的自动重复数据删除,因为每个唯一文件只需要存储一次。 packfile 有效地缓解了这种情况。增量存储(用于 svn 等)在没有压缩的情况下更节省空间,但 git 最终存储效率更高。

    要区分提交,您可以看到可以从比较树哈希开始,然后如果它们不匹配,则遍历树并比较其 blob 和树,等等。由于该模型是围绕原子提交设计的,因此文件差异更昂贵,但并非不合理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-31
      • 2015-11-28
      • 2014-03-23
      • 2016-09-03
      • 1970-01-01
      • 2014-10-07
      • 2018-08-10
      • 2016-12-05
      相关资源
      最近更新 更多