并行执行“git submodule foreach”

yyhrrdl8  于 2023-03-06  发布在  Git
关注(0)|答案(3)|浏览(127)

是否有办法并行执行git submodule foreach命令,类似于--jobs 8参数与git submodule update的工作方式?
例如,我们的一个项目涉及到近200个子组件(子模块),我们大量使用foreach命令来操作它们,我希望加快它们的速度。
PS:在解决方案涉及脚本的情况下,我在Windows上工作,大多数时间使用git-bash。

zpjtge22

zpjtge221#

我建议您使用基于解释语言多平台(如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()

正如你所看到的,工人接收到一个子模块列表,为了清晰起见,并且因为它超出了我们的范围,我建议你看一下.gitmodules,在那里你可以生成读取文件的子模块列表。

💡< Tip >

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

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-_])'

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

💡< / Tip>

然后将子模块列表拆分为与您想要的作业一样多的块:

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)项目的完整实现。

w8f9ii69

w8f9ii692#

一个简单的、仅限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

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

zour9fqk3#

如果有人正在寻找一种纯粹的方式来实现它(不要在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 (usage examples run it)

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

相关问题