繁体   English   中英

列出 Git 存储库中的子模块

[英]List submodules in a Git repository

我有一个 Git 存储库,其中有几个子模块。 运行git submodule init后,如何列出所有子模块的名称?

git submodule foreach命令可以回显子模块的名称,但这只有在它们被检出后才有效,而这在初始化步骤之后没有发生。 链中有更多步骤需要在它们被检出之前发生,我不想将子模块的名称硬连接到脚本中。

那么是否有一个 Git 命令来获取所有当前已注册但尚未签出的子模块的名称?

您可以使用与git submodule init自身相同的机制,即查看.gitmodules 此文件枚举每个子模块路径及其引用的 URL。

例如,从存储库的根目录, cat .gitmodules会将内容打印到屏幕上(假设您有cat )。

因为 .gitmodule 文件具有 Git 配置格式,所以您可以使用 git config 来解析这些文件:

git config --file .gitmodules --name-only --get-regexp path

将向您显示所有子模块条目,并使用

git config --file .gitmodules --get-regexp path | awk '{ print $2 }'

你只会得到子模块路径本身。

如果要显示嵌套的子模块,可以使用git submodule status或可选的git submodule status --recursive

来自 Git 文档:

显示子模块的状态。 这将打印每个子模块的当前签出提交的 SHA-1,以及 SHA-1 的子模块路径和 git describe 的输出。 每个 SHA-1 将带有前缀 - 如果子模块未初始化,+ 如果当前签出的子模块提交与包含存储库的索引中找到的 SHA-1 不匹配,如果子模块存在合并冲突,则为 U。

以下命令将列出子模块:

git submodule--helper list

输出是这样的:

<mode> <sha1> <stage> <location>

注意:它需要 Git 2.7.0 或更高版本。

要仅返回已注册子模块的名称,可以使用以下命令:

grep path .gitmodules | sed 's/.*= //'

将其视为不存在的git submodule --list

利用:

$ git submodule

它将列出指定 Git 存储库中的所有子模块。

我用这个:

git config --list|egrep ^submodule

我注意到这个问题的答案中提供的命令为我提供了我正在寻找的信息:

在 .gitmodule 中找不到不是子模块的路径的子模块映射

git ls-files --stage | grep 160000

如果您不介意只对已初始化的子模块进行操作,则可以使用git submodule foreach来避免文本解析。

git submodule foreach --quiet 'echo $name'

您可以使用:

git submodule | awk '{ print $2 }'

我用这个:

git submodule status | cut -d' ' -f3-4 

输出(路径+版本):

tools/deploy_utils (0.2.4)

这对我有用:

git ls-files --stage | grep ^160000

它基于这篇很棒的文章: 了解 Git 子模块

它必须读取grep ^160000

按名称列出所有子模块:

git submodule --quiet foreach --recursive 'echo $name'

使用内置的 git 函数显示有关每个子模块的所有信息:

git submodule foreach -q git config -l

或者只是 URL-s:

git submodule foreach -q git config remote.origin.url

这里偷来的。

只是子模块路径,女士...

git config --list | grep \^submodule | cut -f 2 -d .
 Vendor/BaseModel Vendor/ObjectMatcher Vendor/OrderedDictionary Vendor/_ObjC Vendor/XCodeHelpers

👍🏼

git config允许指定一个配置文件。
.gitmodules一个配置文件。

因此,在“使用空格作为分隔符和剪切命令”的帮助下:

git config --file=.gitmodules --get-regexp ^^submodule.*\.path$ | cut -d " " -f 2

这将只列出路径,每个声明的子模块一个。

正如蒂诺在评论中指出的那样:

  • 对于其中包含空格的子模块,这将失败。
  • 子模块路径可能包含换行符,如

    git submodule add https://github.com/hilbix/bashy.git "sub module" git mv 'sub module' $'sub\nmodule'

作为更强大的替代方案,Tino 建议:

 git config -z --file .gitmodules --get-regexp '\.path$' | \ sed -nz 's/^[^\n]*\n//p' | \ tr '\0' '\n'

对于其中包含换行符的路径(可以使用git mv创建),请不要使用| tr '\0' '\n' | tr '\0' '\n'并使用类似... | while IFS='' read -d '' path; do ... ... | while IFS='' read -d '' path; do ... ... | while IFS='' read -d '' path; do ...使用 bash 进行进一步处理。
这需要一个理解read -d ''的现代 bash (不要忘记-d and ''之间的空格)。

在我的 Git [1]版本中,每个 Git 子模块都有一个name和一个path 它们不一定必须相同[2] 以可靠的方式获得两者,而不首先检查子模块( git update --init ),是一个棘手的 shell 巫术。

获取子模块names列表

