git subtree fusionne un dépôt externe dans un sous-dossier de votre
projet, sans métadonnées séparées. Contrairement aux submodules, les
collaborateurs n'ont rien de spécial à faire au clone, le code est
directement dans le repo. Ce guide couvre l'ajout, la mise à jour, la
contribution en retour et le comparatif avec les submodules.
Prérequis : Submodules et Remotes fondamentaux.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Ajouter un dépôt externe avec
git subtree addsans fichier de configuration - Mettre à jour et contribuer des modifications vers le dépôt source
- Comprendre les différences entre subtree et submodule
- Choisir la stratégie adaptée selon votre organisation et vos équipes
Pourquoi subtree plutôt que submodule ?
Section intitulée « Pourquoi subtree plutôt que submodule ? »Les submodules demandent à chaque collaborateur de connaître les
commandes submodule. Un subtree est transparent :
- Le code est dans le repo, pas de référence externe
git clonerécupère tout, rien à initialiser- Pas de
.gitmodules, pas de HEAD détaché - Les outils CI/CD fonctionnent sans configuration spéciale
Le compromis : l'historique est mélangé et les mises à jour sont plus verbeuses.
Ajouter un dépôt avec subtree
Section intitulée « Ajouter un dépôt avec subtree »L'opération se fait en deux temps : déclarer le dépôt source comme remote,
puis demander à Git de greffer sa branche dans un sous-dossier. Le remote n'est
pas strictement obligatoire, git subtree add accepte une URL complète, mais il
évite de retaper l'adresse à chaque mise à jour et rend les commandes suivantes
beaucoup plus courtes.
# Ajouter le remote pour simplifier les futures commandesgit remote add lib-utils https://github.com/org/lib-utils.gitgit fetch lib-utils
# Intégrer dans un sous-dossiergit subtree add --prefix=libs/utils lib-utils main --squash--prefix=libs/utils: dossier de destinationlib-utils main: remote et branche source--squash: compresse l'historique en un seul commit (recommandé pour garder un historique lisible)
La commande affiche Added dir 'libs/utils' et pose deux commits dans votre
historique. Attention à la lecture de git log --oneline, qui trie du plus
récent au plus ancien : le commit de merge arrive en tête, tandis que le
commit Squashed se retrouve plus bas car il porte la date du dépôt source,
pas celle de votre intégration.
2057758 Merge commit '5e5ab16f05587744d24ee8c2fd3c187f99b45039' as 'libs/utils'dbf1bc3 chore: init5e5ab16 Squashed 'libs/utils/' content from commit a4afd38Mettre à jour depuis l'amont
Section intitulée « Mettre à jour depuis l'amont »Le dépôt source continue de vivre de son côté. Pour récupérer ses nouveautés,
vous rejouez un subtree pull sur le même préfixe. Il n'y a pas de pointeur
de version à faire avancer comme avec un submodule : Git fusionne réellement les
fichiers amont dans votre arbre de travail, donc un conflit se résout ici
avec les outils de merge habituels.
git fetch lib-utilsgit subtree pull --prefix=libs/utils lib-utils main --squashGit fusionne les nouveautés de lib-utils/main dans votre
sous-dossier. Avec --squash, un seul commit de merge apparaît dans
votre historique.
Contribuer en retour (push)
Section intitulée « Contribuer en retour (push) »Vous avez modifié du code dans libs/utils/ et voulez renvoyer les
changements au dépôt d'origine :
git subtree push --prefix=libs/utils lib-utils fix/correctionGit extrait les commits qui touchent libs/utils/, réécrit les chemins
et les pousse vers la branche fix/correction du remote lib-utils.
Créez ensuite une pull request sur le dépôt d'origine.
Alternative avec split
Section intitulée « Alternative avec split »split extrait un historique nettoyé sans pousser :
git subtree split --prefix=libs/utils -b subtree-branchgit push lib-utils subtree-branch:fix/correctionUtile si vous voulez inspecter les commits avant de pousser.
Workflow complet
Section intitulée « Workflow complet »Voici l'enchaînement sur un cycle réel : intégration, travail quotidien,
synchronisation, remontée. Le point à retenir, c'est que seules les étapes 1, 3
et 4 emploient git subtree. Entre les deux, vous travaillez avec les
commandes Git habituelles, sans rien de spécifique au sous-dépôt, et c'est
précisément ce qui rend l'approche transparente pour le reste de l'équipe.
# 1. Ajouter le remote et le subtreegit remote add lib-utils https://github.com/org/lib-utils.gitgit subtree add --prefix=libs/utils lib-utils main --squash
# 2. Travailler normalement : les fichiers sont dans libs/utils/git add libs/utils/src/helper.pygit commit -m "fix: corriger le helper dans lib-utils"
# 3. Mettre à jour depuis l'amontgit subtree pull --prefix=libs/utils lib-utils main --squash
# 4. Contribuer en retourgit subtree push --prefix=libs/utils lib-utils fix/correctionSubmodule vs subtree
Section intitulée « Submodule vs subtree »Deux lignes de ce tableau pèsent plus lourd que les autres dans la décision : Clone et Historique. La première décide de ce que vos collègues devront apprendre avant de pouvoir contribuer, la seconde de la lisibilité du dépôt parent sur le long terme. Les autres critères se contournent avec un alias ou un script, ces deux-là non.
| Critère | Submodule | Subtree |
|---|---|---|
| Métadonnées | .gitmodules + ref dans l'index | Aucune, code dans le repo |
| Clone | --recurse-submodules nécessaire | Clone standard |
| Historique | Séparé (chaque repo le sien) | Fusionné dans le parent |
| Mise à jour | submodule update | subtree pull |
| Contribution | Push dans le submodule directement | subtree push (extraction) |
| Complexité | Moyenne-élevée | Moyenne |
| Taille du repo | Petite (référence seulement) | Plus grande (code intégré) |
| CI/CD | Configuration nécessaire | Transparent |
| Detached HEAD | Oui (après update) | Non |
Quand choisir subtree
Section intitulée « Quand choisir subtree »Le subtree est gagnant quand le coût d'apprentissage de l'équipe compte plus que la propreté de l'historique. Un seul de ces critères suffit rarement à trancher, mais si vous en cochez trois sur quatre, le choix est fait.
- Le sous-dépôt est rarement mis à jour
- Les collaborateurs ne connaissent pas les submodules
- L'intégration CI/CD doit être simple
- Le code du sous-dépôt est petit
Quand choisir submodule
Section intitulée « Quand choisir submodule »Le submodule reprend l'avantage dès que la référence de version devient un sujet en soi : audit, reproductibilité d'un build, ou plusieurs projets qui doivent consommer exactement la même révision d'une bibliothèque partagée.
- Le sous-dépôt évolue souvent et indépendamment
- Plusieurs projets référencent le même sous-dépôt
- Vous voulez une version figée précise
- Le sous-dépôt est volumineux
Dépannage
Section intitulée « Dépannage »Presque tous les incidents de git subtree remontent au même point : la
cohérence du mode --squash entre le add initial et les pull suivants.
Les messages de Git ne le disent jamais explicitement, d'où la traduction
ci-dessous. Lisez la colonne « Cause probable » avant de toucher à l'historique :
la mauvaise réaction, ici, consiste à forcer un merge qui n'a rien à faire là.
| Symptôme | Cause probable | Solution |
|---|---|---|
Conflits au subtree pull | Mélange --squash / pas --squash | Restez cohérent : toujours --squash ou jamais |
subtree push est lent | Historique volumineux à parcourir | Utilisez split + push séparés |
Working tree has modifications | Fichiers non commités | Commitez ou stashez avant le subtree pull |
refusing to merge unrelated histories | add fait avec --squash, pull lancé sans | Rejouez le pull avec --squash : git subtree pull n'accepte pas --allow-unrelated-histories |
À retenir
Section intitulée « À retenir »git subtree addintègre un dépôt dans un sous-dossier, aucune métadonnée externegit subtree pullmet à jour,pushrenvoie les modifications--squashcompresse l'historique, gardez la cohérence- Subtree = transparence et simplicité, submodule = séparation et contrôle
- Les collaborateurs n'ont rien de spécial à faire au clone