Git подмодуль init async
Когда я запускаю git submodule update --init
в первый раз в проектах с большим количеством подмодулей, это обычно занимает много времени, потому что большинство подмодулей хранится на медленных публичных серверах.
Есть ли возможность инициализировать подмодули асинхронно?
Ответы
Ответ 1
Linux:
cat .gitmodules | grep -Po '".*"' | sed 's/.\(.\+\).$/\1/' | while sleep 0.1 && read line; do git submodule update --init $line & done
Mac:
cat .gitmodules | grep -o '".*"' | cut -d '"' -f 2 | while sleep 0.1 && read line; do git submodule update --init $line & done
Ответ 2
Что касается Git 2.8, вы можете это
git submodule update --init --jobs 4
где 4 - количество подмодулей, загружаемых параллельно.
Ответ 3
Обновление в январе 2016 года:
С Git 2.8 (Q1 2016) вы сможете получить подмодули параллельно (!) с помощью git fetch --recurse-submodules -j2
.
См. "Как ускорить/распараллелить загрузки подмодулей Git с помощью Git clone --recursive?"
Оригинальный ответ в середине 2013 года
Вы можете попробовать:
Затем синтаксис foreach
:
git submodule foreach git submodule update --recursive -- $path &
Если "&
" применяется ко всей строке (вместо части "git submodule update --recursive -- $path
" ), вы можете вызвать script, который сделает обновление в фоновом режиме.
git submodule foreach git_submodule_update
Ответ 4
Это также можно сделать в Python. В Python 3 (потому что мы в 2015...), мы можем использовать что-то вроде этого:
#!/usr/bin/env python3
import os
import re
import subprocess
import sys
from functools import partial
from multiprocessing import Pool
def list_submodules(path):
gitmodules = open(os.path.join(path, ".gitmodules"), 'r')
matches = re.findall("path = ([\w\-_\/]+)", gitmodules.read())
gitmodules.close()
return matches
def update_submodule(name, path):
cmd = ["git", "-C", path, "submodule", "update", "--init", name]
return subprocess.call(cmd, shell=False)
if __name__ == '__main__':
if len(sys.argv) != 2:
sys.exit(2)
root_path = sys.argv[1]
p = Pool()
p.map(partial(update_submodule, path=root_path), list_submodules(root_path))
Это может быть безопаснее, чем однострочный код, заданный @Karmazzin (так как он просто продолжает нерестовые процессы без какого-либо контроля над количеством порожденных процессов), все же он следует той же логике: прочитайте .gitmodules
, затем введите несколько процессы, выполняющие правильную команду git, но здесь, используя пул процессов (также можно установить максимальное количество процессов). Путь к клонированному репозиторию должен быть передан в качестве аргумента. Это было широко протестировано в репозитории с около 700 подмодулей.
Обратите внимание, что в случае инициализации подмодуля каждый процесс будет пытаться записать в .git/config
, и могут возникнуть проблемы с блокировкой:
Ошибка: не удалось заблокировать файл конфигурации .git/config: Файл существует
Не удалось зарегистрировать url для пути подмодуля '...'
Это можно поймать с помощью блока subprocess.check_output
и try/except subprocess.CalledProcessError
, который является более чистым, чем спящий режим, добавленный в метод @Karmazzin. Обновленный метод может выглядеть так:
def update_submodule(name, path):
cmd = ["git", "-C", path, "submodule", "update", "--init", name]
while True:
try:
subprocess.check_output(cmd, stderr=subprocess.PIPE, shell=False)
return
except subprocess.CalledProcessError as e:
if b"could not lock config file .git/config: File exists" in e.stderr:
continue
else:
raise e
При этом мне удалось запустить init/update из 700 подмодулей во время сборки Travis без необходимости ограничивать размер пула процессов. Я часто вижу несколько зацепов, пойманных таким образом (~ 3 макс.).