Listar submódulos em um repositório Git

246

Eu tenho um repositório Git que possui vários submódulos. Como listar os nomes de todos os submódulos após a git submodule initexecução?

O git submodule foreachcomando poderia ecoar os nomes do submódulo, mas isso só funciona depois que eles foram retirados, o que não aconteceu após a etapa init. Há mais etapas na cadeia que precisam acontecer antes que possam ser verificadas, e não quero ter que conectar os nomes dos submódulos ao script.

Existe um comando Git para obter os nomes de todos os submódulos registrados atualmente, mas ainda não verificados?

tpg2114
fonte
4
Super bobo que não, mas a resposta de Sandeep mostra que git submodulese comporta como eu esperava que um git submodule listcomportamento hipotético - eu nunca pensei em verificar o que acontece sem argumentos git submodule. (Ainda bem que testei esse link, desde que peguei o link errado 'Share' inicialmente!) #
196
4
Eu vim aqui porque git submodule listnão existia e git submodule helpnão ajudou (de acordo com o último, a solução, git submodulenão é um uso válido).
user2394284
A maioria das respostas (incluindo a aceita) lista o submódulo paths, namese não é interrompido quando contêm caracteres especiais. Eu tentei dar uma resposta para nomes e caminhos que devem ser seguros: stackoverflow.com/a/56912913/3215929
Ente

Respostas:

175

Você pode usar o mesmo mecanismo que o git submodule initpróprio usa, ou seja, observe .gitmodules. Esses arquivos enumeram cada caminho do sub-módulo e a URL a que se refere.

Por exemplo, a partir da raiz do repositório, cat .gitmodulesimprimirá o conteúdo na tela (supondo que você tenha cat).

Como os arquivos .gitmodule têm o formato de configuração Git, você pode usar o git config para analisar esses arquivos:

git config --file .gitmodules --name-only --get-regexp path

Mostraria todas as entradas do submódulo e com

git config --file .gitmodules --get-regexp path | awk '{ print $2 }'

você apenas obteria o próprio caminho do submódulo.

Ikke
fonte
Mas não imprime nada no console.
IgorGanapolsky
2
@IgorGanapolsky - você pode imprimir no console, se fizer cat .gitmodulesna raiz do repositório ...
sdaau
A awkpeça falhará se você tiver espaços no caminho do submódulo.
Maikel
@ Ikke Eu gostaria de recuperar uma lista de submódulos, incluindo nome, caminho e URL. Tentei o seguinte comando Git, mas ele não retorna nada: git config --file = .gitmodules --get-regexp. *? Submodule (. *)] Path = (. *) Url = (. *) I tentei git config --file = .gitmodules --get-regexp. *? (submódulo). *? (caminho). *? (url) também. Talvez você tenha uma solução. Agradeço antecipadamente.
Odrai 17/11/19
8
AVISO: awkfalha para submódulos com espaços! O comando deve ler git config -z --file .gitmodules --get-regexp '\.path$' | sed -nz 's/^[^\n]*\n//p' | tr '\0' '\n'(você precisa de um moderno sedcom -z). Isso falha nos caminhos com Novas Linhas (eles podem ser criados com git mv). Se você quiser se proteger contra isso, deixe o | tr '\0' '\n'e use algo como ... | while IFS='' read -d '' path; do ...para processamento adicional com o bash. Isso precisa de uma festança moderna que entenda read -d ''(não esqueça o espaço entre -de '').
Tino
109

Você pode usar git submodule statusou, opcionalmente, git submodule status --recursivese desejar mostrar submódulos aninhados.

Na documentação do Git:

Mostrar o status dos submódulos. Isso imprimirá o SHA-1 da confirmação atualmente registrada para cada submódulo, junto com o caminho do submódulo e a saída do git description para o SHA-1. Cada SHA-1 será prefixado com - se o submódulo não for inicializado, + se a confirmação do submódulo com check-out atualmente não corresponder ao SHA-1 encontrado no índice do repositório que contém e U se o submódulo tiver conflitos de mesclagem.

