git“浅克隆+非浅”与“正常克隆”

问题描述

我已经看到了这个答案 for a git clone error,其中建议不要克隆整个 repo,而是只克隆最新的提交,然后使用 unshallow 来获取其余的提交。

考虑以下两个命令

1. 
git clone <url> --depth 1
git fetch --unshallow

2. 
git clone <url>

克隆的最终输出是否相同? 如果是这样,对于非常大的存储库,第二个命令的运行速度如何比第一个命令快得多?

浅克隆是否有其他一些事情没有做,或者有一些缺点,特别是在处理大型存储库时?

解决方法

当您克隆一个非常大的存储库时,您需要传输大量数据。根据您的网络速度,这可能需要很长时间。为了提供一些具体的数字,让我们使用 10 GiB 作为整个存储库数据传输大小(数据传输大小和磁盘大小会有所不同,但通常不会相差很大)并假设您可以获得的传输速率为1 兆字节/秒。这意味着数据传输需要 10240 MiB / (1 MiB/s) = 10240 s = 170.667 分钟 = 不到 3 小时(约 2 小时 50 分钟)。

所使用的各种协议都内置了错误检测和(通常在硬件级别)纠错功能,但在此期间连接仍有可能失败。如果连接确实失败,git clone 会将整个事情视为原子:没有一个工作正常,因此 Git 将删除整个克隆。

如果使用 --depth 1 获取导致初始克隆仅复制大约 1/3 的整体数据,我们将克隆时间缩短到大约 1 小时,从而也降低了完全失败的风险。然后可以逐步添加到浅层克隆(将 --deepen 或更大的 --depth 数字添加到 git fetch)。每一个都有自己的失败风险,但失败只会导致不添加任何对象:现有的克隆没有受到伤害。根据需要重试一小时的传输比重新启动整个 3 小时的传输却在 2 小时 20 分钟后失败要少。1

最终,最终的 --unshallow 为您提供了您本应获得的一切,如果您能够一次无误地进行完整克隆。请注意,您可能希望在初始浅层克隆期间使用 --no-single-branch,或者在初始浅层克隆之后修复 fetch refspec。

克隆的最终输出是否相同?

对此的答案是否定的。我们需要准确定义“最终输出”的含义。不过,就对所有提交和其他对象的有用访问而言,结果是相同的(前提是您撤消了 --depth 参数的单分支效果)。

如果是这样,对于非常大的存储库,第二个命令的工作速度如何比第一个命令快得多?

它不一定工作得更快。然而,在进行完整克隆时,Git 可以——不一定可以,但可以——只发送一个现有的包文件,而不是构建一个新的包文件。这可以节省发送端的 CPU 时间。如果发送方上的(单)包文件构造良好,则接收方上的结果单包文件也构造良好。使用浅层克隆或什至单个非浅层重复加深的结果通常会在接收器上产生多个包文件;这些可能没有那么好构建。


1我从这里的实际经验说起。 :-) 现在的传输率比 2005 年的 DSL 布线要高,但现在存储库也更大了。而且,即使是现在,美国在某些地方的互联网基础设施也很糟糕。