我没有找到如何使用git config或任何其他git命令来实现这一点的方法。 因此我们回到.gitmodules上的正则表达式(超级丑陋)。 但它似乎有点安全,因为git限制了子模块names允许的可能代码空间。 此外,由于您可能希望将此列表用于进一步的 shell 处理,因此下面的解决方案使用NULL -bytes ( \0 ) 分隔条目。

$ sed -nre \
  's/^\[submodule \"(.*)\"]$/\1\x0/p' \
  "$(git rev-parse --show-toplevel)/.gitmodules" \
| tr -d '\n' \
| xargs -0 -n1 printf "%b\0"

在你的脚本中:

#!/usr/bin/env bash

while IFS= read -rd '' submodule_name; do
  echo submodule name: "${submodule_name}"
done < <(
  sed -nre \
    's/^\[submodule \"(.*)\"]$/\1\x0/p' \
    "$(git rev-parse --show-toplevel)/.gitmodules" \
  | tr -d '\n' \
  | xargs -0 -n1 printf "%b\0"
)

注意read -rd ''需要bash并且不能与sh一起使用。

获取子模块paths列表

在我的方法中,我尝试使用awktrsed来处理来自git config --get-regexp的输出,而是将其传递给一个零字节,并将其分隔回git config --get 这是为了避免子模块paths中出现换行符、空格和其他特殊字符(例如 Unicode)的问题。 此外,由于您可能希望将此列表用于进一步的 shell 处理,因此下面的解决方案使用NULL -bytes ( \0 ) 分隔条目。

$ git config --null --file .gitmodules --name-only --get-regexp '\.path$' \
| xargs -0 -n1 git config --null --file .gitmodules --get

例如,在 Bash 脚本中,您可以:

#!/usr/bin/env bash

while IFS= read -rd '' submodule_path; do
  echo submodule path: "${submodule_path}"
done < <(
  git config --null --file .gitmodules --name-only --get-regexp '\.path$' \
  | xargs -0 -n1 git config --null --file .gitmodules --get
)

注意read -rd ''需要bash并且不能与sh一起使用。


脚注

[1] Git 版本

$ git --version
git version 2.22.0

[2] namepath不同的子模块

设置测试存储库:

$ git init test-name-path
$ cd test-name-path/
$ git checkout -b master
$ git commit --allow-empty -m 'test'
$ git submodule add ./ submodule-name
Cloning into '/tmp/test-name-path/submodule-name'...
done.
$ ls
submodule-name

$ cat .gitmodules
[submodule "submodule-name"]
    path = submodule-name
    url = ./

移动子模块以使namepath发散:

$ git mv submodule-name/ submodule-path

$ ls
submodule-path

$ cat .gitmodules
[submodule "submodule-name"]
    path = submodule-path
    url = ./

$ git config --file .gitmodules --get-regexp '\.path$'
submodule.submodule-name.path submodule-path

测试

设置测试存储库:

$ git init test
$ cd test/
$ git checkout -b master
$ git commit --allow-empty -m 'test'
$
$ git submodule add ./ simplename
Cloning into '/tmp/test/simplename'...
done.
$
$ git submodule add ./ 'name with spaces'
Cloning into '/tmp/test/name with spaces'...
done.
$
$ git submodule add ./ 'future-name-with-newlines'
Cloning into '/tmp/test/future-name-with-newlines'...
done.
$ git mv future-name-with-newlines/ 'name
> with
> newlines'
$
$ git submodule add ./ 'name-with-unicode-💩'
Cloning into '/tmp/test/name-with-unicode-💩'...
done.
$
$ git submodule add ./ sub/folder/submodule
Cloning into '/tmp/test/sub/folder/submodule'...
done.
$
$ git submodule add ./ name.with.dots
Cloning into '/tmp/test/name.with.dots'...
done.
$
$ git submodule add ./ 'name"with"double"quotes'
Cloning into '/tmp/test/name"with"double"quotes'...
done.
$
$ git submodule add ./ "name'with'single'quotes"
Cloning into '/tmp/test/name'with'single'quotes''...
done.
$ git submodule add ./ 'name]with[brackets'
Cloning into '/tmp/test/name]with[brackets'...
done.
$ git submodule add ./ 'name-with-.path'
Cloning into '/tmp/test/name-with-.path'...
done.

.gitmodules

[submodule "simplename"]
    path = simplename
    url = ./
[submodule "name with spaces"]
    path = name with spaces
    url = ./
[submodule "future-name-with-newlines"]
    path = name\nwith\nnewlines
    url = ./
[submodule "name-with-unicode-💩"]
    path = name-with-unicode-💩
    url = ./
[submodule "sub/folder/submodule"]
    path = sub/folder/submodule
    url = ./
[submodule "name.with.dots"]
    path = name.with.dots
    url = ./
