如何使用gitclone加速/并行下载git子模块——递归



克隆具有大量子模块的git存储库需要很长时间。以下示例中有大约100个子模块

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

Git一个接一个地克隆它们。所需时间远远超过所需时间。让我们假设(可能的)客户端和服务器都有足够的资源来同时回答多个(并行)请求。

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

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

参见Jonathan Nieder提交的fbf7164(2015年12月16日)(artagnon
参见Stefan Beller(stefanbeller)的提交62104ba、提交fe85ee6、提交c553c72、提交bfb6b53、提交b4e04fb、提交1079c4b(2015年12月16日)
(由Junio C Hamano合并——gitster——提交187c0d3,2016年1月12日)

添加一个框架来并行生成一组进程,并使用它要运行";CCD_ 5";并行。

为此,git fetch有了新的选择:

-j, --jobs=<n>

用于获取子模块的并行子级数
每个模块将从不同的子模块中提取,这样提取多个子模块会更快
默认情况下,将一次提取一个子模块。

示例:

git fetch --recurse-submodules -j2

这个新功能的大部分内容在Stefan Beller(stefanbeller)提交的c553c72(2015年12月16日)中。

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

这允许在stderr上并行运行外部命令和有序输出

如果我们并行运行外部命令,我们就无法将输出直接管道传输到stdout/err,因为它会混淆。因此,每个进程的输出都将流经一个管道,我们对其进行缓冲。一个子流程可以直接通过管道输出stdout/err,以便向用户提供低延迟反馈。


注意,在Git 2.24(2019年第四季度)之前;CCD_ 9";在获取子模块时允许<n>并行作业,但这不适用于";CCD_ 11";从多个远程存储库中获取
现在确实如此。

参见Johannes Schindelin(dscho)提交的d54dea7(2019年10月5日)
(由Junio C Hamano合并——gitster——提交d96e31e,2019年10月15日)

fetch:也让--jobs=<n>并行化--multiple

签字人:Johannes Schindelin

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

让我们改变一下
使用此补丁,还可以并行化从多个远程获取

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

  • 配置设置CCD_ 19仍然仅控制CCD_
  • 而新引入的设置CCD_ 21控制两者(但对于具有CCD_

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

当我运行您的命令时,下载68Mb需要338秒的墙壁时间。

通过安装以下依赖GNU并行的Python程序,

#! /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 

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

最新更新