Lista de submódulos en un repositorio Git

246

Tengo un repositorio de Git que tiene varios submódulos. ¿Cómo enumero los nombres de todos los submódulos después de que git submodule initse haya ejecutado?

El git submodule foreachcomando podría hacer eco de los nombres del submódulo, pero eso solo funciona una vez que se han desprotegido, lo que no sucedió después del paso de inicio. Hay más pasos en la cadena que deben suceder antes de que se puedan verificar, y no quiero tener que conectar los nombres de submódulos al script.

Entonces, ¿hay un comando Git para obtener los nombres de todos los submódulos actualmente registrados, pero aún no desprotegidos?

tpg2114
fuente
44
Súper tonto que no, pero la respuesta de Sandeep muestra que se git submodulecomporta como esperaba que se comportara un hipotético git submodule list: simplemente nunca pensé en verificar lo que sucede sin argumentos git submodule. (¡Me alegro de haber probado ese enlace, ya que inicialmente cogí el enlace incorrecto 'Compartir'!)
sabio
44
Vine aquí porque git submodule listno existía y git submodule helpno ayudó (según este último, la solución git submoduleno es un uso válido).
user2394284
La mayoría de las respuestas (incluida la aceptada) enumeran submódulos paths, no namesy se rompen cuando contienen caracteres especiales. Traté de dar una respuesta para los nombres y las rutas que deberían ser seguras: stackoverflow.com/a/56912913/3215929
Ente el

Respuestas:

175

Podrías usar el mismo mecanismo que se git submodule initusa, es decir, mirar .gitmodules. Estos archivos enumeran cada ruta de submódulo y la URL a la que se refiere.

Por ejemplo, desde la raíz del repositorio, cat .gitmodulesimprimirá el contenido en la pantalla (suponiendo que lo haya hecho cat).

Debido a que los archivos .gitmodule tienen el formato de configuración Git, puede usar git config para analizar esos archivos:

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

Le mostraría todas las entradas de submódulos y con

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

solo obtendría la ruta del submódulo en sí.

Ikke
fuente
Pero no imprime nada en la consola.
IgorGanapolsky
2
@IgorGanapolsky - puede imprimir en la consola, si lo hace cat .gitmodulesen la raíz del repositorio ...
sdaau
La awkparte falla si tiene espacios en la ruta del submódulo.
maikel
@ Ikke Me gustaría recuperar una lista de submódulos que incluyen nombre, ruta y url. Intenté el siguiente comando de Git, pero no devuelve nada: git config --file = .gitmodules --get-regexp. *? Submodule (. *)] Path = (. *) Url = (. *) I Probé git config --file = .gitmodules --get-regexp. *? (submodule). *? (ruta). *? (url) también. Quizás tengas una solución. Gracias de antemano.
Odrai
8
ADVERTENCIA: awkfalla para submódulos con espacios! El comando debería leer git config -z --file .gitmodules --get-regexp '\.path$' | sed -nz 's/^[^\n]*\n//p' | tr '\0' '\n'(necesita un moderno sedcon -z). Esto falla para las rutas con Newlines en ellas (se pueden crear con git mv). Si desea estar a salvo de estos, deje de lado | tr '\0' '\n'y use algo como ... | while IFS='' read -d '' path; do ...para un mayor procesamiento con bash. Esto necesita una fiesta moderna que comprenda read -d ''(no olvide el espacio entre -dy '').
Tino
109

Puede usar git submodule statusu opcionalmente git submodule status --recursivesi desea mostrar submódulos anidados.

De la documentación de Git:

Mostrar el estado de los submódulos. Esto imprimirá el SHA-1 del commit actualmente desprotegido para cada submódulo, junto con la ruta del submódulo y la salida de git describe para el SHA-1. Cada SHA-1 tendrá como prefijo: si el submódulo no se inicializa, + si la confirmación del submódulo actualmente desprotegido no coincide con el SHA-1 encontrado en el índice del repositorio que contiene y U si el submódulo tiene conflictos de fusión.

Jon Koops
fuente
2
Simplemente ejecute git submodule update --init --recursivepara inicializar todos los submódulos.
Jon Koops
2
@IgorGanapolsky acaba de leer el comentario de Stefaan: este tipo de comandos no funcionan si los submódulos aún no se han inicializado . Sin salida = sin submódulo (lista vacía).
Tim
66
Definitivamente, esto debería marcarse como la mejor respuesta para usar un gitcomando simple y basado en nativos sin ninguna ayuda de bash. Y esta solución es elegante en la forma en que funciona bien en cualquier punto de la copia de trabajo (pero los submódulos mismos, por supuesto, para lo cual el resultado se aplica directamente a ellos).
Tim
Como dice la respuesta de Sandeep Gill , git submodulesin argumentos es igual que git submodule status, por lo que puede ahorrarse escribiendo 7 caracteres ;-)
Sam
@Sam No son lo mismo ya que git submodule status --recursivefunciona, pero git submodule --recursiveno funciona. Esos 7 personajes te compran algo. Sin embargo, depende de tus necesidades.
Kevin Rak
60

