12

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

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

所以另一个问题:在 git 中,因为如果您向其中添加相同的文件,它会对所有文件进行哈希处理,因此不需要再次存储文件内容,因为哈希值已经在系统中,对吗?

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

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

4

3 回答 3

7

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

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

在https://githubengineering.com/counting-objects上还有更多关于他们如何做到这一点的详细信息。

于 2017-06-15T22:11:23.743 回答
6

github.com与 git 的语义完全相同,但它周围有一个基于 Web 的 GUI 界面。

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

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

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

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

github 的一位开发人员写了一篇关于他们如何在内部完成自己的 git 工作流的博客文章。虽然它与您关于他们如何管理服务的实际工作流程的问题无关,但我认为结论中的这句话非常有用:

Git 本身理解起来相当复杂,使您使用它的工作流程比必要的复杂得多,这只会给每个人的一天增加更多的精神开销。我总是提倡使用最简单的系统,为你的团队工作,直到它不再工作,然后只在绝对需要时增加复杂性。

我从中得到的是,他们承认 git 本身是多么复杂,所以他们很可能会采取尽可能轻的接触来围绕它来提供服务,并让 git 做它本机最擅长的事情。

于 2012-08-15T18:24:33.930 回答
2

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

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

在裸仓库的目录中(.git/如果不是裸仓库,则在子目录中)有三件事是仓库工作的最低要求:

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

假设有人orig在 Github 上创建了他的原始(非分叉)回购。
为了模拟,我们在本地做

$ git init --bare github_orig

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

$ 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 ..

this 之后github_origobject目录将包含我们推送的提交对象、一个 blob 对象file和一个树对象。该refs/heads/master文件将包含提交哈希。

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

$ mkdir github_fork
$ cd github_fork/
$ cp ../github_orig/HEAD .
$ cp -r ../github_orig/refs .
$ ln -s ../github_orig/objects
$ cd ..

请注意,我们复制 HEAD了,refs我们objects. 正如我们所见,制作叉子非常便宜。即使我们有数十个分支,每个分支都只是refs/heads目录中的一个文件,其中包含一个简单的十六进制散列(40 字节)。因为objects我们只链接到原始对象目录——我们不复制任何东西!

现在我们模拟创建分叉的用户,在本地克隆分叉的 repo:

$ git clone github_fork local_fork
$ cd local_fork
$ # ls
.git/  file

我们可以看到我们已经成功克隆了虽然我们克隆的 repo 没有自己的objects但链接到原始 repo 的 repo。
现在分叉用户可以创建分支、提交,然后将它们推送到github_fork. 对象将被推送到与!objects相同的目录中。github_orig但是refsandHEAD将被修改并且将不再匹配github_orig.

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

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

于 2014-10-24T14:32:40.370 回答