Список подмодулей в репозитории Git


247

У меня есть Git-репозиторий, в котором есть несколько подмодулей. Как мне перечислить имена всех подмодулей после git submodule initзапуска?

Команда git submodule foreachможет отображать имена подмодулей, но это работает только после того, как они были извлечены, чего не произошло после шага инициализации. Есть еще несколько шагов в цепочке, которые необходимо выполнить, прежде чем их можно будет извлечь, и я не хочу жестко привязывать имена подмодулей к сценарию.

Так есть ли команда Git, чтобы получить имена всех зарегистрированных в настоящее время, но еще не проверенных подмодулей?


5
Очень глупо, что это не так, но ответ Сандипа показывает, что git submoduleведет себя так, как я ожидал от гипотетического git submodule listповедения - я просто никогда не думал проверять, что происходит без аргументов git submodule. (Рад, что я проверил эту ссылку, так как изначально я получил неправильную ссылку «Поделиться»!)
мудрец

4
Я пришел сюда, потому git submodule listчто не существовало и git submodule helpне помогло (согласно последнему, решение git submoduleне является допустимым использованием).
user2394284

Большинство ответов (включая принятые) перечисляются в подмодуле paths, а не в namesпрерываниях, когда они содержат специальные символы. Я попытался дать ответ для имен и путей, которые должны быть безопасными: stackoverflow.com/a/56912913/3215929
Ente

Ответы:


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 }'

вы получите только сам путь субмодуля.


Но он ничего не печатает на консоли.
Игорь Ганапольский

2
@IgorGanapolsky - вы можете печатать на консоли, если вы делаете это cat .gitmodulesв корне хранилища ...
sdaau

awkЧасть терпит неудачу , если у вас есть пробелы в пути подмодуль.
Майкл

@Ikke Я хотел бы получить список подмодулей, включая имя, путь и URL. Я попробовал следующую команду Git, но она ничего не возвращает: git config --file = .gitmodules --get-regexp. *? Submodule (. *)] Path = (. *) Url = (. *) I попробовал git config --file = .gitmodules --get-regexp. *? (подмодуль). *? (путь). *? (url). Возможно, у вас есть решение. Заранее спасибо.
Одрай

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и '').
Тино

111

Вы можете использовать git submodule statusили опционально, git submodule status --recursiveесли хотите показать вложенные подмодули.

Из документации Git:

Показать статус субмодулей. Это напечатает SHA-1 текущего извлеченного коммита для каждого подмодуля, а также путь к подмодулю и выходные данные описания git для SHA-1. Каждому SHA-1 будет предшествовать префикс - если субмодуль не инициализирован, + если текущий извлеченный коммит субмодуля не соответствует SHA-1, найденному в индексе содержащего репозитория, и U, если субмодуль имеет конфликты слияния.


2
Просто запустите, git submodule update --init --recursiveчтобы инициализировать все подмодули.
Джон Купс

2
@IgorGanapolsky только что прочитал комментарий Стефаана: этот вид команд не работает, если субмодули еще не инициализированы . Нет вывода = нет подмодуля (пустой список).
Тим

6
Это, безусловно, следует пометить как лучший ответ для использования простой и встроенной gitкоманды без помощи bash. И это решение элегантно в том смысле, что оно хорошо работает в любой точке рабочей копии (но, конечно, сами подмодули, результат которых применяется непосредственно к себе).
Тим

Как говорится в ответе Сандипа Гилла , git submoduleаргументы без аргументов совпадают git submodule status, поэтому вы можете сэкономить, набрав 7 символов ;-)
Сэм

@Sam Они не совсем одинаковые, так как git submodule status --recursiveработает, но git submodule --recursiveне делает. Эти 7 персонажей действительно что-то покупают. Это зависит от ваших потребностей, хотя.
Кевин Рак

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.
Колин Д. Беннетт

6
И это незабываемо для загрузки!
Дан

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.
Тино,

58

Следующая команда выведет список подмодулей:

git submodule--helper list

Вывод примерно такой:

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

Примечание: требуется Git 2.7.0 или выше.


1
Большой!. Где эта команда задокументирована? Работает над Git для Windwos, но не может найти никаких документов на git-scm.com
DarVar

1
git ls-files --stage | grep ^160000(из Ответа stackoverflow.com/a/29325219 ), кажется, дает тот же результат, так что, возможно, это хорошая замена, если вам нужно быть совместимым со старыми гитами.
Тино

Обратите внимание на --двойные submodule--helper
штрихи

26

Использование:

$ git submodule

В нем будут перечислены все подмодули в указанном Git-репозитории.


3
Это фактически дублирующий ответ двух других упомянутых ответов git submodule [status](обратите внимание, что statusподразумевается, если опущен, так что это то же самое).
Колин Д. Беннетт

Не всегда работает fatal: no submodule mapping found in .gitmodules for path 'bla-bla/foo-bar', Сначала вы должны удалить все внутренние репозитории, которые еще не запланированы.
it3xl

1
Лучший ответ (• ̀ ω • ́) ✧
Răzvan Flavius ​​Panda

Обратите внимание, что если вы хотите использовать --recursiveфлаг, вы должны явно добавить statusкоманду: git submodule status --recursiveработает, но git submodule --recursiveне делает.
Сэм

17

Я использую это:

git config --list|egrep ^submodule

1
@IgorGanapolsky только что прочитал комментарий Стефаана: Это не работает, если субмодули еще не инициализированы . Нет вывода = нет подмодуля (пустой список).
Тим

