Как ускорить / распараллелить загрузку подмодулей git с помощью git clone --recursive?

клонирование репозиториев git с большим количеством подмодулей занимает очень много времени. В следующем примере представлены ~100 подмодулей

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

Git клонирует их один за другим. Занимает гораздо больше времени, чем требуется. Предположим (вероятное), что и клиент, и сервер имеют достаточные ресурсы для ответа на несколько (параллельных) запросов одновременно.

Как ускорить / распараллелить загрузку подмодулей git с помощью git clone --recursive?

2 ответов


когда я запускаю вашу команду, для загрузки 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 2.8 (Q12016) вы сможете инициировать выборку подмодулей... параллельно!

посмотреть совершить fbf7164 (16 Dec 2015) by Джонатан Нидер (artagnon).
См.совершить 62104ba, совершить fe85ee6, совершить c553c72, совершить bfb6b53, совершить b4e04fb, совершить 1079c4b (16 Dec 2015) by Стефан Беллер (stefanbeller).
(слитый Junio C Hamano -- gitster -- на совершить 187c0d3, 12 янв 2016)

добавьте фреймворк для создания группы процессов параллельно и используйте это бежать"git fetch --recurse-submodules" параллельно.

для этого git fetch новый вариант:

-j, --jobs=<n>

количество параллельных детей, которые будут использоваться для выборки подмодулей.
каждый будет получать от различные подмодули, такие, что извлечение многих подмодулей будет быстрее.
По умолчанию подмодули будут извлекаться по одному.

пример:

git fetch --recurse-submodules -j2

основная часть этой новой функции в совершить c553c72 (16 Dec 2015) by Стефан Беллер (stefanbeller).

run-command добавить асинхронный параллельный процессор ребенок

это позволяет запускать внешние команды параллельно с упорядоченным выводом в stderr.

если мы запускаем внешние команды параллельно, мы не можем напрямую передавать выходные данные к нашему stdout / err, как это будет смешиваться. Таким образом, выход каждого процесса будет поток через трубу, которую мы буферизуем. Один подпроцесс может быть напрямую piped для выхода stdout / err для обратной связи с низкой задержкой для пользователя.