Para devolver solo los nombres de los submódulos registrados, puede usar este comando:

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

Piense en ello como git submodule --listsi no existiera.

mholm815
fuente
3
En cambio, use el perl -ne '/^\s*path =\s*(.*)/ and push(@submods, $1); END { print(join("\n", sort(@submods)));}' "$(git rev-parse --show-toplevel)/.gitmodules"que en comparación con esta respuesta (1) funciona desde cualquier subdirectorio (aunque no dentro de un submódulo); (2) ordena los submódulos por nombre; e (3) ignora las líneas comentadas en .gitmodules.
Colin D Bennett
66
¡Y es memorable arrancar!
Dan
1
Tenga en cuenta que esta es una forma muy descuidada de hacerlo, ya que pathpodría estar presente en el nombre del submódulo ( git submodule add https://github.com/commercialhaskell/path.git). Pero probablemente ya lo sabías antes. Si desea acceder .gitconfigdesde cualquier lugar en un árbol de trabajo o necesita ejecutar esto en un --barerepositorio, puede usar algo como git cat-file -p HEAD:.gitmodules | .... Si necesita hacer referencia al archivo "en escena", puede hacerlo git cat-file -p :.gitmodules | ..., sin embargo, esto necesita un git indexpara estar presente.
Tino
56

El siguiente comando enumerará los submódulos:

git submodule--helper list

El resultado es algo como esto:

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

Nota: Requiere Git 2.7.0 o superior.

A
fuente
1
¡Excelente!. ¿Dónde se documenta este comando? Funciona en Git para Windwos pero no puede encontrar ningún documento en git-scm.com
DarVar
1
git ls-files --stage | grep ^160000(de la Respuesta stackoverflow.com/a/29325219 ) parece producir el mismo resultado, por lo que quizás este sea un buen reemplazo si necesita ser compatible con gits anteriores.
Tino
Tener en cuenta las --rayas dobles ensubmodule--helper
it3xl
25

Utilizar:

$ git submodule

Enumerará todos los submódulos en el repositorio Git especificado.

Sandeep Gill
fuente
3
Esta es efectivamente una respuesta duplicada de las otras dos respuestas que mencionan git submodule [status](tenga en cuenta que statusestá implícito si se omite, por lo que es lo mismo).
Colin D Bennett
No siempre funciona fatal: no submodule mapping found in .gitmodules for path 'bla-bla/foo-bar'. Primero debe eliminar todos los repositorios internos que aún no están submódulados.
it3xl
1
Mejor respuesta (• ̀ ω • ́) ✧
Răzvan Flavius ​​Panda
Tenga en cuenta que si desea usar la --recursivebandera, debe agregar explícitamente el statuscomando: git submodule status --recursivefunciona, pero git submodule --recursiveno lo hace.
Sam
17

Yo uso esto:

git config --list|egrep ^submodule
Robert Sjödahl
fuente
1
@IgorGanapolsky acaba de leer el comentario de Stefaan: Esto no funciona si los submódulos aún no se han inicializado . Sin salida = sin submódulo (lista vacía).
Tim
Esto es exactamente lo que necesitaba. Muestra las subcarpetas de los submódulos y las URL de sus controles remotos.
Thinsoldier
17

Noté que el comando proporcionado en una respuesta a esta pregunta me dio la información que estaba buscando:

No se encontró asignación de submódulo en .gitmodule para una ruta que no sea un submódulo

git ls-files --stage | grep 160000
Max Cantor
fuente
Esta respuesta es agradable y limpia, ¿hay alguna desventaja en comparación con la respuesta de mholm815 que analiza .gitmodules ?
Colin D Bennett
Por cierto, si solo quieres los nombres de los submódulos, usagit ls-files --stage | grep 160000 | perl -ne 'chomp;split;print "$_[3]\n"'
Colin D Bennett
Esto funcionó para mí ya que no tenía .gimodulesni nada .git/config. (No estoy seguro de cómo sucedió, obtuve el repositorio en este estado. Fue un error, así que la solución fue git rm)
Wenzeslaus
1
Está trabajando con repositorios desnudos. A diferencia de la solución .gitmodules.
kupson el
1
Esta es la mejor respuesta. Solo un pequeño error: grep "^160000 "sería un poco más robusto.
Lassi
12

Si no le importa operar solo en submódulos inicializados, puede usar git submodule foreachpara evitar el análisis de texto.

git submodule foreach --quiet 'echo $name'
Benesch
fuente
1
Esta respuesta es única, ya que solo funciona para submódulos que se han inicializado. Dependiendo de lo que necesite, este podría ser el comportamiento deseado (pero también podría ser inesperado y no deseado).
Seth Johnson
9

Puedes usar:

git submodule | awk '{ print $2 }'
fabceolin
fuente
1
Esto parece producir el mismo resultado que stackoverflow.com/a/23490756/895245 pero es más corto.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Voto negativo Este comando es peligroso porque falla para un submódulo iE llamado test (master)(nombre con un espacio seguido de paréntesis), que es un nombre de submódulo válido . El comando tampoco se puede arreglar, porque git imprime moduleo module (branch). Y es aún peor, ya que este comando no es porcelánico, por lo que la salida del comando puede cambiar en el futuro sin previo aviso.
Tino
7

Yo uso este:

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

Salida (ruta + versión):

tools/deploy_utils (0.2.4)
Skarab
fuente
La desventaja que veo de esta versión es que es lenta. Parece estar haciendo algún tipo de comprobación de estado en cada submódulo, tal vez cerca de un segundo por cada submódulo en mi máquina, por lo que en proyectos con muchos submódulos esto no es una buena solución, o si se debe ejecutar desde un script, etc.
Colin D Bennett
7

Para enumerar todos los submódulos por nombre:

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

bGorle
fuente
5

Solo los submódulos, por favor, señora ...

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

👍🏼

Alex Gray
fuente
44
Si bien esta es probablemente una respuesta completamente válida para la pregunta, aquí hay algunas advertencias: esto solo funciona para submódulos que no contienen puntos en su nombre. Un punto es completamente válido en los nombres de los módulos. No se restringe a las .urlteclas, por lo que otras entradas pueden aparecer también (por lo general, no hay ninguna, pero sucede una mierda). Debe usar --localaquí, porque no desea ver --globalni la --systemconfiguración. Y por último, porque puede pasarse por alto, solo funciona para submódulos, que ya están presentes en .git/config(como después git submodule init, vea la pregunta).
Tino
5

git configpermite especificar un archivo de configuración.
Y .gitmodules es un archivo de configuración.

Entonces, con la ayuda de " usar el espacio como delimitador con comando de corte ":

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

Eso enumerará solo las rutas, una por submódulo declarado.

Como Tino señala en los comentarios :

  • Esto falla para submódulos con espacios en él.
  • las rutas de submódulos pueden contener nuevas líneas , como en

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

Como una alternativa más robusta, Tino propone:

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

Para rutas con nuevas líneas en ellas (se pueden crear con git mv), deje de lado | tr '\0' '\n'y use algo como ... | while IFS='' read -d '' path; do ...para un mayor procesamiento con bash.
Esto necesita una fiesta moderna que comprenda read -d ''(no olvide el espacio entre -d and '').

VonC
fuente
hmmm ... "debe haber una mejor manera" son mis pensamientos inmediatos
arcseldon
Supongo que podría alias ese comando en bash profile, etc. Gracias de todos modos.
arcseldon
Esto falla para submódulos con espacios en él. También tenga en cuenta que las rutas pueden contener líneas nuevas ( git submodule add https://github.com/hilbix/bashy.git "sub module"; git mv 'sub module' $'sub\nmodule'). Vea mi comentario a la respuesta aceptada que es muy similar a la suya.
Tino
@Tino Buenos puntos. He incluido tu comentario en la respuesta para mayor visibilidad.
VonC
3

En mi versión de Git [1] , cada submódulo Git tiene a namey a path. No necesariamente tienen que ser iguales [2] . Obtener ambos de una manera confiable, sin consultar primero los submódulos ( git update --init), es un poco complicado de hechicería.

Obtenga una lista de submódulos names

No encontré una manera de lograr esto usando git configo cualquier otro gitcomando. Por lo tanto, volvemos a regex on .gitmodules(super feo). Pero parece ser algo seguro ya que gitlimita el posible espacio de código permitido para el submódulo names. Además, dado que probablemente desee utilizar esta lista para el procesamiento posterior del shell, la solución debajo de las entradas separadas con 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"

Y en tu guión:

#!/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 ''requiere bashy no funcionará con sh.

Obtenga una lista de submódulos paths

En mi enfoque Intento no para procesar la salida de git config --get-regexpla awk, tr, sed, ... sino que lo pase un byte cero separó de nuevo a git config --get. Esto es para evitar problemas con las nuevas líneas, espacios y otros caracteres especiales (por ejemplo, Unicode) en el submódulo paths. Además, dado que probablemente desee utilizar esta lista para el procesamiento posterior del shell, la solución debajo de las entradas separadas con NULL-bytes ( \0).

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

Por ejemplo, en una secuencia de comandos Bash, podría:

#!/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 ''requiere bashy no funcionará con sh.


Notas al pie

[1] Versión Git

$ git --version
git version 2.22.0

[2] Submódulo con divergencia nameypath

Configurar el repositorio de prueba:

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

Mueva el submódulo para crear namey 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

Pruebas

Configurar el repositorio de prueba:

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

Obtener 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

Obtener 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
fuente
0

Si no hay ningún .gitmodulesarchivo, pero existe una configuración de submódulos en .git/modules/:

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

Aquí hay otra forma de analizar los nombres de submódulos Git desde .gitmodules sin la necesidad de configuraciones sed o sofisticadas de 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
fuente
Además, esta solución parece manejar correctamente el ejemplo de comillas dobles escapadas, mientras que la solución anterior produce una salida escapada. Lo que puede no ser correcto en algunos casos. ;-)
devC0de
"arriba" y "abajo" no funcionan realmente en un orden de clasificación basado en la votación.
Ente