如何使用git clone --recursive加速/并行下载git子模块?

29

克隆具有很多子模块的git仓库需要非常长的时间。以下示例中有约100个子模块。

git clone --recursive https://github.com/Whonix/Whonix

Git会一个一个地克隆它们。这比所需时间要长得多。让我们做出(可能的)假设,即客户端和服务器都有足够的资源来同时响应多个(并行)请求。

如何使用 git clone --recursive 加速/并行下载git子模块?


1
https://dev59.com/F2Qm5IYBdhLWcg3w0Bu5 - Andrew C
https://dev59.com/F2Qm5IYBdhLWcg3w0Bu5#17322442 - adrelanos
在 Git 2.8(2016年第一季度)中,您将能够使用 git fetch --recurse-submodules -j2 并行获取子模块。请参见下面的答案 - VonC
@adrelanos 您可能希望考虑将您接受的答案更改为引用现在内置的 Git 选项的答案。 - Andrew Marshall
我正在使用git 2.17.1,但是没有一个答案有效。 - banderlog013
2个回答

38

在 git 2.8(Q12016)中,您将能够并行启动子模块的提取!

请查看提交fbf7164(2015年12月16日),作者为Jonathan Nieder(artagnon
请查看提交62104ba, 提交fe85ee6, 提交c553c72, 提交bfb6b53, 提交b4e04fb, 提交1079c4b(2015年12月16日),作者为Stefan Beller(stefanbeller
(由Junio C Hamano -- gitster --提交187c0d3中合并,日期为2016年1月12日)

添加一个框架以并行生成一组进程,并使用它来并行运行“git fetch --recurse-submodules”。
为此,git fetch 有了新选项:
-j, --jobs=<n>

并行使用的子模块数量。
每个子模块都会从不同的子模块中获取,这样获取多个子模块将更快。
默认情况下,子模块将一个接一个地获取。

示例:

git fetch --recurse-submodules -j2

这个新功能的主要内容在提交c553c72(2015年12月16日)由Stefan Beller (stefanbeller)完成。

run-command:添加异步并行子进程处理器

这允许在stderr上有序输出的情况下并行运行外部命令

如果我们并行运行外部命令,我们不能直接将输出管道传输到我们的stdout/err,因为它会混合。因此,每个进程的输出将通过一个缓冲区进行流式传输。一个子进程可以直接传输到我们的stdout/err以提供低延迟反馈给用户。


请注意,在 Git 2.24(2019年第4季度)之前,“git fetch --jobs=<n>”在获取子模块时允许使用<n>个并行作业,但这不适用于从多个远程存储库获取的“git fetch --multiple”。
现在可以了。
参见 commit d54dea7(由 Johannes Schindelin (dscho) 在2019年10月05日提交)。
(由Junio C Hamano -- gitster --commit d96e31e中合并,2019年10月15日)

fetch: 让 --jobs=<n> 同样可以并行处理 --multiple

签署者: Johannes Schindelin

到目前为止,--jobs=<n> 只能并行处理子模块的获取/克隆,并不能处理 --multiple 获取,这是不直观的,因为选项名称并没有特别提到子模块。

让我们改变这一点。
通过这个补丁,也会并行获取多个远程仓库。

为了向后兼容(并为了准备子模块和多远程仓库获取可能需要不同的并行限制的用例):

  • 配置设置 submodule.fetchJobs 仍然只控制 git fetch 的子模块部分,
  • 而新引入的设置 fetch.parallel 控制两者(但可以使用 submodule.fetchJobs 覆盖子模块部分)。

另请参阅 Git 2.40 (2023年第一季度) 和 git pull 并行多个远程仓库,该版本修复了使用 git fetch --jobs=0 时的一个错误。

注意:对于带有子模块的仓库,git clone 命令将尝试克隆子模块两次(一次重试)。请参阅“有没有办法从 Git 克隆失败的点继续克隆?”。 - VonC

9
当我运行您的命令时,下载68 Mb需要338秒的墙钟时间。
使用下面的Python程序,该程序依赖于已安装GNU parallel。
#! /usr/bin/env python
# coding: utf-8

from __future__ import print_function

import os
import subprocess

jobs=16

modules_file = '.gitmodules'

packages = []

if not os.path.exists('Whonix/' + modules_file):
    subprocess.call(['git', 'clone', 'https://github.com/Whonix/Whonix'])

os.chdir('Whonix')

# get list of packages from .gitmodules file
with open(modules_file) as ifp:
    for line in ifp:
        if not line.startswith('[submodule '):
            continue
        package = line.split(' "', 1)[1].split('"', 1)[0]
        #print(package)
        packages.append(package)

def doit():
    p = subprocess.Popen(['parallel', '-N1', '-j{0}'.format(jobs),
                          'git', 'submodule', 'update', '--init',
                          ':::'],
                         stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    res = p.communicate('\n'.join(packages))
    print(res[0])
    if res[1]:
        print("error", res[1])
    print('git exit value', p.returncode)
    return p.returncode

# sometimes one of the updates interferes with the others and generate lock
# errors, so we retry
for x in range(10):
    if doit() == 0:
        print('zero exit from git after {0} times'.format(x+1))
        break
else:
    print('could not get a non-zero exit from git after {0} times'.format(
          x+1))

在同一系统上,我将时间缩短到了45秒(我没有进行多次运行以平均波动)。

为了检查事情是否正常,我将检出的文件与以下内容进行了“比较”:

find Whonix -name ".git" -prune -o -type f -print0 | xargs -0 md5sum > /tmp/md5.sum

在同一目录下

md5sum -c /tmp/md5sum 

在另一个目录中,反之亦然。

一旦您执行了克隆命令,您将会遇到git submodule foreach命令速度慢的问题。为此,您可以使用parallel foreach submodule。您可以参考这个答案:https://dev59.com/j6rka4cB1Zd3GeqPkNyD#50178665 - RDCH106

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