13

当我git submodule update --init第一次在有很多子模块的项目上运行时,这通常会花费很多时间,因为大多数子模块都存储在速度较慢的公共服务器上。

是否有可能异步初始化子模块?

4

4 回答 4

5

Linux:

cat .gitmodules | grep -Po '".*"' | sed 's/.\(.\+\).$/\1/' | while sleep 0.1 && read line; do git submodule update --init $line & done

苹果电脑:

cat .gitmodules | grep -o '".*"' | cut -d '"' -f 2 | while sleep 0.1 && read line; do git submodule update --init $line & done
于 2013-06-26T14:08:31.463 回答
4

从 Git 2.8 开始,您可以这样做:

git submodule update --init --jobs 4

其中 4 是要并行下载的子模块的数量。

于 2016-06-20T19:43:57.930 回答
2

2016 年 1 月更新:

使用 Git 2.8(2016 年第一季度),您将能够使用git fetch --recurse-submodules -j2.
请参阅“如何使用 git clone --recursive 加速/并行下载 git 子模块?


2013年年中的原始答案

你可以试试:

  • 首先初始化所有子模块:

    git子模块初始化

然后,foreach语法

git submodule foreach git submodule update --recursive -- $path &

如果 ' &' 适用于所有行(而不仅仅是 ' git submodule update --recursive -- $path' 部分),那么您可以调用一个脚本来在后台进行更新。

git submodule foreach git_submodule_update
于 2013-06-26T13:14:37.973 回答
1

这也可以在 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

有了这个,我设法在 Travis 构建期间运行了 700 个子模块的初始化/更新,而无需限制进程池的大小。我经常看到一些锁以这种方式被捕获(最多 3 个)。

于 2015-09-30T15:15:41.940 回答