[submodule "name\"with\"double\"quotes"]
    path = name\"with\"double\"quotes
    url = ./
[submodule "name'with'single'quotes"]
    path = name'with'single'quotes
    url = ./
[submodule "name]with[brackets"]
    path = name]with[brackets
    url = ./
[submodule "name-with-.path"]
    path = name-with-.path
    url = ./

获取子模块names列表

$ sed -nre \
  's/^\[submodule \"(.*)\"]$/\1\x0/p' \
  "$(git rev-parse --show-toplevel)/.gitmodules" \
| tr -d '\n' \
| xargs -0 -n1 printf "%b\0" \
| xargs -0 -n1 echo submodule name:
submodule name: simplename
submodule name: name with spaces
submodule name: future-name-with-newlines
submodule name: name-with-unicode-💩
submodule name: sub/folder/submodule
submodule name: name.with.dots
submodule name: name"with"double"quotes
submodule name: name'with'single'quotes
submodule name: name]with[brackets
submodule name: name-with-.path

获取子模块paths列表

$ git config --null --file .gitmodules --name-only --get-regexp '\.path$' \
| xargs -0 -n1 git config --null --file .gitmodules --get \
| xargs -0 -n1 echo submodule path:
submodule path: simplename
submodule path: name with spaces
submodule path: name
with
newlines
submodule path: name-with-unicode-💩
submodule path: sub/folder/submodule
submodule path: name.with.dots
submodule path: name"with"double"quotes
submodule path: name'with'single'quotes
submodule path: name]with[brackets
submodule path: name-with-.path

如果没有任何.gitmodules文件,但.git/modules/中存在子模块配置:

find .git/modules/ -name config -exec grep url {} \;

假设您目前已全部注册,但尚未签出如下子模块:

$ cat .gitmodules
[submodule ".github/workflows/packages"]
        path = .github/workflows/packages
        url = https://github.com/MarketLeader/Packages
[submodule ".github/workflows/builders"]
        path = .github/workflows/builders
        url = https://github.com/chetabahana/runner
[submodule "docs"]
        path = docs
        url = https://github.com/eq19/lexer

这是获取存储库根目录的 Git 命令:

$ git submodule foreach -q '[[ "$sm_path" == */* ]] || git config remote.origin.url'
https://github.com/eq19/lexer

这是获取特定路径列表的 Git 命令:

$ git submodule foreach -q '[[ ! "$sm_path" == .github/* ]] || git config remote.origin.url'
https://github.com/chetabahana/runner
https://github.com/MarketLeader/Packages

当然你也可以把git config remote.origin.url其他命令。

顺便说一句,我使用的是 git 版本 2.38.1

获取所有子模块的名称

这是从 .gitmodules 解析 Git 子模块名称的另一种方法,无需 sed 或花哨的 IFS 设置。 :-)

#!/bin/env bash

function stripStartAndEndQuotes {
  temp="${1%\"}"
  temp="${temp#\"}"
  echo "$temp"
}

function getSubmoduleNames {
  line=$1
  len=${#line} # Get line length
  stripStartAndEndQuotes "${line::len-1}" # Remove last character
}

while read line; do
  getSubmoduleNames "$line"
done < <(cat .gitmodules | grep "\[submodule.*\]" | cut -d ' ' -f 2-)

获取路径

grep url .gitmodules | sed 's/.*= //'

在 repos 中获取名称

grep path .gitmodules | sed 's/.*= //'

只是子模块的直接列表:

git submodule--helper list | cut -d$'\t' -f 2

一个不错的选择是Tino的一条评论中所示:

git config --null --file .gitmodules --get-regexp "\.path\$" | \
sed -nz "s/^[^\\n]*\n//p" | \
xargs --null rm -v;

但是--null/-z选项不适用于 BusyBox 上的 sed 或 xargs。 然后,这是我能想到的最佳选择(其中 xargs 不会在空格上中断):

grep "^\s*path\s*=" .gitmodules | \
sed -e "s/.*=\s*\"*//" -e "s/\"*\s*\$//" | \
xargs -n 1 -I mark rm -v "mark";

但它有一个缺点,因为xargs在没有--null的情况下被调用并且我们正在使用-I mark ,它正在吃前导空格。 要修复它,请不要使用xargs并将名称写入文件:

grep "^\s*path\s*=" .gitmodules | \
sed -e "s/.*=\s*\"*//" -e "s/\"*\s*\$//" > tempfilefordanmxargsmotherbroked.txt;
while IFS= read -r line; do
   echo ".$line."
done <"tempfilefordanmxargsmotherbroked.txt"
rm "tempfilefordanmxargsmotherbroked.txt"
  • 更正:BusyBox 上的xargs没有删除前导空格,但完整版本是。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM