List Submodules in a Git Repository

List Submodules in a Git Repository

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?

3

20 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.

6

You can use git submodule status or optionally git submodule status --recursive if you want to show nested submodules.

From the Git documentation:

Show 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.

5

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.

5

To 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.

3

Use:

$ git submodule

It will list all the submodules in the specified Git repository.

4

I use this:

git config --list|egrep ^submodule
2

I 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
5

If 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'
1

You can use:

git submodule | awk '{ print $2 }'
2

I use this one:

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

Output (path + version):

tools/deploy_utils (0.2.4)
1

To 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.

1

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

👍🏼

1

git 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:

git 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 understands read -d '' (do not forget the space between -d and '').

4

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
Sophia Al-Mansoor
Author

Sophia Al-Mansoor

Sophia analyzes international trade, startup ecosystems, retail transformation, and supply chain logistics for modern digital publications.