275

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?

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
tpg2114
  • 11,938
  • 6
  • 37
  • 54
  • 5
    Super silly that it doesn't, but [Sandeep's answer](http://stackoverflow.com/a/26083995/527489) shows that `git submodule` behaves as I expected a hypothetical `git submodule list` to behave - I just never thought to check what happens with *no* arguments to `git submodule`. (Glad I tested that link, since I grabbed the wrong 'Share' link initially!) – sage Jan 20 '17 at 23:38
  • 4
    I came here because `git submodule list` didn't exist and `git submodule help` didn't help (according to the latter, the solution, `git submodule`, is not valid usage). – user2394284 Feb 07 '17 at 13:24
  • Most of the answers (including the accepted one) list submodule `paths`, not `names` and break when they contain special characters. I tried to give an answer for both names and paths which should be safe: https://stackoverflow.com/a/56912913/3215929 – Ente Jul 06 '19 at 16:52

19 Answers19

198

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.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Ikke
  • 90,705
  • 23
  • 91
  • 118
  • But it doesn't print anything out on the console. – IgorGanapolsky Feb 10 '15 at 14:03
  • 2
    @IgorGanapolsky - you can print on the console, if you do `cat .gitmodules` in the repository root... – sdaau Sep 17 '15 at 11:56
  • 1
    The `awk` part fails if you have spaces in the submodule path. – maikel Aug 02 '17 at 01:03
  • @Ikke I would like to retrieve a list of submodules including name, path and url. I tried the following Git command, but it doesn't return anything: git config --file=.gitmodules --get-regexp .*?submodule (.*)] path = (.*) url = (.*) I tried git config --file=.gitmodules --get-regexp .*?(submodule).*?(path).*?(url) as well. Perhaps you have a solution. Thank you in advance. – Odrai Nov 17 '17 at 20:06
  • @Odrai If you just want everything, why don't you just specify `--list`? Like: `git config --file .gitmodules -l` – Ikke Nov 18 '17 at 20:09
  • 11
    **WARNING: `awk` fails for submodules with spaces!** The command should read **`git config -z --file .gitmodules --get-regexp '\.path$' | sed -nz 's/^[^\n]*\n//p' | tr '\0' '\n'`** (you need a modern `sed` with `-z`). This fails for **paths with Newlines in them** (they can be created with `git mv`). If you want to be safe against these, 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 `''`). – Tino Feb 04 '18 at 15:22
136

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.

Jon Koops
  • 6,949
  • 5
  • 24
  • 45
  • 2
    Just run `git submodule update --init --recursive` to initialise all submodule. – Jon Koops Feb 11 '15 at 12:05
  • 2
    @IgorGanapolsky just read at Stefaan's comment: this kind of commands do _not work if the submodules are not initialised yet_. No output = no submodule (empty list). – Tim Nov 25 '15 at 10:45
  • 6
    This should definitely be marked as _best answer_ for using a simple and native `git`-based command without any help from bash. And this solution is elegant in the way that it runs well at any point of the working copy (but the submodules themselves of course, for which the result applies directly to themselves). – Tim Nov 25 '15 at 10:49
  • As Sandeep Gill's [answer](https://stackoverflow.com/a/26083995/328817) says, `git submodule` with no arguments is the same as `git submodule status`, so you can save yourself typing 7 characters ;-) – Sam Jun 21 '19 at 09:15
  • @Sam They aren't quite the same since `git submodule status --recursive` works, but `git submodule --recursive` doesn't. Those 7 characters do buy you something. It depends on your needs though. – Kevin Rak Jan 14 '20 at 19:31
