并行执行“git submodule foreach”

Posted

技术标签:

【中文标题】并行执行“git submodule foreach”【英文标题】:Execute "git submodule foreach" in parallel 【发布时间】:2018-10-04 13:51:53 【问题描述】:

有什么方法可以并行执行git submodule foreach 命令,类似于--jobs 8 参数与git submodule update 一起使用的方式?

例如,我们从事的一个项目涉及近 200 个子组件(子模块),我们大量使用foreach 命令对其进行操作。我想加快速度。

PS:如果解决方案涉及脚本,我在 Windows 上工作,并且大部分时间使用 git-bash。

【问题讨论】:

没有内置方法,你必须使用外部工具,如foreach_submodule.js 或git-deep。 PS。我还没有尝试过,不知道它们是否有效。 @phd 可惜没有包含内置方式,我想因为保证操作之间互斥的复杂性所以根本不提供它更安全,我会采取看看那些包,谢谢! 【参考方案1】:

我向您推荐一种基于 Python 等解释语言多平台的解决方案。

进程启动器


首先你需要定义一个类来管理启动命令的进程。

class PFSProcess(object):
    def __init__(self, submodule, path, cmd):
        self.__submodule = submodule
        self.__path = path
        self.__cmd = cmd
        self.__output = None
        self.__p = None

    def run(self):
        self.__output = "\n\n" + self.__submodule + "\n"
        self.__p = subprocess.Popen(self.__cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True,
                             cwd=os.path.join(self.__path, self.__submodule))
        self.__output += self.__p.communicate()[0].decode('utf-8')
        if self.__p.communicate()[1]:
            self.__output += self.__p.communicate()[1].decode('utf-8')
        print(self.__output)

多线程


下一步是生成多线程执行。 Python 在其核心中包含非常强大的库来处理线程。您可以使用它导入以下包:

import threading

在创建线程之前,您需要创建一个工作线程,一个为每个线程调用的函数:

def worker(submodule_list, path, command):
    for submodule in submodule_list:
        PFSProcess(submodule, path, command).run()

正如你所见,worker 接收了一个子模块列表。为了清楚起见并且因为它超出了我们的范围,我建议您查看.gitmodules,您可以从中生成读取文件的子模块列表。

?

作为基本方向,您可以在每个子模块中找到以下行:

path = relative_path/project

为此,您可以使用这个正则表达式:

'path ?= ?([A-za-z0-9-_]+)(\/[A-za-z0-9-_]+)*([A-za-z0-9-_])'

如果正则表达式匹配,您可以在同一行中使用以下内容获取相对路径:

' ([A-za-z0-9-_]+)(\/[A-za-z0-9-_]+)*([A-za-z0-9-_])'

注意,因为最后一个正则表达式返回的相对路径在第一个位置有一个空格字符。

?提示>

然后将子模块列表拆分为所需的作业数量:

num_jobs = 8

i = 0
for submodule in submodules:
    submodule_list[i % num_jobs].append(submodule)
    i += 1

最后将每个块(作业)分派给每个线程并等待所有线程完成:

for i in range(num_jobs):
    t = threading.Thread(target=worker, args=(list_submodule_list[i], self.args.path, self.args.command,))
    self.__threads.append(t)
    t.start()

for i in range(num_jobs):
    self.__threads[i].join()

显然我已经暴露了基本概念,但是您可以访问GitHub中的parallel_foreach_submodule (PFS)项目的完整实现。

【讨论】:

非常感谢!已经在使用它了!顺便说一句,i += 1 不应该在for submodule in submodules 循环内吗?【参考方案2】:

一个简单的、仅限 bash 的解决方案是这样做(替换 <command with your command>):

IFS=$'\n'
for DIR in $(git submodule foreach -q sh -c pwd); do
    cd $DIR && <command> &
done
wait

作为通用命令(创建一个名为“git-foreach-parallel”的文件):

#!/bin/bash

if [ -z "$1" ]; then
    echo "Missing Command" >&2
    exit 1
fi

COMMAND="$@"

IFS=$'\n'
for DIR in $(git submodule foreach -q sh -c pwd); do
    cd "$DIR" && $COMMAND &
done
wait

【讨论】:

也许我没有正确阅读它,但我在任何时候都看不到并行化。 @cbuchart 请注意,命令末尾有`&`。它使命令并行 哦!非常感谢,我需要新眼镜?【参考方案3】:

如果有人正在寻找一种纯 bash 的方式来做到这一点(不在 docker 容器中安装 python 或其他东西),那对我有帮助

使用示例

bash git-submodule-foreach-parallel.sh "git fetch && git checkout master"

bash git-submodule-foreach-parallel.sh "git fetch && git pull"

bash git-submodule-foreach-parallel.sh "git fetch && git push"

COMMAND="git clean -dfx -e \"**/.idea\""
# Running command in parent repository
eval "$COMMAND"
# Running command in submodules
bash git-submodule-foreach-parallel.sh "$COMMAND"

git-submodule-foreach-parallel.sh(使用示例运行)

#!/bin/bash

if [ -z "$1" ]; then
    echo "Missing Command" >&2
    exit 1
fi

COMMAND="$@"

IFS=$'\n'
for DIR in $(git submodule foreach --recursive -q sh -c pwd); do
    printf "\nStarted running command \"$COMMAND\" in directory \"$DIR\"\n" \
    && \
    cd "$DIR" \
    && \
    eval "$COMMAND" \
    && \
    printf "Finished running command \"$COMMAND\" in directory \"$DIR\"\n" \
    &
done
wait

【讨论】:

以上是关于并行执行“git submodule foreach”的主要内容,如果未能解决你的问题,请参考以下文章

oracle中并行执行不一定比串行执行快

testng多线程并行执行测试

Spark 并行度

Jenkins pipeline 并行执行任务流

并行 JUnit 测试不执行关闭挂钩

android中的asynctask可不可以并行执行多个