Git仓库内部格式解释

13

有没有关于 Git 如何在其仓库中存储文件的文档?我试着在互联网上搜索,但没有有用的结果。也许是我使用了错误的查询,或者这是一个伟大的秘密——Git 仓库的内部格式?

让我解释一下,为什么我需要这个火箭科学信息:我正在使用 C# 从仓库获取文件历史记录。但是在 libgit2sharp 库中,目前尚未实现此功能。所以(作为一个负责任的人 ;)),我需要自己实现此功能并为社区做出贡献。

但是在将内核源代码移动到 github 后,我甚至不知道从哪里开始搜索。

非常感谢提前!


你可以挖掘其他开源的Git实现。 - Marian Theisen
谢谢,Marian,你能推荐一个吗? - shytikov
1
我建议从这里开始 http://progit.org/book/ch9-2.html - Jacob Groundwater
1个回答

43

仓库的内部格式非常简单。Git 本质上是一个内容可寻址的用户空间文件系统。

以下是一个简要概述。

对象

Git 将其内部数据结构存储为对象。它有四种对象类型:blob(类似于文件)、tree(类似于目录)、commit(在特定时间点对文件系统的快照以及到达该状态的信息)和 tag(指向有用的提交,用于标记重要的提交)。

如果你查看仓库的 .git 目录,你会发现一个包含 SHA-1 哈希命名的文件的目录,即一个对象目录。每个文件代表一个对象。你可以使用 Git plumbing 命令git cat-file来检查它们。下面是我一个仓库中一个示例 commit 对象:

noufal@sanitarium% git cat-file -p 7347addd901afc7d237a3e9c9512c9b0d05c6cf7
tree c45d8922787a3f801c0253b1644ef6933d79fd4a
parent 4ee56fbe52912d3b21b3577b4a82849045e9ff3f
author Noufal Ibrahim <noufal@..> 1322165467 +0530
committer Noufal Ibrahim <noufal@..> 1322165467 +0530

Added a .md extension to README

您还可以在.git/objects/73/47addd901afc7d237a3e9c9512c9b0d05c6cf7中查看对象本身。

您可以像这样检查其他对象。每个提交指向表示该时间点文件系统的树,并具有一个(或更多,在合并提交的情况下)父项。

对象作为单个文件存储在objects目录中。这些称为松散对象。当您运行git gc时,将修剪无法访问的对象,并将其余对象打包成一个单一的文件并进行增量压缩。这更节省空间并压缩存储库。运行gc后,您可以查看.git/objects/pack/目录以查看git packfiles。要解包它们,您可以使用plumbing命令git unpack-objects.git/objects/info/packs文件包含当前存在的packfiles列表。

参考资料

您需要了解的下一件事是引用是什么。它们是对某些提交或对象的指针。您的分支和其他类似的东西都实现为引用。有两种类型“真实”(类似于文件系统中的硬链接)和“符号”(指向真实引用的指针-如符号链接)。

这些位于.git/refs目录中。例如,在以上存储库中,我在master分支上。我的最新提交是

noufal@sanitarium% git log -1
commit 7347addd901afc7d237a3e9c9512c9b0d05c6cf7
Author: Noufal Ibrahim <noufal@...>
Date:   Fri Nov 25 01:41:07 2011 +0530

    Added a .md extension to README
你可以看到我的 master 分支引用位于 .git/refs/heads/master,指向这个提交。
noufal@sanitarium% more .git/refs/heads/master
7347addd901afc7d237a3e9c9512c9b0d05c6cf7

当前分支存储在符号引用 HEAD 中,它位于.git/HEAD中。这里是它:

noufal@sanitarium% more .git/HEAD
ref: refs/heads/master

如果你切换分支,它将会改变。

同样,标签也是类似引用的东西(但它们不像分支那样可移动)。

整个仓库只是使用一棵提交记录的有向无环图(DAG)来管理(每个提交记录指向一个表示文件在某一时刻的树),以及指向DAG上各种提交记录的引用,这样你就可以对它们进行操作。

进一步阅读

  • 我有一个介绍git培训的演示文稿在这里,其中解释了一些内容。
  • 社区书籍在http://book.git-scm.com/中有一些关于内部工作原理的章节。
  • Scott Chacon的《Pro Git》书中有一个关于内部工作原理的章节。
  • 他还有一份关于内部工作原理的PDF文档

非常有信息量。我想对于Alexey来说,在不调用任何git命令的情况下手动解析通用数据库可能会很困难。 - Jacob Groundwater
1
谢谢。解析数据库的一种方法是通过某些子进程接口使用git plumbing命令并解析响应。Plumbing是API,因此库将更加健壮(尽管速度较慢)。我相信Ruby宝石Grit就是这样工作的。另一个选项是实际上实现C#函数来解析数据库,这需要更多的工作和更难维护,但速度更快,更“本地化”。 - Noufal Ibrahim
这是主要想法,我终于有时间为libgit2做出贡献,并使其更适用于实际项目。看起来像个非常糟糕的玩笑——git库无法显示文件修订历史记录。 - shytikov
1
还有第五种类型的对象:注释对象。请参阅:http://schacon.github.com/git/git-notes.html - Tower
1
@Tower:笔记不是一种对象类型;它们与常规历史记录完全相同的方式存储。 - user1686
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接