Lister les sous-modules dans un référentiel Git


246

J'ai un référentiel Git qui contient plusieurs sous-modules. Comment répertorier les noms de tous les sous-modules après leur git submodule initexécution?

La git submodule foreachcommande pourrait faire écho aux noms du sous-module, mais cela ne fonctionne qu'une fois qu'ils ont été extraits, ce qui ne s'est pas produit après l'étape d'initialisation. Il y a plus d'étapes dans la chaîne qui doivent se produire avant de pouvoir les extraire, et je ne veux pas avoir à câbler les noms des sous-modules dans le script.

Existe-t-il une commande Git pour obtenir les noms de tous les sous-modules actuellement enregistrés, mais pas encore extraits?


4
Super idiot que ce n'est pas le cas, mais la réponse de Sandeep montre que git submodulese comporte comme je m'attendais à ce qu'un hypothétique git submodule listse comporte - je n'ai jamais pensé à vérifier ce qui se passait sans argument git submodule. (Heureux d'avoir testé ce lien, car j'ai pris le mauvais lien 'Partager' au départ!)
sage

4
Je suis venu ici parce qu'il git submodule listn'existait pas et git submodule helpn'a pas aidé (selon ce dernier, la solution git submodule, n'est pas une utilisation valide).
user2394284

La plupart des réponses (y compris celle acceptée) répertorient les sous paths- modules , pas nameset se cassent lorsqu'ils contiennent des caractères spéciaux. J'ai essayé de donner une réponse pour les noms et les chemins qui devraient être sûrs: stackoverflow.com/a/56912913/3215929
Ente

Réponses:


175

Vous pouvez utiliser le même mécanisme que git submodule initlui-même, à savoir regarder .gitmodules. Ce fichier énumère chaque chemin de sous-module et l'URL à laquelle il se réfère.

Par exemple, à partir de la racine du référentiel, cat .gitmodulesimprimera le contenu à l'écran (en supposant que vous l'ayez cat).

Les fichiers .gitmodule ayant le format de configuration Git, vous pouvez utiliser git config pour analyser ces fichiers:

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

Vous montrerait toutes les entrées de sous-module, et avec

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

vous obtiendrez uniquement le chemin du sous-module lui-même.


Mais il n'imprime rien sur la console.
IgorGanapolsky

2
@IgorGanapolsky - vous pouvez imprimer sur la console, si vous le faites cat .gitmodulesà la racine du référentiel ...
sdaau

La awkpièce échoue si vous avez des espaces sur le chemin du sous-module.
maikel

@Ikke Je voudrais récupérer une liste de sous-modules comprenant le nom, le chemin et l'URL. J'ai essayé la commande Git suivante, mais elle ne renvoie rien: git config --file = .gitmodules --get-regexp. *? Submodule (. *)] Path = (. *) Url = (. *) I essayé git config --file = .gitmodules --get-regexp. *? (sous-module). *? (chemin). *? (url) également. Vous avez peut-être une solution. Merci d'avance.
Odrai

