Git submodule : ajouter, mettre à jour et supprimer des sous-modules
Un git submodule est un autre dépôt imbriqué dans le vôtre et figé sur un commit précis. On ajoute un sous-module avec git submodule add <url> <chemin>, on le récupère au clonage avec git clone --recurse-submodules, on le met à jour avec git submodule update --remote et on le supprime avec git submodule deinit puis git rm.
Un git submodule (sous-module) est un dépôt Git indépendant, imbriqué dans un dossier de votre projet. Sa propriété essentielle : le dépôt parent ne stocke pas les fichiers du sous-module, mais une référence vers un commit précis de celui-ci. Ainsi, toute personne qui clone le projet obtient le sous-module dans exactement la même version — jusqu'à ce que vous la changiez explicitement.
On utilise les sous-modules pour intégrer une bibliothèque tierce sans gestionnaire de paquets, partager du code commun entre plusieurs projets, ou isoler un thème graphique ou un firmware dans un dépôt séparé avec son propre historique.
Comment fonctionnent les git submodules
Un sous-module se compose de trois éléments ; comprendre leur lien élimine la plupart des problèmes :
- Le fichier
.gitmodulesà la racine du projet — il est commité et indique quel chemin provient de quelle URL. - L'entrée gitlink dans l'arbre du commit — un élément spécial de mode
160000qui contient, au lieu d'un contenu, le hash du commit du sous-module. - Le dépôt du sous-module lui-même — ses données sont stockées dans
.git/modules/<nom>du parent, et le dossier du sous-module contient un fichier.gitqui pointe vers cet emplacement.
cat .gitmodules
[submodule "libs/ui-kit"]
path = libs/ui-kit
url = https://github.com/example/ui-kit.git
git ls-tree HEAD libs/
160000 commit 8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c libs/ui-kit
Notez bien : le parent fige un commit, pas une branche. C'est la différence fondamentale entre un sous-module git et une dépendance du type « dernière version de main ».
Comment ajouter un sous-module
git submodule add https://github.com/example/ui-kit.git libs/ui-kit
Cloning into '/home/dev/shop/libs/ui-kit'...
done.
git status
new file: .gitmodules
new file: libs/ui-kit
git commit -m "Ajouter ui-kit comme sous-module"
La commande clone le dépôt, crée ou complète .gitmodules et ajoute le gitlink à l'index. Il ne reste qu'à commiter. Si vous n'indiquez pas de chemin, le dossier porte le nom du dépôt.
Pour que le sous-module suive une branche précise lors des mises à jour, indiquez-la dès l'ajout : git submodule add -b develop <url> <chemin>. La branche est inscrite dans .gitmodules sous la forme branch = develop.
URL relative
Si le sous-module est hébergé sur le même serveur, vous pouvez indiquer un chemin relatif : git submodule add ../ui-kit.git libs/ui-kit. Il est résolu par rapport à l'URL du dépôt distant du parent, donc il fonctionne aussi bien en SSH qu'en HTTPS — selon la façon dont le projet a été cloné.
Comment cloner un projet avec ses sous-modules
La plainte la plus fréquente : « j'ai cloné, mais le dossier du sous-module est vide ». Par défaut, git clone ne télécharge pas les sous-modules. La bonne méthode :
git clone --recurse-submodules https://github.com/example/shop.git
Si le projet a déjà été cloné sans les sous-modules :
git submodule update --init --recursive
--init enregistre les sous-modules de .gitmodules dans le .git/config local, update les télécharge et les place sur le commit figé, --recursive fait de même pour les sous-modules imbriqués dans des sous-modules.
Comment mettre à jour un sous-module
Il faut distinguer deux besoins : « récupérer la version que les collègues ont commitée » et « passer à une nouvelle version de la bibliothèque ».
Récupérer la version figée dans le projet
Après un git pull dans le parent, le gitlink a pu changer, mais les fichiers du sous-module ne se mettent pas à jour tout seuls — git status affiche modified: libs/ui-kit (new commits). Synchronisez :
git pull
git submodule update --init --recursive
Pour éviter de le faire à la main, activez l'automatisme : git config submodule.recurse true. Dès lors, pull, checkout et switch mettront à jour les sous-modules eux-mêmes. Pour un usage ponctuel, il existe git pull --recurse-submodules.
Passer à une nouvelle version de la bibliothèque
# Option 1 : récupérer le dernier commit de la branche suivie
git submodule update --remote libs/ui-kit
# Option 2 : choisir la version à la main
cd libs/ui-kit
git fetch
git checkout v3.2.0
cd ../..
# Dans les deux cas — figer la nouvelle version dans le parent
git add libs/ui-kit
git commit -m "Mettre à jour ui-kit vers v3.2.0"
--remote prend la branche indiquée par le paramètre branch de .gitmodules, ou à défaut la branche pointée par le HEAD du dépôt distant du sous-module (en général main). Sans ce dernier commit dans le parent, vos collègues n'auront pas la nouvelle version : mettre à jour un sous-module, c'est toujours modifier le gitlink, et cela doit être commité.
Pour voir ce qui a changé dans les sous-modules :
git diff --submodule=log
git config diff.submodule log # en faire le format par défaut
git submodule status
8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c libs/ui-kit (v3.1.0)
+1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b libs/charts (v1.4.0-3-g1a2b3c4)
Le préfixe dans git submodule status : - — sous-module non initialisé, + — son commit actuel diffère de celui figé dans le parent, U — conflit de fusion.
Comment modifier le code d'un sous-module
Après git submodule update, le sous-module est en detached HEAD — sur un commit précis, pas sur une branche. Si vous commencez à commiter ainsi, vous risquez de perdre vos commits au prochain update. La bonne marche à suivre :
cd libs/ui-kit
git switch main # se placer sur une branche
# ... modifications ...
git commit -am "Corriger les marges du bouton"
git push # on pousse d'abord le sous-module !
cd ../..
git add libs/ui-kit
git commit -m "ui-kit : marges du bouton corrigées"
git push
L'ordre des push est crucial. Si vous poussez le parent avant le sous-module, le projet sur le serveur référencera un commit que personne d'autre que vous ne peut télécharger, et chez vos collègues git submodule update échouera avec fatal: remote error: upload-pack: not our ref ou did not contain. Pour vous protéger, utilisez git push --recurse-submodules=check (refuse le push si le sous-module n'a pas été envoyé) ou =on-demand (envoie d'abord les sous-modules automatiquement).
Comment supprimer un sous-module
Autrefois, la suppression exigeait de modifier plusieurs fichiers à la main. Avec les versions récentes de Git, trois étapes suffisent :
git submodule deinit -f libs/ui-kit # retirer de .git/config et vider le dossier
git rm -f libs/ui-kit # retirer le gitlink et l'entrée de .gitmodules
rm -rf .git/modules/libs/ui-kit # supprimer les données conservées du sous-module
git commit -m "Supprimer le sous-module ui-kit"
La dernière étape n'est pas indispensable au bon fonctionnement, mais sans elle, si vous ajoutez de nouveau un sous-module au même chemin, Git peut se plaindre qu'un dossier local existe déjà (A git directory for 'libs/ui-kit' is found locally).
Si l'URL du sous-module a changé
Le dépôt a déménagé ? Corrigez .gitmodules et synchronisez la configuration locale :
git submodule set-url libs/ui-kit https://gitlab.com/example/ui-kit.git
git submodule sync --recursive
git commit -am "Nouvelle adresse pour ui-kit"
Vos collègues doivent eux aussi lancer git submodule sync après le pull : l'URL de .gitmodules n'est copiée dans leur .git/config qu'au moment d'un init ou d'un sync.
Commandes utiles pour plusieurs sous-modules
| Besoin | Commande |
|---|---|
| Liste et état | git submodule status --recursive |
| Exécuter une commande dans chacun | git submodule foreach 'git switch main && git pull' |
| Accélérer le téléchargement | git submodule update --init --jobs 4 |
| Télécharger sans l'historique | git submodule update --init --depth 1 |
| Résumé court dans status | git config status.submoduleSummary true |
Problèmes courants avec les sous-modules
- Dossier vide après le clonage — l'init n'a pas été fait. Solution :
git submodule update --init --recursive. - « modified: (new commits) » juste après un pull — le parent pointe vers un autre commit, mais les fichiers sont anciens. Solution :
git submodule update. - Sous-module ramené en arrière par accident — vous avez fait
git add .alors que le sous-module était sur un ancien commit, et vous avez commité un retour de version. Vérifiez toujoursgit diff --submoduleavant de commiter. - Conflit de gitlink lors d'un merge — dans deux branches, le sous-module a été mis à jour vers des commits différents. Entrez dans le sous-module, choisissez le bon commit (souvent le plus récent, qui contient les deux changements), puis
git add cheminet terminez le merge. - Sous-module privé en CI — le build ne peut pas télécharger le sous-module sans accès. Il faut fournir à la CI une clé ou un jeton pour le dépôt du sous-module ; les URL relatives simplifient la configuration.
Sous-modules en équipe : règles communes
La plupart des problèmes de sous-modules ne viennent pas de Git, mais du fait que chacun s'y prend différemment. Quelques règles à inscrire dans le README du projet :
- Toujours cloner avec
--recurse-submoduleset activersubmodule.recurse truechez tout le monde. - Mettre à jour un sous-module dans un commit dédié, avec un message clair : quelle version et pourquoi.
- Ne pas modifier le code du sous-module depuis le projet parent sans commit et push séparés dans le sous-module lui-même.
- En CI, exécuter explicitement
git submodule update --init --recursiveet vérifier que le build ne dépend d'aucune modification locale.
Pour vérifier en CI que tous les commits de sous-modules figés sont bien accessibles, un clone propre suffit : si quelqu'un a oublié de pousser un sous-module, le build échouera à l'étape update, et pas chez un collègue une semaine plus tard.
git clone --recurse-submodules --jobs 4 "$REPO_URL" build
cd build
git submodule status --recursive
Submodule ou alternatives
Les sous-modules offrent un contrôle précis de la version et des historiques séparés, mais ajoutent des étapes manuelles pour chaque participant. Si le code est une bibliothèque classique, un gestionnaire de paquets est plus simple (npm, pip, Composer, Go modules). Si vous voulez que le code externe soit simplement présent dans le projet et se clone sans commande supplémentaire, envisagez git subtree : il copie l'historique dans le dépôt principal, et vos collègues n'ont rien à savoir. Pour un grand nombre de services liés, les équipes choisissent souvent un monorepo. Un sous-module se justifie quand le code imbriqué a sa propre vie, ses propres releases et sa propre équipe.
En résumé
Un git submodule est une référence vers un commit précis d'un autre dépôt. Ajout : git submodule add ; récupération : --recurse-submodules ou update --init ; passage à une nouvelle version : update --remote plus un commit dans le parent ; suppression : deinit, git rm et nettoyage de .git/modules. Gardez en tête le detached HEAD dans le sous-module et l'ordre des push. Les outils avancés de Git sont mis en pratique dans le cours Git.
Questions fréquentes
Pourquoi un sous-module est-il toujours en detached HEAD ?
Le parent stocke le hash d'un commit et non une branche, et git submodule update place le sous-module exactement sur ce commit. Pour modifier le code, basculez à la main sur une branche ou utilisez git submodule update --remote --merge.
Peut-on figer un sous-module sur un tag ?
Pas directement, mais vous pouvez passer sur le tag dans le sous-module avec git checkout et commiter le commit obtenu dans le parent ; git submodule status affichera le tag entre parenthèses.
GitHub inclut-il les sous-modules dans l'archive ZIP ?
Non, les archives de sources de GitHub n'incluent pas le contenu des sous-modules, les dossiers seront vides. Utilisez git clone --recurse-submodules.
Quelle différence entre git submodule et git subtree ?
Submodule stocke une référence vers un commit d'un dépôt externe et demande des commandes à part pour récupérer le code, alors que subtree copie les fichiers et l'historique directement dans le dépôt principal.
Peut-on déplacer un sous-module dans un autre dossier ?
Oui, avec git mv ancien/chemin nouveau/chemin : Git met à jour .gitmodules et les références internes, il ne reste qu'à commiter.
Ce sujet est traité en détail avec de la pratique dans le cours « Git de zéro à développeur confirmé ».
Voir le cours