I have a Git repository that has several submodules in it. How do I list the names of all the submodules after git submodule init has been run?
The git submodule foreach command could echo the names of the submodule, but that only works once they have been checked out which has not happened after the init step. There are more steps in the chain that need to happen before they can be checked out, and I don't want to have to hard-wire names of submodules into the script.
So is there a Git command to get the names of all currently registered, but not yet checked out submodules?
320 Answers
You could use the same mechanism as git submodule init uses itself, namely, look at .gitmodules. This files enumerates each submodule path and the URL it refers to.
For example, from root of repository, cat .gitmodules will print contents to the screen (assuming you have cat).
Because .gitmodule files have the Git configuration format, you can use git config to parse those files:
git config --file .gitmodules --name-only --get-regexp path Would show you all submodule entries, and with
git config --file .gitmodules --get-regexp path | awk '{ print $2 }' you would only get the submodule path itself.
6You can use git submodule status or optionally git submodule status --recursive if you want to show nested submodules.
From the Git documentation:
5Show the status of the submodules. This will print the SHA-1 of the currently checked out commit for each submodule, along with the submodule path and the output of git describe for the SHA-1. Each SHA-1 will be prefixed with - if the submodule is not initialized, + if the currently checked out submodule commit does not match the SHA-1 found in the index of the containing repository and U if the submodule has merge conflicts.
The following command will list the submodules:
git submodule--helper list The output is something like this:
<mode> <sha1> <stage> <location> Note: It requires Git 2.7.0 or above.
5To return just the names of the registered submodules, you can use this command:
grep path .gitmodules | sed 's/.*= //' Think of it as git submodule --list which doesn't exist.
Use:
$ git submodule It will list all the submodules in the specified Git repository.
4I use this:
git config --list|egrep ^submodule 2I noticed that the command provided in an answer to this question gave me the information I was looking for:
No submodule mapping found in .gitmodule for a path that's not a submodule
git ls-files --stage | grep 160000 5If you don't mind operating only on initialized submodules, you can use git submodule foreach to avoid text parsing.
git submodule foreach --quiet 'echo $name' 1You can use:
git submodule | awk '{ print $2 }' 2I use this one:
git submodule status | cut -d' ' -f3-4 Output (path + version):
tools/deploy_utils (0.2.4) 1To list all submodules by name:
git submodule --quiet foreach --recursive 'echo $name'
This worked for me:
git ls-files --stage | grep ^160000 It is based on this great article: Understanding Git Submodules
It must read grep ^160000.
Display ALL info about each submodule using built-in git functions:
git submodule foreach -q git config -l Or just URL-s:
git submodule foreach -q git config remote.origin.url Stolen from here.
Just the submodule paths please, ma'am...
git config --list | grep \^submodule | cut -f 2 -d . Vendor/BaseModel Vendor/ObjectMatcher Vendor/OrderedDictionary Vendor/_ObjC Vendor/XCodeHelpers
👍🏼
1git config allows to specify a config file.
And .gitmodules is a config file.
So, with the help of "use space as a delimiter with cut command":
git config --file=.gitmodules --get-regexp ^^submodule.*\.path$ | cut -d " " -f 2 That will list only the paths, one per declared submodule.
As Tino points out in the comments:
- This fails for submodules with spaces in it.
submodule paths may contain newlines, as in
git submodule add "sub module" git mv 'sub module' $'sub\nmodule'
As a more robust alternative, Tino proposes:
4git config -z --file .gitmodules --get-regexp '\.path$' | \ sed -nz 's/^[^\n]*\n//p' | \ tr '\0' '\n'For paths with newlines in them (they can be created with
git mv), leave away the| tr '\0' '\n'and use something like... | while IFS='' read -d '' path; do ...for further processing with bash.
This needs a modern bash which understandsread -d ''(do not forget the space between-d and '').
In my version of Git [1], every Git submodule has a name and a path. They don't necessarily have to be the same [2]. Getting both in a reliable way, without checking out the submodules first (git update --init), is a tricky bit of shell wizardry.
Get a list of submodule names
I didn't find a way how to achieve this using git config or any other git command. Therefore we are back to regex on .gitmodules (super ugly). But it seems to be somewhat safe since git limits the possible code space allowed for submodule names. In addition, since you probably want to use this list for further shell processing, the solution below separate entries with 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" And in your script:
#!/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" ) Note: read -rd '' requires bash and won't work with sh.
Get a list of submodule paths
In my approach I try not to process the output from git config --get-regexp with awk, tr, sed, ... but instead pass it a zero byte separated back to git config --get. This is to avoid problems with newlines, spaces and other special characters (e.g. Unicode) in the submodule paths. In addition, since you probably want to use this list for further shell processing, the solution below separate entries with NULL-bytes (\0).
$ git config --null --file .gitmodules --name-only --get-regexp '\.path$' \ | xargs -0 -n1 git config --null --file .gitmodules --get For example, in a Bash script you could then:
#!/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 ) Note: read -rd '' requires bash and won't work with sh.
Footnotes
[1] Git version
$ git --version git version 2.22.0 [2] Submodule with diverging name and path
Set up test repository:
$ 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 = ./ Move submodule to make name and path diverge:
$ 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 Testing
Set up test repository:
$ 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 = ./ Get list of submodule 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 Get list of submodule 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 If there isn't any .gitmodules file, but a submodules configuration exists in .git/modules/:
find .git/modules/ -name config -exec grep url {} \; Here is another way to parse Git submodule names from .gitmodules without the need for sed or fancy IFS settings. :-)
#!/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-) 2To get path
grep url .gitmodules | sed 's/.*= //' To get names as in repos
grep path .gitmodules | sed 's/.*= //' Just a straight list of submodules:
git submodule--helper list | cut -d$'\t' -f 2