列出Git存储库中的子模块


246

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

git submodule foreach命令可以回显子模块的名称,但是仅在签出子模块后才起作用,而在初始化步骤之后却没有发生。在链中还需要执行更多步骤,才能将它们检出,而且我不想将子模块的名称硬连接到脚本中。

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


4
超级傻,但事实并非如此,但是Sandeep的回答表明,它的git submodule行为符合我预期的假设git submodule list行为-我只是从没想过要检查发生了什么,而没有任何参数git submodule。(很高兴,我测试了该链接,因为我最初抓住了错误的“共享”链接!)
sage,

4
我之所以来到这里,是因为它git submodule list不存在并且git submodule help没有帮助(根据后者,解决方案git submodule无效)。
user2394284 '17

大多数答案(包括接受的答案)都列出了子模块paths,但names如果它们包含特殊字符,则不会中断。我试图给出应该安全的名称和路径的答案:stackoverflow.com/a/56912913/3215929
Ente

Answers:


175

您可以使用与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 }'

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


但是它不会在控制台上打印任何内容。
IgorGanapolsky

2
@IgorGanapolsky-如果cat .gitmodules在存储库根目录中进行操作,则可以在控制台上打印...
sdaau

awk,如果你有辅助模块路径中有空格部分失败。
迈克尔'17

@Ikke我想检索子模块列表,包括名称,路径和url。我尝试了以下Git命令,但未返回任何内容:git config --file = .gitmodules --get-regexp。*?submodule(。*)] path =(。*)url =(。*)我也尝试过git config --file = .gitmodules --get-regexp。*?(submodule)。*?(path)。*?(url)。也许您有解决方案。先感谢您。
Odrai

8
警告:awk带有空格的子模块失败! 该命令应显示为git config -z --file .gitmodules --get-regexp '\.path$' | sed -nz 's/^[^\n]*\n//p' | tr '\0' '\n'(您需要带有的现代sed符号-z)。对于其中包含换行符的路径(使用可以创建git mv),此操作将失败。如果您想避免这些危险,请不要| tr '\0' '\n'使用,并使用类似的东西... | while IFS='' read -d '' path; do ...进行bash的进一步处理。这需要一个现代化的bash它理解read -d ''(不要忘记之间的空间-d'')。
蒂诺

109

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

从Git文档中:

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


2
只需运行git submodule update --init --recursive即可初始化所有子模块。
乔恩·库普斯

2
@IgorGanapolsky刚刚读了Stefaan的评论:如果尚未初始化子模块,则此类命令不起作用。无输出=无子模块(空列表)。
蒂姆(Tim)

6
在没有bash的任何帮助的情况下,绝对应该将此标记为使用简单且基于本机的命令的最佳答案git。而且该解决方案的优雅之处在于,它可以在工作副本的任何位置正常运行(但是子模块本身当然也可以,其结果直接应用于自身)。
蒂姆(Tim)

由于桑迪普吉尔的回答说,git submodule不带任何参数是一样的git submodule status,所以你可以保存自己打字7个字符;-)
山姆

@Sam自从git submodule status --recursive工作以来git submodule --recursive并不完全相同,但事实并非如此。那七个字符确实可以买东西。不过这取决于您的需求。
Kevin Rak

60

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

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

认为它git submodule --list不存在。


3
取而代之的是,使用perl -ne '/^\s*path =\s*(.*)/ and push(@submods, $1); END { print(join("\n", sort(@submods)));}' "$(git rev-parse --show-toplevel)/.gitmodules"与该答案相比(1)可以在任何子目录中使用(尽管不在子模块中);(2)按名称对子模块进行排序;(3)忽略.gitmodules中的注释行。
Colin D Bennett 2014年

6
而且值得记住!
2015年