8
ATTENTION: awkéchoue pour les sous-modules avec des espaces! La commande devrait lire git config -z --file .gitmodules --get-regexp '\.path$' | sed -nz 's/^[^\n]*\n//p' | tr '\0' '\n'(vous avez besoin d'une version moderne sedavec -z). Cela échoue pour les chemins contenant des retours à la ligne (ils peuvent être créés avec git mv). Si vous voulez vous protéger contre ces derniers, laissez de côté le | tr '\0' '\n'et utilisez quelque chose comme ... | while IFS='' read -d '' path; do ...pour un traitement ultérieur avec bash. Cela nécessite un bash moderne qui comprend read -d ''(n'oubliez pas l'espace entre -det '').
Tino

109

Vous pouvez utiliser git submodule statusou facultativement git submodule status --recursivesi vous souhaitez afficher les sous-modules imbriqués.

Depuis la documentation de Git:

Affiche l'état des sous-modules. Cela affichera le SHA-1 de la validation actuellement extraite pour chaque sous-module, ainsi que le chemin du sous-module et la sortie de git pour le SHA-1. Chaque SHA-1 sera précédé de - si le sous-module n'est pas initialisé, + si la validation du sous-module actuellement extrait ne correspond pas au SHA-1 trouvé dans l'index du référentiel contenant et U si le sous-module a des conflits de fusion.


2
Il suffit d'exécuter git submodule update --init --recursivepour initialiser tous les sous-modules.
Jon Koops

2
@IgorGanapolsky vient de lire le commentaire de Stefaan: ce type de commandes ne fonctionne pas si les sous-modules ne sont pas encore initialisés . Pas de sortie = pas de sous-module (liste vide).
Tim

6
Cela devrait certainement être marqué comme la meilleure réponse pour utiliser une commande simple et native gitsans aucune aide de bash. Et cette solution est élégante dans la mesure où elle fonctionne bien à tout moment de la copie de travail (mais les sous-modules eux-mêmes bien sûr, pour lesquels le résultat s'applique directement à eux-mêmes).
Tim

Comme le dit la réponse de Sandeep Gill , git submodulesans argument est le même que git submodule status, donc vous pouvez vous épargner en tapant 7 caractères ;-)
Sam

@Sam Ils ne sont plus tout à fait les mêmes depuis qu'ils git submodule status --recursivefonctionnent, mais ce git submodule --recursiven'est pas le cas. Ces 7 personnages t'achètent quelque chose. Cela dépend de vos besoins.
Kevin Rak

60

Pour renvoyer uniquement les noms des sous-modules enregistrés, vous pouvez utiliser cette commande:

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

Pensez-y comme git submodule --listqui n'existe pas.


3
Au lieu de cela, utilisez perl -ne '/^\s*path =\s*(.*)/ and push(@submods, $1); END { print(join("\n", sort(@submods)));}' "$(git rev-parse --show-toplevel)/.gitmodules"ce qui, comparé à cette réponse (1), fonctionne à partir de n'importe quel sous-répertoire (mais pas à l'intérieur d'un sous-module); (2) trie les sous-modules par nom; et (3) ignore les lignes commentées dans .gitmodules.
Colin D Bennett

6
Et c'est mémorable pour démarrer!
Dan

1
Notez que c'est une façon très bâclée de le faire, comme cela pathpeut être présent dans le nom du sous-module ( git submodule add https://github.com/commercialhaskell/path.git). Mais vous le saviez probablement déjà auparavant. Si vous souhaitez accéder .gitconfigà n'importe où dans un arbre de travail ou devez l'exécuter dans un --bareréférentiel, vous pouvez utiliser quelque chose comme git cat-file -p HEAD:.gitmodules | .... Si vous devez vous référer au fichier "intermédiaire", vous pouvez le faire git cat-file -p :.gitmodules | ..., mais cela nécessite la indexprésence d' un git .
Tino

56

La commande suivante répertorie les sous-modules:

git submodule--helper list

La sortie est quelque chose comme ceci:

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

Remarque: il nécessite Git 2.7.0 ou supérieur.


1
Génial!. Où cette commande est-elle documentée? Fonctionne sur Git pour Windwos mais ne trouve aucun document sur git-scm.com
DarVar

1
git ls-files --stage | grep ^160000(de Answer stackoverflow.com/a/29325219 ) semble produire le même résultat, donc c'est peut-être un bon remplacement si vous devez être compatible avec des gits plus anciens.
Tino

Faites attention aux --doubles tirets danssubmodule--helper
it3xl

25

Utilisation:

$ git submodule

Il répertoriera tous les sous-modules dans le référentiel Git spécifié.


3
Il s'agit en fait d'une réponse en double des deux autres réponses qui mentionnent git submodule [status](notez que cela statusest implicite si omis, c'est donc la même chose).
Colin D Bennett

Ça ne marche pas toujours. fatal: no submodule mapping found in .gitmodules for path 'bla-bla/foo-bar'. Vous devez d'abord supprimer tous les référentiels internes qui ne sont pas encore sous-modulés.
it3xl

1
Meilleure réponse (• ̀ ω • ́) ✧
Răzvan Flavius ​​Panda

Notez que si vous souhaitez utiliser l' --recursiveindicateur, vous devez ajouter explicitement la statuscommande: git submodule status --recursivefonctionne, mais git submodule --recursivene fonctionne pas.
Sam

17

J'utilise ceci:

git config --list|egrep ^submodule

1
@IgorGanapolsky vient de lire le commentaire de Stefaan: cela ne fonctionne pas si les sous-modules ne sont pas encore initialisés . Pas de sortie = pas de sous-module (liste vide).
Tim

C'est exactement ce dont j'avais besoin. Il montre les sous-dossiers des sous-modules et les URL de leurs télécommandes.
thinsoldier

17

J'ai remarqué que la commande fournie dans une réponse à cette question m'a donné les informations que je cherchais:

Aucun mappage de sous-module trouvé dans .gitmodule pour un chemin qui n'est pas un sous-module

git ls-files --stage | grep 160000

Cette réponse est agréable et propre, y a-t-il des inconvénients par rapport à la réponse de mholm815 en analysant les .gitmodules ?
Colin D Bennett

BTW, si vous voulez juste les noms des sous-modules, utilisezgit ls-files --stage | grep 160000 | perl -ne 'chomp;split;print "$_[3]\n"'
Colin D Bennett

Cela a fonctionné pour moi car je n'avais rien .gimodulesou rien .git/config. (Je ne sais pas comment cela s'est produit, j'ai obtenu le référentiel dans cet état. C'était une erreur, alors la solution était git rm.)
wenzeslaus

1
Cela fonctionne avec des dépôts nus. Contrairement à la solution .gitmodules.
kupson

1
C'est la meilleure réponse. Juste un petit peu: grep "^160000 "serait légèrement plus robuste.
Lassi

12

Si cela ne vous dérange pas d'opérer uniquement sur des sous-modules initialisés, vous pouvez utiliser git submodule foreachpour éviter l'analyse de texte.

git submodule foreach --quiet 'echo $name'

1
Cette réponse est unique en ce qu'elle ne fonctionne que pour les sous-modules qui ont été initialisés. Selon ce dont vous avez besoin, cela peut être le comportement souhaité (mais cela peut aussi être inattendu et indésirable.)
Seth Johnson

9

Vous pouvez utiliser:

git submodule | awk '{ print $2 }'

1
Cela semble produire la même sortie que stackoverflow.com/a/23490756/895245 mais est plus court.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Voté. Cette commande est dangereuse, car elle échoue pour un sous-module iE nommé test (master)(nom avec un espace suivi de parenthèses), qui est un nom de sous-module valide . La commande ne peut pas non plus être corrigée, car git imprime moduleou module (branch). Et c'est encore pire, car cette commande n'est pas porcellaine, donc la sortie de la commande peut changer à l'avenir sans préavis.
Tino

7

J'utilise celui-ci:

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

Sortie (chemin + version):

tools/deploy_utils (0.2.4)

L'inconvénient que je vois de cette version est qu'elle est lente. Il semble faire une sorte de vérification d'état sur chaque sous-module, peut-être près d'une seconde par sous-module sur ma machine, donc sur les projets avec de nombreux sous-modules, ce n'est pas une bonne solution, ou s'il doit être exécuté à partir d'un script, etc.
Colin D Bennett

7

Pour répertorier tous les sous-modules par nom:

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



5

Juste les chemins du sous - module s'il vous plaît, madame ...

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

👍🏼


4
Bien que ce soit probablement une réponse tout à fait valable à la question, voici quelques mises en garde: cela ne fonctionne que pour les sous-modules qui ne contiennent pas de points en leur nom. Un point est complètement valide dans les noms de module. Il ne se limite pas aux .urlclés, donc d'autres entrées peuvent également apparaître (généralement il n'y en a pas, mais la merde se produit). Vous devez utiliser --localici, car vous ne voulez pas voir --globalet les --systemparamètres. Enfin, car il peut être ignoré, il ne fonctionne que pour les sous-modules qui sont déjà présents dans .git/config(comme après git submodule init, voir question).
Tino

