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 

[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-) 
2

To 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 

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like