67

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.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
A.T.
  • 859
  • 6
  • 9
  • 3
    Great!. Where is this command documented. Works on Git for Windwos but cannot find any docs on https://git-scm.com/ – DarVar Jan 03 '17 at 16:08
  • 1
    `git ls-files --stage | grep ^160000` (from Answer https://stackoverflow.com/a/29325219) seems to produce the same result, so perhaps this is a good replacement if you need to be compatible to older gits. – Tino Feb 04 '18 at 14:58
  • Pay attention to `--` double dashes in `submodule--helper` – it3xl May 01 '19 at 09:13
  • I want to see which branch each submodule is on, how do I do that? – Boris Mar 25 '21 at 19:10
  • 1
    This one is not recursive. – Warren P Mar 26 '21 at 17:46
61

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.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
mholm815
  • 1,799
  • 17
  • 14
  • 3
    Instead, use `perl -ne '/^\s*path =\s*(.*)/ and push(@submods, $1); END { print(join("\n", sort(@submods)));}' "$(git rev-parse --show-toplevel)/.gitmodules"` which compared to this answer (1) works from any subdirectory (though not inside a submodule); (2) sorts the submodules by name; and (3) ignores commented lines in .gitmodules. – Colin D Bennett Dec 19 '14 at 18:24
  • 6
    And it's memorable to boot! – Dan Jun 22 '15 at 20:47
  • 1
    Note that this is very sloppy way of doing it, as `path` might be present in the submodule name (`git submodule add https://github.com/commercialhaskell/path.git`). But you probably already knew that before. If you want to access `.gitconfig` from anywhere in a worktree or need to run this in a `--bare` repository, you can use something like `git cat-file -p HEAD:.gitmodules | ...`. If you need to refer to the "staged" file, you can do `git cat-file -p :.gitmodules | ...`, however this needs a git `index` to be present. – Tino Feb 04 '18 at 16:23
32

Use:

$ git submodule

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

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Sandeep Gill
  • 493
  • 4
  • 5
  • 4
    This is effectively a duplicate answer of the two other answers that mention `git submodule [status]` (note that `status` is implied if omitted, so this is the same). – Colin D Bennett Dec 19 '14 at 18:28
  • Doesn't always work. `fatal: no submodule mapping found in .gitmodules for path 'bla-bla/foo-bar'`. First you have to delete all inner repositories that are not submoduled yet. – it3xl May 01 '19 at 09:17
  • 2
    Best answer ( •̀ ω •́ )✧ – Răzvan Flavius Panda Aug 20 '19 at 22:41
  • 1
    Note that if you want to use the `--recursive` flag, you have to explicitly add the `status` command: `git submodule status --recursive` works, but `git submodule --recursive` doesn't. – Sam Jan 15 '20 at 12:44
17

I use this:

git config --list|egrep ^submodule
James McMahon
  • 45,276
  • 62
  • 194
  • 274
Robert Sjödahl
  • 658
  • 5
  • 16
  • 1
    @IgorGanapolsky just read at Stefaan's comment: _This does not work if the submodules are not initialised yet_. No output = no submodule (empty list). – Tim Nov 25 '15 at 10:41
  • This is exaclty what I needed. It shows the subfolders of the submodules and the urls of their remotes. – thinsoldier Dec 10 '15 at 22:08
17

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
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Max Cantor
  • 7,949
  • 6
  • 42
  • 59
  • This answer is nice and clean, are there any disadvantages compared to [mholm815's answer parsing .gitmodules](http://stackoverflow.com/a/17157444/994153)? – Colin D Bennett Dec 17 '14 at 21:35
  • BTW, if you just want the names of the submodules, use `git ls-files --stage | grep 160000 | perl -ne 'chomp;split;print "$_[3]\n"'` – Colin D Bennett Dec 19 '14 at 18:21
  • This worked for me since I had no `.gimodules` or anything in `.git/config`. (I'm not sure how it happen, I got the repository in this state. It was some mistake, so solution was `git rm`.) – wenzeslaus Aug 11 '15 at 16:09
  • 1
    It's working with bare repos. Unlike .gitmodules solution. – kupson Nov 28 '16 at 11:25
  • 1
    This is the best answer. Just a small nit: `grep "^160000 "` would be slightly more robust. – Lassi Jan 15 '17 at 14:42
14

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'
benesch
  • 4,971
  • 1
  • 20
  • 33
  • 2
    This answer is unique in that it only works for submodules that have been initialized. Depending on what you need, this might be desired behavior (but it might also be unexpected and undesired.) – Seth Johnson Jan 24 '20 at 16:24
9

You can use:

git submodule | awk '{ print $2 }'
fabceolin
  • 317
  • 3
  • 4
  • 1
    This seems to produce the same output as http://stackoverflow.com/a/23490756/895245 but is shorter. – Ciro Santilli新疆棉花TRUMP BAN BAD May 11 '16 at 07:58
  • Downvoted. This command is dangerous, because it fails for a submodule i.E. named **`test (master)`** (name with a space followed by parentheses) , which **is a valid submodule name**. The command also cannot be fixed, because git either prints `module` or `module (branch)`. And it is even worse, as this command is not porcellain, so the output of the command may change in future without prior notice. – Tino Feb 04 '18 at 14:50
9

To list all submodules by name:

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

sschuberth
  • 23,652
  • 5
  • 80
  • 125
bGorle
  • 1,748
  • 2
  • 20
  • 25
8

I use this one:

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

Output (path + version):

tools/deploy_utils (0.2.4)
Skarab
  • 6,511
  • 11
  • 44
  • 81
  • The disadvantage I see of this version is that it is slow. It seems to be doing some kind of status checking on each submodule, maybe close to a second per submodule on my machine, so on projects with many submodules this is not a good solution, or if it should be run from a script etc. – Colin D Bennett Dec 19 '14 at 18:27
7

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.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
LTK
  • 87
  • 1
  • 1
  • 1
    `git ls-files --stage | grep ^160000` seems to produce the same output as `git submodule--helper list` from answer https://stackoverflow.com/a/40877379 – Tino Feb 04 '18 at 14:56
5

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

Alex Gray
  • 14,784
  • 6
  • 90
  • 114
  • 4
    While this probably is a completely valid answer for the question, here are some caveats: This only works for submodules which do not contain dots in their name. A dot is completely valid in module names. It does not restrict to `.url` keys, so other entries might show up as well (usually there are none, but shit happens). You should use `--local` here, because you do not want to see `--global` and `--system` settings. And last to note, because it might be overlooked, it only works for submodules, which are already present in `.git/config` (like after `git submodule init`, see question). – Tino Feb 04 '18 at 15:58
5

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 https://github.com/hilbix/bashy.git "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 '').

VonC
  • 1,042,979
  • 435
  • 3,649
  • 4,283
  • hmmm... "there must be a better way" are my immediate thoughts – arcseldon Mar 01 '16 at 00:31
  • guess i could alias that command in bash profile etc. thanks anyhow. – arcseldon Mar 01 '16 at 00:32
  • This fails for submodules with spaces in it. Also note, that paths may contain Newlines (`git submodule add https://github.com/hilbix/bashy.git "sub module"; git mv 'sub module' $'sub\nmodule'`). See my comment to [the accepted answer](https://stackoverflow.com/a/12641787) which is very similar to yours. – Tino Feb 04 '18 at 15:31
  • @Tino Good points. I have included your comment in the answer for more visibility. – VonC Feb 04 '18 at 16:28
3

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
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Ente
  • 1,898
  • 11
  • 25
2

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.

PolyGlot
  • 432
  • 2
  • 7
1

If there isn't any .gitmodules file, but a submodules configuration exists in .git/modules/:

find .git/modules/ -name config -exec grep url {} \;
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
MrSwed
  • 367
  • 3
  • 12
0

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-)
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
devC0de
  • 1
  • 2
  • Plus, this solution seems to handle the escaped double quotes example correctly whereas the above solution produces an escaped output. Which may not be correct in some cases. ;-) – devC0de Jul 06 '19 at 14:27
  • "above" and "below" does not really work in a voting based sort order. – Ente Jul 06 '19 at 15:31
0

To get path

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

To get names as in repos

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