1
请注意,这是非常草率的方式,path可能会出现在子模块名称(git submodule add https://github.com/commercialhaskell/path.git)中。但是您可能以前已经知道这一点。如果.gitconfig要从工作树中的任何位置进行访问,或者需要在--bare存储库中运行它,则可以使用git cat-file -p HEAD:.gitmodules | ...。如果您需要引用“暂存”文件,则可以执行git cat-file -p :.gitmodules | ...,但这需要存在git index
蒂诺

56

以下命令将列出子模块:

git submodule--helper list

输出是这样的:

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

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


1
大!。该命令在哪里记录。在适用于Windwos的Git上运行,但在git-scm.com
DarVar

1
git ls-files --stage | grep ^160000(来自Answer stackoverflow.com/a/29325219)似乎会产生相同的结果,因此如果您需要与旧版git兼容,那么这也许是一个很好的替代品。
Tino

注意--双破折号submodule--helper
it3xl

25

用:

$ git submodule

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


3
这实际上是提到的其他两个答案的重复答案git submodule [status](请注意,status如果省略,则意味着隐含,因此是相同的)。
Colin D Bennett 2014年

并不总是有效。fatal: no submodule mapping found in .gitmodules for path 'bla-bla/foo-bar'。首先,您必须删除所有尚未进行子调制的内部存储库。
it3xl

1
最佳答案(•̀ω•́)✧
RăzvanFlavius Panda

请注意,如果要使用该--recursive标志,则必须显式添加以下status命令:git submodule status --recursive有效,但git submodule --recursive无效。
山姆

17

我用这个:

git config --list|egrep ^submodule

1
@IgorGanapolsky刚刚读了Stefaan的评论:如果尚未初始化子模块,这将不起作用。无输出=无子模块(空列表)。
蒂姆(Tim)

这是我所需要的。它显示了子模块的子文件夹及其遥控器的URL。
Thinsoldier

17

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

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

git ls-files --stage | grep 160000

这个答案很干净,与mholm815的答案解析.gitmodules相比有什么缺点吗?
Colin D Bennett

顺便说一句,如果您只想要子模块的名称,请使用git ls-files --stage | grep 160000 | perl -ne 'chomp;split;print "$_[3]\n"'
Colin D Bennett 2014年

这对我有用,因为我.gimodules什么也没有.git/config。(我不确定它是如何发生的,我使存储库处于这种状态。这是一个错误,所以解决方案是git rm。)
wenzeslaus

1
它正在使用裸仓库。与.gitmodules解决方案不同。
kupson

1
这是最好的答案。只是一个小问题:grep "^160000 "会更加健壮。
拉西

12

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

git submodule foreach --quiet 'echo $name'

1
该答案是唯一的,因为它仅适用于已初始化的子模块。根据您的需要,这可能是所需的行为(但它也可能是意外的和不希望有的。)
塞斯·约翰逊

9

您可以使用:

git submodule | awk '{ print $2 }'


不赞成投票。此命令很危险,因为它对于名为test (master)有效的子模块名称)的子模块iE (名称(带空格并带有括号))失败。该命令也无法修复,因为git可以打印modulemodule (branch)。甚至更糟,因为该命令不是porcellain,所以该命令的输出将来可能会更改,恕不另行通知。
蒂诺

7

我用这个:

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

输出(路径+版本):

tools/deploy_utils (0.2.4)

我看到的这个版本的缺点是速度很慢。它似乎在每个子模块上进行某种状态检查,也许在我的机器上每个子模块都进行一次状态检查,所以在具有许多子模块的项目中,这不是一个好的解决方案,或者是否应从脚本等运行
Colin D Bennett

7

要按名称列出所有子模块:

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



5

只是子模块路径讨好,夫人...

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

👍🏼


4
尽管这可能是问题的完全有效答案,但以下是一些警告:仅适用于名称中不包含点的子模块。点在模块名称中完全有效。它不限于.url键,因此也可能会显示其他条目(通常不显示任何条目,但是会发生错误)。您应该--local在这里使用,因为您不想看到--global--system设置。最后要注意的是,由于它可能会被忽略,因此它仅适用于已经存在于其中的子模块.git/config(例如after git submodule init,请参见问题)。
蒂诺

5

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

因此,借助“ 使用cut命令将空间用作分隔符 ”:

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'使用,并使用类似的方法... | while IFS='' read -d '' path; do ...对bash进行进一步处理。
这需要现代的bash能够理解read -d ''(不要忘记之间的空格-d and '')。


嗯...“一定有更好的方法”是我的直接想法
arcseldon

猜猜我可以在bash配置文件中为该命令添加别名,谢谢。
arcseldon'3

对于其中带有空格的子模块,此操作将失败。另请注意,路径可能包含换行符(git submodule add https://github.com/hilbix/bashy.git "sub module"; git mv 'sub module' $'sub\nmodule')。请参阅我对已接受答案的评论,评论与您的​​评论非常相似。
蒂诺

@Tino好点。我已将您的评论包括在答案中,以提高知名度。
VonC

3

在我的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

在我的方法,我尽量从处理输出git config --get-regexpawktrsed,...而是传递一个零字节分开回git config --get。这是为了避免子模块中的换行符,空格和其他特殊字符(例如Unicode)出现问题paths。另外,由于您可能希望将此列表用于进一步的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

0

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

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

0

这是从.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-)

另外,此解决方案似乎可以正确处理转义的双引号示例,而上述解决方案会产生转义的输出。在某些情况下可能不正确。;-)
devC0de

“上方”和“下方”在按投票排序顺序中实际上并不起作用。
恩特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.