Git Submodule
Introduction aux sous-modules Git : comment les ajouter, cloner, mettre à jour, pousser et supprimer, avec tous les cas d'usage essentiels.
Un sous-module permet d'intégrer un dépôt Git à l'intérieur d'un autre tout en gardant leurs historiques complètement séparés. Cette page explique ce qu'est un sous-module, quand il est l'outil approprié, et le cycle de vie complet des commandes : ajout, clonage, récupération, mise à jour, envoi et suppression d'un sous-module, ainsi que les pièges courants qui rendent les sous-modules délicats.
Qu'est-ce qu'un sous-module
Très souvent, un dépôt de code dépend de code externe provenant d'autres dépôts. Vous pouvez copier-coller directement le code externe dans le dépôt principal, ou utiliser le gestionnaire de paquets du langage. Cependant, ces deux méthodes ont l'inconvénient de ne pas suivre les modifications du dépôt externe.
Git vous permet d'inclure d'autres dépôts Git — appelés sous-modules — à l'intérieur d'un seul dépôt. Un sous-module réside à un chemin spécifique dans le répertoire de travail du dépôt parent et est lui-même un clone complet d'un autre dépôt avec son propre historique .git.
L'idée clé est qu'un sous-module est épinglé à un commit exact, et non à une branche ou à un tag. Le dépôt parent ne stocke que le chemin du sous-module, son URL et le SHA du commit attendu. C'est ce qui vous permet de dépendre de code externe à un point précis et reproductible dans le temps.
Deux fichiers tracent cette relation :
.gitmodules— un fichier suivi à la racine du dépôt parent. Il associe le chemin de chaque sous-module à son URL distante (et optionnellement une branche). Ce fichier est commité et partagé avec tout le monde..git/configet l'entrée gitlink dans l'arbre — un registre local, propre à chaque clone, qui enregistre le commit réellement extrait (le gitlink apparaît avec le mode160000).
Les sous-modules prennent en charge l'ajout, la synchronisation, la mise à jour et le clonage, mais comme le parent ne mémorise qu'un SHA de commit, la mise à jour d'un sous-module est toujours un acte délibéré en deux étapes — jamais automatique.
Quand utiliser les sous-modules
Travailler avec des sous-modules est délicat ; voici quelques cas d'usage recommandés.
- Si le sous-projet évolue trop vite ou si les changements à venir risquent de casser l'API, verrouillez le code sur un commit spécifique par sécurité.
- Si un composant n'est pas mis à jour très souvent et que vous souhaitez le suivre comme dépendance fournisseur.
- Si vous représentez une partie du projet pour un tiers et souhaitez intégrer son travail à un moment précis (fonctionne uniquement lorsque les mises à jour ne sont pas trop fréquentes).
- Si le contexte technologique permet l'empaquetage et la gestion formelle des dépendances, préférez les gestionnaires de paquets aux sous-modules.
- Si votre base de code est massive et que vous ne voulez pas la récupérer entièrement à chaque fois, utilisez des sous-modules pour que les collaborateurs ne téléchargent que les parties dont ils ont besoin.
Ajouter un sous-module
Tout d'abord, créez (ou placez-vous dans) le dépôt qui contiendra le sous-module :
mkdir git-submodule-demo
cd git-submodule-demo/
git initInitialized empty Git repository in /Users/example/git-submodule-demo/.git/Ajoutez un sous-module avec git submodule add, en passant l'URL du dépôt à intégrer :
git submodule add https://somehost/example/textexampleCloning into '/Users/example/git-submodule-demo/textexample'...
remote: Counting objects: 8, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 8 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (8/8), done.Git clone immédiatement le dépôt textexample dans un dossier du même nom et crée un fichier .gitmodules. Pour placer le sous-module à un chemin différent, ajoutez-le comme dernier argument, par exemple git submodule add <url> vendor/textexample.
Vérifiez maintenant l'état du dépôt avec git status :
git statusOn branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: .gitmodules
new file: textexampleNotez que textexample est indexé comme une seule entrée, et non comme les fichiers individuels qu'il contient. Le dépôt parent ne suit que le pointeur de commit du sous-module. Commitez les deux fichiers avec git add et git commit :
git add .gitmodules textexample
git commit -m "Add textexample submodule"[master (root-commit) d5002d0] Add textexample submodule
2 files changed, 4 insertions(+)
create mode 100644 .gitmodules
create mode 160000 textexampleLe mode 160000 est spécial : il indique que textexample est un gitlink (un pointeur vers un commit) et non un répertoire ordinaire.
Vérifier l'état des sous-modules
Avant de modifier quoi que ce soit, vérifiez où en est chaque sous-module :
git submodule status a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0 textexample (v1.2.0)Le caractère en début de ligne est significatif : un espace indique que le sous-module est au commit attendu, un + indique qu'il est extrait sur un commit différent de celui enregistré par le parent, et un - indique qu'il n'est pas encore initialisé.
Mettre à jour les sous-modules
Les membres de l'équipe doivent mettre à jour le code du sous-module lorsqu'il a été modifié ailleurs. Vous ne pouvez pas vous fier à git pull seul, car tirer le dépôt parent ne modifie que le commit du sous-module enregistré — il ne touche pas les fichiers extraits à l'intérieur du sous-module. Pour extraire le commit attendu par le parent, exécutez :
git submodule updateSans l'option --remote, cette commande extrait le commit enregistré dans le dépôt parent et ne récupère pas les nouveaux changements en amont.
Pour récupérer à la place le dernier commit de la branche suivie par le sous-module (main par défaut, ou la branch définie dans .gitmodules), utilisez --remote :
git submodule update --remote textexampleCette commande récupère les modifications en amont du sous-module et le fait avancer. Le dépôt parent voit maintenant un nouveau pointeur de commit ; vous devez donc effectuer un git add et commiter le sous-module pour enregistrer le changement.
Pour effectuer une mise à jour de tous les sous-modules, y compris les sous-modules imbriqués, ajoutez --init --recursive :
git submodule update --init --recursiveSi le fichier .gitmodules change (par exemple si l'URL du sous-module est déplacée), exécutez git submodule sync pour copier les nouvelles URL dans votre .git/config local avant de mettre à jour :
git submodule sync --recursiveCloner des sous-modules Git
Pour cloner un projet avec des sous-modules, utilisez la commande git clone. Par défaut, elle clone le dépôt parent mais laisse les répertoires des sous-modules vides. Vous devez ensuite exécuter git submodule init et git submodule update. Le premier met à jour le .git/config local avec les correspondances du fichier .gitmodules, tandis que le second récupère les données du sous-module et extrait le commit enregistré.
Si vous avez cloné sans --recurse-submodules, peuplez les sous-modules ensuite :
git clone /url/to/repo/with/submodules
git submodule init
git submodule updateLe raccourci git submodule update --init combine ces deux dernières commandes. Mieux encore, clonez tout en une seule étape avec --recurse-submodules :
git clone --recurse-submodules /url/to/repo/with/submodulesCette commande clone le parent et initialise et extrait automatiquement chaque sous-module, de sorte que l'arbre de travail est immédiatement complet.
Récupérer le code du sous-module
Lorsque vous tirez un dépôt parent qui a acquis de nouveaux sous-modules, ceux-ci arrivent non initialisés. Récupérez d'abord le dernier état du parent :
git pullSi de nouveaux sous-modules sont listés, initialisez-les et récupérez-les en une seule étape :
git submodule update --init --recursivegit submodule init seul ne fait que copier les correspondances dans le .git/config local ; il ne télécharge aucun code. C'est l'étape update qui récupère réellement le sous-module et extrait le commit enregistré. Vous pouvez faire en sorte que git pull fasse cela automatiquement en définissant :
git config submodule.recurse truePousser des modifications dans un sous-module
Un sous-module est un dépôt Git complet et indépendant ; vous commitez et poussez donc à l'intérieur de son répertoire exactement comme vous le feriez ailleurs :
cd textexample
git checkout main
# ...edit files...
git commit -am "Fix typo in textexample"
git push
cd ..Après avoir commité dans le sous-module, le dépôt parent pointe toujours vers l'ancien commit. L'exécution de git status dans le parent affiche alors :
modified: textexample (new commits)Enregistrez le nouveau pointeur en indexant et en commitant le chemin du sous-module dans le parent, puis en poussant :
git add textexample
git commit -m "Bump textexample to latest"
git pushPour éviter de pousser le parent avant que ses commits de sous-module n'existent sur le dépôt distant (ce qui laisserait les coéquipiers avec un pointeur cassé et inaccessible), poussez tout ensemble :
git push --recurse-submodules=on-demandCette commande pousse d'abord les commits de sous-module non encore envoyés, puis le parent.
Supprimer un sous-module
Supprimer le dossier manuellement ne suffit pas — Git conserve son registre interne. Supprimez un sous-module proprement avec :
git submodule deinit -f textexample
git rm textexample
git commit -m "Remove textexample submodule"deinit désenregistre le sous-module et supprime son arbre de travail, git rm supprime le gitlink et son entrée dans .gitmodules, et le commit enregistre la suppression.
Résumé
Les sous-modules sont un bon moyen de conserver des projets dans des dépôts séparés tout en les référençant comme des dossiers dans le répertoire de travail d'un autre dépôt. Le modèle mental à garder est simple : le parent stocke un pointeur de commit, chaque mise à jour est délibérée, et --recurse-submodules vous évite les clones à moitié peuplés. Pour les dépendances qui changent fréquemment, un vrai gestionnaire de paquets est généralement le meilleur choix. Pour approfondir les flux de travail associés, consultez git clone, git pull et git status.