5

git configpermet de spécifier un fichier de configuration.
Et .gitmodules est un fichier de configuration.

Ainsi, à l'aide de " utiliser l'espace comme délimiteur avec la commande cut ":

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

Cela ne listera que les chemins, un par sous-module déclaré.

Comme le souligne Tino dans les commentaires :

  • Cela échoue pour les sous-modules contenant des espaces .
  • les chemins de sous-module peuvent contenir des sauts de ligne , comme dans

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

Comme alternative plus robuste, Tino propose:

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

Pour les chemins contenant des retours à la ligne (ils peuvent être créés avec git mv), laissez le | tr '\0' '\n'et utilisez quelque chose comme ... | while IFS='' read -d '' path; do ...pour un traitement ultérieur avec bash.
Cela nécessite un bash moderne qui comprend read -d ''(n'oubliez pas l'espace entre les deux -d and '').


hmmm ... "il doit y avoir une meilleure façon" sont mes pensées immédiates
arcseldon

suppose que je pourrais alias cette commande dans le profil bash etc. merci de toute façon.
arcseldon

Cela échoue pour les sous-modules contenant des espaces. Notez également que les chemins peuvent contenir des sauts de ligne ( git submodule add https://github.com/hilbix/bashy.git "sub module"; git mv 'sub module' $'sub\nmodule'). Voir mon commentaire sur la réponse acceptée qui est très similaire à la vôtre.
Tino

@Tino Bons points. J'ai inclus votre commentaire dans la réponse pour plus de visibilité.
VonC

3

Dans ma version de Git [1] , chaque sous-module Git a un nameet un path. Ils ne doivent pas nécessairement être les mêmes [2] . Obtenir les deux de manière fiable, sans vérifier d'abord les sous-modules ( git update --init), est un peu délicat de la magie du shell.

Obtenez une liste des sous-modules names

Je n'ai pas trouvé de moyen pour y parvenir en utilisant git configou toute autre gitcommande. Par conséquent, nous sommes de retour à regex .gitmodules(super laid). Mais il semble être quelque peu sûr car gitlimite l'espace de code possible autorisé pour le sous-module names. De plus, comme vous souhaitez probablement utiliser cette liste pour un traitement ultérieur du shell, la solution ci-dessous sépare les entrées avec 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"

Et dans votre 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"
)