Jon Koops
fonte
2
Basta executar git submodule update --init --recursivepara inicializar todo o submódulo.
Jon Koops 11/02
2
@IgorGanapolsky acabou de ler no comentário de Stefaan: esse tipo de comando não funciona se os submódulos ainda não foram inicializados . Sem saída = sem sub-módulo (lista vazia).
Tim
6
Definitivamente, isso deve ser marcado como a melhor resposta para o uso de um gitcomando simples e baseado em nativo sem qualquer ajuda do bash. E essa solução é elegante da maneira que funciona bem em qualquer ponto da cópia de trabalho (mas é claro que os próprios submódulos, para os quais o resultado se aplica diretamente a eles mesmos).
Tim
Como a resposta de Sandeep Gill diz, git submodulesem argumentos é o mesmo que git submodule statusvocê pode salvar 7 digitando 7 caracteres ;-) #
Sam
@ Sam Eles não são exatamente os mesmos desde que git submodule status --recursivefuncionam, mas git submodule --recursivenão. Esses 7 caracteres compram algo para você. Depende de suas necessidades embora.
Kevin Rak
60

Para retornar apenas os nomes dos submódulos registrados, você pode usar este comando:

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

Pense nisso como o git submodule --listque não existe.

mholm815
fonte
3
Em vez disso, o uso perl -ne '/^\s*path =\s*(.*)/ and push(@submods, $1); END { print(join("\n", sort(@submods)));}' "$(git rev-parse --show-toplevel)/.gitmodules"que, comparado a esta resposta (1), funciona a partir de qualquer subdiretório (embora não dentro de um submódulo); (2) classifica os submódulos por nome; e (3) ignora as linhas comentadas nos módulos .git.
Colin D Bennett
6
E é memorável para inicializar!
Dan
1
Observe que essa é uma maneira muito desleixada de fazê-lo, como pathpode estar presente no nome do submódulo ( git submodule add https://github.com/commercialhaskell/path.git). Mas você provavelmente já sabia disso antes. Se você deseja acessar .gitconfigde qualquer lugar em uma árvore de trabalho ou precisar executá-lo em um --barerepositório, pode usar algo como git cat-file -p HEAD:.gitmodules | .... Se você precisar consultar o arquivo "faseado", poderá fazê-lo git cat-file -p :.gitmodules | ..., no entanto, isso precisa de um git indexpara estar presente.
Tino
56

O comando a seguir listará os submódulos:

git submodule--helper list

A saída é algo como isto:

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

Nota: Requer Git 2.7.0 ou superior.

AT
fonte
1
Ótimo!. Onde esse comando está documentado. Funciona no Git for Windwos, mas não consegue encontrar documentos no git-scm.com
DarVar
1
git ls-files --stage | grep ^160000(da resposta stackoverflow.com/a/29325219 ) parece produzir o mesmo resultado, portanto, talvez este seja um bom substituto se você precisar ser compatível com gits mais antigos.
Tino
Preste atenção aos --traços de casal emsubmodule--helper
it3xl
25

Usar:

$ git submodule

Ele listará todos os submódulos no repositório Git especificado.

Sandeep Gill
fonte
3
Esta é efetivamente uma resposta duplicada das duas outras respostas mencionadas git submodule [status](observe que statusestá implícito se omitido, portanto é o mesmo).
Colin D Bennett
Nem sempre funciona. fatal: no submodule mapping found in .gitmodules for path 'bla-bla/foo-bar'. Primeiro, você deve excluir todos os repositórios internos que ainda não estão submodulados.
it3xl
1
Melhor resposta (̀ ω • ́) ✧
Răzvan Flavius ​​Panda
Observe que, se você deseja usar o --recursivesinalizador, é necessário adicionar explicitamente o statuscomando: git submodule status --recursivefunciona, mas git submodule --recursivenão.
Sam
17

Eu uso isso:

git config --list|egrep ^submodule
Robert Sjödahl
fonte
1
@IgorGanapolsky acabou de ler no comentário de Stefaan: Isso não funciona se os submódulos ainda não foram inicializados . Sem saída = sem sub-módulo (lista vazia).
Tim
Isso é exatamente o que eu precisava. Ele mostra as subpastas dos submódulos e os URLs de seus controles remotos.
Dez15
17

Percebi que o comando fornecido em resposta a esta pergunta me deu as informações que eu estava procurando:

Nenhum mapeamento de submódulo encontrado no .gitmodule para um caminho que não é um submódulo

git ls-files --stage | grep 160000
Max Cantor
fonte
Esta resposta é agradável e limpa. Existem desvantagens em comparação com a resposta do mholm815 que analisa .gitmodules ?
Colin D Bennett
BTW, se você quiser apenas os nomes dos submódulos, usegit ls-files --stage | grep 160000 | perl -ne 'chomp;split;print "$_[3]\n"'
Colin D Bennett
Isso funcionou para mim desde que eu não tinha nada .gimodulesou nada .git/config. (Eu não sei como isso acontecer, eu tenho o repositório neste estado Foi um engano, de modo solução foi. git rm.)
Wenzeslaus
1
Está trabalhando com repositórios nus. Ao contrário da solução .gitmodules.
Kupson
1
Esta é a melhor resposta. Apenas um pequeno detalhe: grep "^160000 "seria um pouco mais robusto.
Lassi
12

Se você não se importa de operar apenas em submódulos inicializados, pode usar git submodule foreachpara evitar a análise de texto.

git submodule foreach --quiet 'echo $name'
benesch
fonte
1
Essa resposta é única, pois só funciona para submódulos que foram inicializados. Dependendo do que você precisa, esse pode ser o comportamento desejado (mas também pode ser inesperado e indesejado).
Seth Johnson
9

Você pode usar:

git submodule | awk '{ print $2 }'
fabceolina
fonte
1
Isso parece produzir a mesma saída que stackoverflow.com/a/23490756/895245, mas é mais curto.
Ciro Santilli # 11/16
Votado. Este comando é perigoso, porque falha no sub-módulo iE chamado test (master)(nome com um espaço seguido por parênteses), que é um nome válido para o sub-módulo . O comando também não pode ser corrigido, porque o git imprime moduleou module (branch). E é ainda pior, como esse comando não é de porcelana, portanto a saída do comando pode mudar no futuro sem aviso prévio.
Tino
7

Eu uso este:

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

Saída (caminho + versão):

tools/deploy_utils (0.2.4)
Skarab
fonte
A desvantagem que vejo desta versão é que ela é lenta. Parece estar fazendo algum tipo de verificação de status em cada submódulo, talvez perto de um segundo por submódulo na minha máquina; portanto, em projetos com muitos submódulos, essa não é uma boa solução, ou se deve ser executada a partir de um script etc.
Colin D Bennett
7

Para listar todos os submódulos por nome:

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

bGorle
fonte
7

Isso funcionou para mim:

git ls-files --stage | grep ^160000

Ele é baseado neste excelente artigo: Entendendo os submódulos do Git

Deve ler grep ^160000.

LTK
fonte
1
git ls-files --stage | grep ^160000parece produzir a mesma saída que a git submodule--helper listda resposta stackoverflow.com/a/40877379
Tino #
5

Apenas os caminhos do submódulo, por favor, senhora ...

git config --list | grep \^submodule | cut -f 2 -d .
Vendor/BaseModel
Vendor/ObjectMatcher
Vendor/OrderedDictionary
Vendor/_ObjC
Vendor/XCodeHelpers

👍🏼

Alex Gray
fonte
4
Embora essa seja provavelmente uma resposta completamente válida para a pergunta, aqui estão algumas ressalvas: Isso funciona apenas para submódulos que não contêm pontos em seu nome. Um ponto é completamente válido nos nomes dos módulos. Ele não se restringe às .urlchaves; portanto, outras entradas também podem aparecer (geralmente não existem, mas as coisas acontecem). Você deve usar --localaqui, porque não deseja ver --globale --systemconfigurações. E por último, porque pode ser esquecido, funciona apenas para submódulos, que já estão presentes .git/config(como depois git submodule init, veja a pergunta).
Tino
5

git configpermite especificar um arquivo de configuração.
E .gitmodules é um arquivo de configuração.

Portanto, com a ajuda de " use o espaço como um delimitador com o comando cut ":

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

Isso listará apenas os caminhos, um por sub-módulo declarado.

Como Tino aponta nos comentários :

  • Isso falha para submódulos com espaços nele.
  • caminhos do submódulo podem conter novas linhas , como em

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

Como uma alternativa mais robusta, Tino propõe:

git config -z --file .gitmodules --get-regexp '\.path$' | \
  sed -nz 's/^[^\n]*\n//p' | \
  tr '\0' '\n' 

Para caminhos com novas linhas (eles podem ser criados com git mv), deixe o | tr '\0' '\n'e use algo como ... | while IFS='' read -d '' path; do ...para processamento adicional com o bash.
Isso precisa de uma festança moderna que entenda read -d ''(não esqueça o espaço entre elas -d and '').

VonC
fonte
hmmm ... "deve haver uma maneira melhor" são os meus pensamentos imediatos
arcseldon
acho que eu poderia alias que comando no perfil bash etc. obrigado de qualquer maneira.
Arcseldon 01/03/19
Isso falha nos submódulos com espaços nele. Observe também que os caminhos podem conter Novas Linhas ( git submodule add https://github.com/hilbix/bashy.git "sub module"; git mv 'sub module' $'sub\nmodule'). Veja meu comentário na resposta aceita, que é muito semelhante à sua.
Tino
@Tino Bons pontos. Incluímos seu comentário na resposta para obter mais visibilidade.
VonC
3

Na minha versão do Git [1] , todo submódulo do Git possui a namee a path. Eles não precisam necessariamente ser os mesmos [2] . Obter os dois de maneira confiável, sem verificar primeiro os submódulos ( git update --init), é um pouco complicado de assistente de shell.

Obter uma lista do submódulo names

Não encontrei uma maneira de conseguir isso usando git configou qualquer outro gitcomando. Portanto, estamos de volta à regex em .gitmodules(super feia). Mas parece ser um pouco seguro, pois gitlimita o possível espaço de código permitido para o submódulo names. Além disso, como você provavelmente deseja usar esta lista para processamento de shell adicional, a solução abaixo separa as entradas com 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"

E no seu 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"
)

Nota : read -rd ''requer bashe não funcionará sh.

Obter uma lista do submódulo paths

Na minha abordagem que eu tente não para processar a saída git config --get-regexpcom awk, tr, sed, ... mas em vez passá-lo a zero bytes separados de volta para git config --get. Isso é para evitar problemas com novas linhas, espaços e outros caracteres especiais (por exemplo, Unicode) no submódulo paths. Além disso, como você provavelmente deseja usar esta lista para processamento de shell adicional, a solução abaixo separa as entradas com NULL-bytes ( \0).

$ git config --null --file .gitmodules --name-only --get-regexp '\.path$' \
| xargs -0 -n1 git config --null --file .gitmodules --get

Por exemplo, em um script Bash, você pode:

#!/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
)

Nota : read -rd ''requer bashe não funcionará sh.


Notas de rodapé

[1] versão Git

$ git --version
git version 2.22.0

[2] Submódulo com divergentes nameepath

Configure o repositório de teste:

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

Mova o submódulo para criar namee pathdivergir:

$ 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

Teste

Configure o repositório de teste:

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

Obter lista de submódulo 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

Obter lista de submódulo 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
Ente
fonte
0

Se não houver nenhum .gitmodulesarquivo, mas existir uma configuração de submódulos em .git/modules/:

find .git/modules/ -name config -exec grep url {} \;
MrSwed
fonte
0

Aqui está outra maneira de analisar nomes de submodulos Git de .gitmodules sem a necessidade de configurações IFS sed ou sofisticadas. :-)

#!/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
fonte
Além disso, esta solução parece manipular o exemplo de aspas duplas com escape corretamente, enquanto a solução acima produz uma saída com escape. O que pode não estar correto em alguns casos. ;-)
devC0de
"acima" e "abaixo" realmente não funcionam em uma ordem de classificação baseada em votação.
Ente