我只是想知道在github上完成fork时会发生什么。
例如,当我分叉一个项目时,它是否在github服务器上复制了所有代码,或只是创建了一个链接?
另一个问题: 在git中,因为如果你向它添加相同的文件它会散列所有文件,它不需要再次存储文件内容,因为散列已经在系统中,对吗?
github是这样的吗?因此,如果我碰巧上传与另一个用户完全相同的代码片段,那么当github gits时,它实际上只是创建一个指向该文件的链接,因为它具有相同的哈希值,或者它是否单独再次保存所有内容? / p>
任何启蒙都会很棒,谢谢!
答案 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
相同!但objects
和github_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执行此操作的详细信息。