Github fork的解释以及它们如何存储文件

时间:2012-08-15 18:10:02

标签: git version-control github git-svn

我只是想知道在github上完成fork时会发生什么。

例如,当我分叉一个项目时,它是否在github服务器上复制了所有代码,或只是创建了一个链接?

另一个问题: 在git中,因为如果你向它添加相同的文件它会散列所有文件,它不需要再次存储文件内容,因为散列已经在系统中,对吗?

github是这样的吗?因此,如果我碰巧上传与另一个用户完全相同的代码片段,那么当github gits时,它实际上只是创建一个指向该文件的链接,因为它具有相同的哈希值,或者它是否单独再次保存所有内容? / p>

任何启蒙都会很棒,谢谢!

3 个答案:

答案 0 :(得分:5)

github.com与git的语义完全相同,但基于Web的GUI界面包围着它。

Storage:“Git将文件的每个版本存储为唯一的blob对象” 因此,每个文件都是唯一存储的,但它使用SHA-1哈希来确定文件之间的更改。

对于github,fork本质上是一个克隆。这意味着新的fork是其服务器上的一个新存储区域,并引用了它的ORIGIN。它绝不会在两者之间建立联系,因为git本质上可以跟踪遥控器。每个分支都知道上游。

当你说“如果我碰巧上传与另一个用户完全相同的代码”时,“git”意义上的“上传”一词有点模糊。如果您正在使用相同的存储库,并且git甚至允许您提交相同的文件,这意味着它是不同的并且它在该修订中签入。但是,如果你的意思是在另一个repo的克隆/ fork上工作,那将是相同的情况,但也不会在文件系统上与其他repo建立链接。

我不能声称对内部系统内部github可能进行的优化有任何了解。他们可能正在进行中间自定义操作以节省磁盘空间。但是他们所做的任何事情对你来说都是透明的,并且无关紧要,因为它应该总是在预期的git语义下运行。

github wrote a blog post的开发人员,了解他们如何在内部执行自己的git工作流程。虽然它与您关于如何管理服务的实际工作流程的问题无关,但我认为结论的引用非常有用:

  

Git本身的理解相当复杂,使得工作流程变得复杂   你使用它比必要更复杂只是添加更多   每个人的心理开销。我会一直主张使用   最简单的系统,适用于您的团队并且这样做   直到它不再起作用,然后只增加复杂性   绝对需要。

我从中得到的是,他们承认复杂的git本身是多么复杂,所以他们最有可能采取最轻微的触摸来包裹它以提供服务,并让git做本地最好的工作。

答案 1 :(得分:1)

我不知道GitHub究竟是怎么做到的,但这是一种可行的方法。它需要了解git存储数据的方式。

简短的回答是,回购可以共享 objects数据库,但每个都有自己的引用。
我们甚至可以在本地模拟它以获得概念验证。

在一个裸仓库的目录中(或者在.git/子目录中,如果它不是裸的),有三件事是回购工作的最小值:

  • objects/子目录,它存储所有对象(提交,树,blob ......)。它们可以单独存储为名称等于对象哈希值的文件,也可以存储在.pack文件中。
  • refs/子目录,它存储像refs/heads/master这样的简单文件,其内容是它引用的对象的哈希值。
  • HEAD文件,它表示当前提交的内容。它的值是原始哈希值(对应于分离头,即我们不在任何命名分支上)或者是指向可以找到实际哈希值的ref的文本链接(例如{{1 - 这意味着我们在分支ref: refs/heads/master

让我们假设有人在Github创建了他的原始(不是分叉)回购master。 要模拟,我们在本地做

orig

我们想象上面发生在Github服务器上。现在有一个空的github存储库。然后我们想象从我们自己的PC我们克隆github repo:

$ git init --bare github_orig

当然,在现实生活中,我们将使用$ git clone github_orig local_orig 代替github_orig。现在我们已经在https://github...中克隆了github仓库。

local_orig

$ cd local_orig/ $ echo zzz > file $ git add file $ git commit -m initial $ git push $ cd .. 的{​​{1}}目录将包含我们推送的提交对象,github_orig的一个blob对象和一个树对象。 object文件将包含提交哈希。

现在让我们想象当有人点击file按钮时会发生什么。 我们将创建一个git repo但手动

refs/heads/master

请注意,我们复制 Fork$ mkdir github_fork $ cd github_fork/ $ cp ../github_orig/HEAD . $ cp -r ../github_orig/refs . $ ln -s ../github_orig/objects $ cd .. ,但我们为HEAD制作了符号链接。我们可以看到制作 fork 非常便宜。即使我们有数十个分支,每个分支只是refs目录中的一个文件,其中包含一个简单的十六进制散列(40个字节)。对于objects,我们只链接到原始对象目录 - 我们不复制任何内容!

现在我们模拟用户创建fork,在本地克隆forked repo:

refs/heads

我们可以看到我们已经成功克隆了虽然我们克隆的回购没有自己的objects但是链接到原始回购的回复。 现在,分叉用户可以创建分支,提交然后将它们推送到$ git clone github_fork local_fork $ cd local_fork $ # ls .git/ file 。对象将被推送到objects目录,这与github_fork相同!但objectsgithub_orig将被修改,不再与refs的匹配。

所以底线是属于同一分叉树的所有回购共享一个公共对象池,而每个回购包含它自己的引用。任何将提交提交到他自己的分叉仓库的人都会修改自己的引用,但会将对象放在共享池中。

当然要真正可用,必须注意更多的事情 - 最重要的是git垃圾收集器必须被调用,除非调用它的repo具有的知识所有引用 - 不仅仅是它自己的。否则,它可能会丢弃共享池中的对象,这些对象无法从其引用中访问,但可以从其他repos'refs中访问。

答案 2 :(得分:1)

根据https://enterprise.github.com/releases/2.2.0/notes GitHub Enterprise(我假设GitHub)以某种方式在forks之间共享对象以减少磁盘空间使用:

  

此版本改变了GitHub Enterprise存储存储库的方式,   通过在fork和。之间共享Git对象来减少磁盘使用量   在读取存储库数据时提高了缓存性能。

还有更多有关他们如何在https://githubengineering.com/counting-objects执行此操作的详细信息。