Это именно то, что мне было нужно. Он показывает подпапки субмодулей и URL-адреса их пультов.
Thinsoldier

17

Я заметил, что команда, предоставленная в ответе на этот вопрос, дала мне информацию, которую я искал:

В .gitmodule не найдено отображение подмодулей для пути, который не является подмодулем

git ls-files --stage | grep 160000

Этот ответ хорош и понятен , есть ли недостатки по сравнению с ответом mholm815 .gitmodules ?
Колин Д. Беннетт

Кстати, если вы просто хотите имена подмодулей, используйтеgit ls-files --stage | grep 160000 | perl -ne 'chomp;split;print "$_[3]\n"'
Colin D Bennett

Это сработало для меня, так как у меня не было .gimodulesили ничего в .git/config. (Я не уверен, как это произошло, я получил хранилище в этом состоянии. Это была какая-то ошибка, поэтому решение было git rm.)
wenzeslaus

1
Это работает с голыми репозиториями. В отличие от решения .gitmodules.
Купсон

1
Это лучший ответ. Просто маленькая гнида: grep "^160000 "будет чуть более крепкой.
Ласси

12

Если вы не против работать только с инициализированными подмодулями, вы можете использовать их, git submodule foreachчтобы избежать разбора текста.

git submodule foreach --quiet 'echo $name'

1
Этот ответ уникален тем, что он работает только для субмодулей, которые были инициализированы. В зависимости от того, что вам нужно, это может быть желаемое поведение (но оно также может быть неожиданным и нежелательным.)
Сет Джонсон

9

Ты можешь использовать:

git submodule | awk '{ print $2 }'

1
Это похоже на тот же вывод, что и stackoverflow.com/a/23490756/895245, но оно короче.
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

Downvoted. Эта команда опасна, потому что она не выполняется для подмодуля iE test (master)(имя с пробелом, за которым следуют скобки), который является допустимым именем подмодуля . Команда также не может быть исправлена, потому что git либо печатает, moduleлибо module (branch). И это еще хуже, поскольку эта команда не является фарфоровой, поэтому выходные данные команды могут измениться в будущем без предварительного уведомления.
Тино

7

Я использую это:

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

Выход (путь + версия):

tools/deploy_utils (0.2.4)

Недостаток этой версии в том, что она медленная. Похоже, что он выполняет какую-то проверку состояния каждого подмодуля, возможно, около секунды на подмодуль на моем компьютере, поэтому в проектах с большим количеством подмодулей это не очень хорошее решение, или его следует запускать из сценария и т. Д.
Колин Д. Беннетт

7

Чтобы вывести список всех подмодулей по имени:

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


7

Это сработало для меня:

git ls-files --stage | grep ^160000

Он основан на этой замечательной статье: Понимание Git Submodules

Это должно прочитать grep ^160000.


1
git ls-files --stage | grep ^160000Кажется, выводит тот же вывод, что и git submodule--helper listиз ответа stackoverflow.com/a/40877379
Tino

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(например, после git submodule init, см. Вопрос).
Тино

5

git configпозволяет указать файл конфигурации.
И .gitmodules это конфигурационный файл.

Итак, с помощью « использовать пробел в качестве разделителя с командой cut »:

git config --file=.gitmodules --get-regexp ^^submodule.*\.path$ | cut -d " " -f 2

Это будет перечислять только пути, по одному на объявленный подмодуль.

Как Tino указывает в комментариях :

  • Это не работает для подмодулей с пробелами в нем.
  • пути субмодулей могут содержать переводы строк , как в

    git submodule add https://github.com/hilbix/bashy.git "sub module"
      git mv 'sub module' $'sub\nmodule'
    

В качестве более надежной альтернативы Тино предлагает:

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

Это не работает для подмодулей с пробелами в нем. Также обратите внимание, что пути могут содержать Newlines ( git submodule add https://github.com/hilbix/bashy.git "sub module"; git mv 'sub module' $'sub\nmodule'). Смотрите мой комментарий к принятому ответу, который очень похож на ваш.
Тино,

@ Тино Хорошие очки. Я включил ваш комментарий в ответ для большей наглядности.
VonC

3

В моей версии Git [1] каждый подмодуль Git имеет a nameи a path. Они не обязательно должны быть одинаковыми [2] . Получение обоих надежным способом без проверки подмодулей first ( git update --init) - сложная задача волшебника оболочки.

Получить список субмодулей names

Я не нашел способ, как добиться этого с помощью git configили любой другой gitкоманды. Поэтому мы вернулись к регулярному выражению .gitmodules(супер уродливо). Но это кажется несколько безопасным, поскольку gitограничивает возможное пространство кода, разрешенное для подмодуля names. Кроме того, поскольку вы, вероятно, захотите использовать этот список для дальнейшей обработки оболочки, решение ниже 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-regexpс awk, tr, sed, ... , но вместо того, чтобы передать его нулевые байты отделенными обратно git config --get. Это сделано для того, чтобы избежать проблем с символами новой строки, пробелами и другими специальными символами (например, Unicode) в подмодуле paths. Кроме того, поскольку вы, вероятно, захотите использовать этот список для дальнейшей обработки оболочки, решение ниже 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] Подмодуль с расходящимися nameиpath

Настроить тестовый репозиторий:

$ 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 = ./

Переместите субмодуль для создания nameи pathрасхождения:

$ 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

Вот еще один способ проанализировать имена подмодулей Git из .gitmodules без необходимости использования 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

«выше» и «ниже» на самом деле не работает в порядке сортировки на основе голосования.
Ente
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.