Git如何确定需要在仓库之间发送哪些对象?

4

我已经看过这里,但是无法完全弄清楚我想知道的事情:如何确定git pushgit pull在另一端缺少哪些提交对象?

假设我们有一个包含以下提交的存储库:(字母代表SHA-1 ID,drefs/heads/master

a -> b -> c -> d

相比之下,遥控器具有以下功能:

a -> e -> f -> g

根据git文档,远程会告诉我们它的refs/heads/masterg,但由于我们不知道那个提交,这实际上并没有告诉我们任何信息。这怎么足以找出丢失的数据?
在另一个方向上,文件说:
在这一点上,fetch-pack进程查看它拥有的对象,并通过发送“want”和所需的SHA-1来响应它所需的对象。 它使用“have”和SHA-1来发送所有已有的对象。 在此列表的末尾,它写入“done”以启动upload-pack过程,开始发送它需要的数据的packfile:
这解释了远程如何确定要发送什么数据,但是这是否会影响具有许多对象的存储库的拉取性能?否则,文本实际上意味着什么?
显然,数据传输的方式在推送和拉取方面有很大不同。这种设计选择面临哪些挑战,以及我如何理解文档中的描述?
1个回答

11
“魔力”就在于ID。提交ID由许多因素组成,但基本上它是这个SHA-1 hash的哈希值。
内容(所有内容,而不仅仅是差异),作者,日期,日志信息,父ID。
更改其中任何一个,您需要创建一个新的提交并生成一个新的ID。请注意,父ID已包含在内。
对于Git来说,这意味着如果我告诉您我有提交“ABC123”,而您也有提交“ABC123”,那么我们知道我们具有相同的提交,包括相同的内容、相同的作者、相同的日期、相同的信息和相同的父级。这些父级具有相同的ID,因此它们具有相同的内容、相同的作者、相同的日期、相同的信息和相同的父级。以此类推。如果ID匹配,则它们必须具有相同的历史记录,无需进一步检查。这是Git的一个伟大优势,它深深地融入了其设计中,您无法理解Git而没有它。
一个 pull 操作是 fetch 和 merge 的结合。执行 git pull origin master 相当于执行 git fetch origin,再执行 git merge master origin/master(或使用 --rebase 进行 rebase)。fetch 操作大致如下...
remote @ http://example.com/project.git

                  F - G [bugfix]
                 /
A - B - C - D - E - J [master]
                     \
                      H - I [feature]

local
origin = http://example.com/project.git

                  F - G [origin/bugfix]
                 /
A - B - C - D - E [origin/master] [master]
  • [local] 你好远程,你有哪些分支?
  • [remote] 我在 G 分支上有 bugfix。
  • [local] 我也在 G 分支上有 bugfix!完成了。还有什么?
  • [remote] 我在 I 分支上有 feature。
  • [local] 我没有 feature,也没有 I。I 的父分支是什么?
  • [remote] I 的父分支是 H。
  • [local] 我没有 H,H 的父分支是什么?
  • [remote] H 的父分支是 J。
  • [local] 我没有 J,J 的父分支是什么?
  • [remote] J 的父分支是 E。
  • [local] 我有 E!请把 J、H 和 I 发给我。
  • [remote] 好的,马上发送。
  • [local] 将 J、H 和 I 添加到仓库并将 origin/feature 设为 I好的,还有其他的吗?
  • [remote] 我在 J 分支上有 master。
  • [local] 我在 E 分支上有 master,你已经将 J 发送给我了。将 origin/master 移动到 J 还有其他的吗?
  • [remote] 没有了!
  • [local] 拜拜了您嘞!

现在本地看起来像这样...

local
origin = http://example.com/project.git

                  F - G [origin/bugfix]
                 /
A - B - C - D - E [master] - J [origin/master]
                              \
                               H - I [origin/feature]

然后它将执行 git merge master origin/master 完成拉取,这将快进到 J。推送类似,只是过程相反(本地发送提交到远程),并且只会快进。这就是Pro Git所称的“愚蠢协议”,当您的远程是简单的HTTP服务器时使用。智能协议更常用,更少喋喋不休,并且有许多优化。但是您可以看到两者都非常高效。没有必要通信整个历史记录,他们只需要发送20字节的哈希密钥,直到找到一个共同的祖先。这里有一些参考资料和进一步阅读。

你的回答很棒,谢谢!如果您不介意,我特别想知道如何避免多次往返。是简单地急切地发送例如最多50个哈希值(<1kb),还是有更理论、算法机制可以更快地确定提交是否已知? - Silly Freak
1
另外,如果您有任何答案来源,那就太好了。我的关注点更多是“实现类似git的同步机制”,所以我已经非常满意了,但是未来寻找“如何详细实现git同步”的读者可能会很感激。 - Silly Freak
@SillyFreak 我添加了一些进一步详细的参考资料。对话示例是我在概念上教授它的方式,Pro Git 称之为 "The Dumb Protocol"。你可以查看 "The Smart Protocol" 以获取更高效的示例。 - Schwern
智能协议是我一开始关注、引用并请求澄清的内容。但是,我会查看示例代码,谢谢。 - Silly Freak
1
@SillyFreak,我现在理解了智能协议。如果你有关于它的具体问题,我可能能够提供帮助。只需在此处链接它们,以便我能够看到。 - Schwern

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