Remarque : read -rd ''nécessite bashet ne fonctionnera pas avec sh.

Obtenez une liste des sous-modules paths

Dans mon approche , j'essaie pas de traiter la sortie git config --get-regexpavec awk, tr, sed, ... mais passe plutôt un zéro octet séparé retour à git config --get. C'est pour éviter les problèmes avec les nouvelles lignes, les espaces et autres caractères spéciaux (par exemple Unicode) dans le sous-module paths. De plus, comme vous souhaitez probablement utiliser cette liste pour un traitement ultérieur du shell, la solution ci-dessous sépare les entrées avec NULL-bytes ( \0).

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

Par exemple, dans un script Bash, vous pouvez alors:

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

Remarque : read -rd ''nécessite bashet ne fonctionnera pas avec sh.


Notes de bas de page

[1] Version Git

$ git --version
git version 2.22.0

[2] Sous-module avec divergence nameetpath

Configurer le référentiel de test:

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

Déplacer le sous-module pour faire nameet pathdiverger:

$ 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

Essai

Configurer le référentiel de test:

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

Obtenir la liste des sous-modules 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

Obtenir la liste des sous-modules 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

S'il n'y a pas de .gitmodulesfichier, mais une configuration de sous-modules existe dans .git/modules/:

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

0

Voici une autre façon d'analyser les noms de sous-modules Git à partir de .gitmodules sans avoir besoin de paramètres IFS sed ou fantaisie. :-)

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

De plus, cette solution semble gérer correctement l'exemple de guillemets doubles échappés tandis que la solution ci-dessus produit une sortie d'échappement. Ce qui peut ne pas être correct dans certains cas. ;-)
devC0de

"ci-dessus" et "ci-dessous" ne fonctionne pas vraiment dans un ordre de tri basé sur le vote.
Ente
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.