Accéder au contenu principal

Git subtree expliqué : guide pratique avec exemples

Utilisez quatre commandes essentielles (add, pull, push, split) pour garder des bibliothèques partagées synchronisées entre équipes.
Actualisé 19 sept. 2026  · 12 min lire

Explorer avec l’IA

ChatGPTClaudePerplexity

Vous travaillez sur un projet d’analyse de données et devez inclure une bibliothèque d’utilitaires partagée que votre équipe maintient dans un autre dépôt. Vous pourriez copier-coller le code, mais vous perdriez la possibilité de le mettre à jour. Vous pourriez utiliser les sous-modules Git, mais vous avez entendu dire qu’ils sont compliqués. Il existe une troisième option : git subtree.

git subtree vous permet d’embarquer un dépôt Git à l’intérieur d’un autre en tant que sous-répertoire, en conservant tout l’historique et une voie de mise à jour simple.

Ce guide explique quand utiliser git subtree, comment cela fonctionne et propose des exemples concrets pour les workflows courants. Nous utiliserons des scénarios réalistes que vous rencontrez réellement dans des projets de data science.

Qu’est-ce que git subtree ?

Git subtree inclut un autre dépôt Git sous un dossier spécifique de votre projet tout en conservant l’intégralité de son historique de commits. Contrairement au simple copier-coller ou aux liens symboliques, subtree maintient un lien avec le dépôt d’origine pour pouvoir récupérer les mises à jour et renvoyer des changements.

Voici ce qui le différencie d’un simple copier-coller :

Copier-coller du code :

  • Aucun lien avec le dépôt d’origine
  • Pas de voie de mise à jour quand la bibliothèque évolue
  • Aucun historique sur l’origine du code

Git subtree :

  • Historique complet des commits du dépôt d’origine
  • Récupération des mises à jour avec une seule commande
  • Possibilité de renvoyer des changements vers le dépôt d’origine si besoin
  • Tout vit dans un seul clone du dépôt

La différence clé avec les sous-modules Git : le contenu du subtree est directement committé dans votre dépôt principal. Quand quelqu’un clone votre projet, il récupère tout en une seule opération, sans étape de configuration supplémentaire.

Résultat : une arrivée plus fluide des nouveaux contributeurs et moins de problèmes « ça marche sur ma machine ».

Comment fonctionne git subtree

Quand vous ajoutez un subtree, Git fait quelque chose d’astucieux : il fusionne l’historique d’un autre dépôt dans un sous-répertoire de votre projet. Concrètement :

  1. Git récupère l’historique du dépôt distant
  2. Git réécrit ces commits pour qu’ils apparaissent sous le sous-répertoire choisi
  3. Git fusionne cet historique réécrit dans votre branche courante
  4. Les mises à jour futures suivent le même schéma : fetch, réécriture, merge

Votre dépôt contient de vrais fichiers et l’historique complet du subtree, pas seulement un pointeur. Quand quelqu’un clone votre projet ou change de branche, il a immédiatement tout le code. Pas d’étape séparée « initialiser les sous-modules ».

Le compromis : votre dépôt grossit puisqu’il contient à la fois votre code et celui du subtree avec tout son historique. Est-ce que cela vaut le coup ? Cela dépend, mais pour la plupart des équipes data, oui selon moi.

Comment utiliser git subtree (commandes courantes)

Maintenant que vous comprenez ce que fait subtree, passons en revue les opérations les plus courantes. Nous prendrons un cas réaliste : vous développez un projet d’analyse et devez inclure une bibliothèque d’utilitaires partagée.

Exemple de configuration :

  • Projet principal : data-pipeline

  • Bibliothèque partagée : shared-utils (hébergée sur https://github.com/yourteam/shared-utils.git)

  • Vous souhaitez l’inclure sous libs/shared-utils/ dans votre projet

git subtree add

La commande add intègre pour la première fois un autre dépôt dans votre projet.

La syntaxe de base, pour référence, ressemble à ceci :

git subtree add --prefix=<directory> <remote-url> <branch> --squash

Exemple concret :

# Ajouter le dépôt shared-utils sous libs/shared-utils
git subtree add --prefix=libs/shared-utils \
  https://github.com/yourteam/shared-utils.git \
  main \
  --squash

Ce que cela fait :

  • Crée le répertoire libs/shared-utils
  • Copie tous les fichiers de shared-utils dedans
  • Committe le tout dans votre dépôt
  • Enregistre la connexion pour les futures mises à jour

Comprendre les options :

  • --prefix=libs/shared-utils : l’emplacement du subtree dans votre projet. Vous devez utiliser exactement le même préfixe pour toutes les opérations futures. Changez-le une fois et vous finirez à chercher sur Stack Overflow à 2 h du matin pourquoi tout a cassé.

  • --squash : combine tout l’historique du subtree en un seul commit dans votre projet. Cela garde l’historique plus lisible. Sans --squash, vous verriez chaque commit du dépôt d’origine dans l’historique de votre projet, ce qui peut être trop verbeux.

Quand écraser l’historique : utilisez --squash sauf si vous devez préserver l’attribution détaillée des commits du dépôt d’origine. Pour la plupart des projets data avec des utilitaires partagés, un historique écrasé est plus propre et plus facile à exploiter. Personnellement, je l’utilise toujours.

Après exécution, vous verrez un nouveau commit avec un message du type « Add 'libs/shared-utils/' from commit 'abc123' ». Le dossier libs/shared-utils contient désormais tous les fichiers, immédiatement utilisables.

git subtree pull

La commande pull met à jour votre subtree avec les changements du dépôt d’origine. Si l’équipe en charge de la bibliothèque corrige un bug ou ajoute une fonctionnalité, c’est ainsi que vous les récupérez.

Syntaxe de base :

git subtree pull --prefix=<directory> <remote-url> <branch> --squash

Exemple concret :

# Récupérer les derniers changements de shared-utils
git subtree pull --prefix=libs/shared-utils \
  https://github.com/yourteam/shared-utils.git \
  main \
  --squash

Ce que cela fait :

  • Récupère les nouveaux commits depuis le dépôt distant
  • Les fusionne dans votre répertoire libs/shared-utils
  • Crée un commit de fusion dans votre projet

Le préfixe doit correspondre exactement. Si vous avez utilisé libs/shared-utils lors de l’ajout, vous devez utiliser libs/shared-utils pour le pull. Un préfixe différent ne fonctionnera pas. Croyez-moi.

Cohérence du squash : si vous avez utilisé --squash lors de l’ajout, utilisez --squash pour chaque pull. Mélanger des mises à jour écrasées et non écrasées rend l’historique confus. Leçon apprise à la dure.

Gestion des conflits : si vous avez modifié des fichiers dans libs/shared-utils et que les mêmes fichiers ont changé en amont, vous aurez des conflits de fusion. Résolvez-les comme tout conflit Git : éditez les fichiers, indexez-les et terminez la fusion.

Documentez dans le README de votre équipe si vous utilisez --squash ou non, pour garder tout le monde aligné.

git subtree push

La commande push renvoie vers le dépôt d’origine les changements que vous avez effectués dans le dossier du subtree.

Syntaxe de base :

git subtree push --prefix=<directory> <remote-url> <branch>

Exemple concret :

# Renvoyer les changements de libs/shared-utils vers le dépôt d’origine
git subtree push --prefix=libs/shared-utils \
  https://github.com/yourteam/shared-utils.git \
  main

Ce que cela fait :

  • Extrait uniquement les commits qui ont affecté libs/shared-utils
  • Les réécrit comme s’ils avaient été faits à la racine de shared-utils
  • Pousse ces commits vers le dépôt d’origine

Quand l’utiliser : vous maintenez une bibliothèque partagée dans le dépôt de votre application et souhaitez contribuer des améliorations en retour. Par exemple, vous avez ajouté une fonction de validation de données utile à shared-utils en travaillant sur votre projet data-pipeline, et d’autres projets doivent en bénéficier.

Considérez push comme l’inverse de pull. Pull fait entrer les changements ; push les envoie. Git n’extrait que l’historique pertinent et le traduit vers la structure du dépôt d’origine. Plutôt malin.

Vous avez besoin des droits d’écriture sur le dépôt d’origine. Si vous n’avez pas l’autorisation, forkez le dépôt, poussez sur votre fork, puis créez une pull request.

git subtree split

La commande split extrait l’historique d’un sous-répertoire dans une branche dédiée.

Syntaxe de base :

git subtree split --prefix=<directory> -b <new-branch-name>

Exemple concret :

# Extraire libs/shared-utils dans sa propre branche
git subtree split --prefix=libs/shared-utils -b shared-utils-extracted

Ce que cela fait :

  • Crée une nouvelle branche ne contenant que les commits ayant affecté libs/shared-utils
  • Cette branche ressemble à un dépôt autonome de ce seul répertoire
  • La branche d’origine reste inchangée

Cas d’usage fréquents :

Extraire une bibliothèque d’un monorepo : votre pipeline de traitement de données a pris de l’ampleur et vous souhaitez isoler un composant réutilisable en bibliothèque autonome. Split crée une branche avec uniquement l’historique de ce composant, que vous pouvez pousser vers un nouveau dépôt.

Publier un sous-répertoire comme projet indépendant : vous avez créé un module de visualisation de données utile dans votre projet d’analyse et souhaitez le partager en package autonome. Split l’extrait en conservant tout l’historique.

Exemple de workflow pour créer un nouveau dépôt autonome :

# Extraire le sous-répertoire
git subtree split --prefix=libs/shared-utils -b shared-utils-standalone

# Créer un nouveau dépôt et y pousser
git remote add shared-utils-origin https://github.com/yourteam/shared-utils-new.git
git push shared-utils-origin shared-utils-standalone:main

shared-utils-new est maintenant un dépôt complet avec l’historique intégral de ce seul sous-répertoire.

git subtree vs. submodule

La question qui revient toujours : subtree ou submodule ?

Les deux permettent d’inclure du code externe dans votre dépôt, mais il existe des différences. Si vous avez besoin d’un verrouillage de version strict, ou si la taille du dépôt est contrainte, utilisez submodule. Si votre équipe a des niveaux variés en Git et que vous voulez la simplicité, utilisez subtree. Sinon, les deux conviennent.

Aspect

git subtree

git submodule

Complexité de mise en place

Moyenne (commandes simples)

Élevée (étape init séparée requise)

Expérience au clonage

Simple (un clone, tout fonctionne)

Complexe (clone + git submodule update --init)

Taille du dépôt

Plus grande (inclut le contenu + l’historique du subtree)

Plus petite (simple pointeur vers un autre dépôt)

Onboarding des développeurs

Facile (tout fonctionne après clone)

Plus difficile (il faut comprendre le workflow submodule)

Complexité CI/CD

Simple (clone et c’est parti)

Plus complexe (il faut initialiser les sous-modules)

Verrouillage de version

Moins précis (fusion du dernier commit ou d’un commit spécifique)

Précis (hash de commit exact)

Workflow de mise à jour

git subtree pull (fusionne les changements)

cd submodule && git pull (manuel)

Risque « ça marche sur ma machine »

Plus faible (tout est committé)

Plus élevé (l’état du sous-module peut diverger)

Renvoyer des changements

git subtree push

Git push classique à l’intérieur du sous-module

Séparation des préoccupations

Mixte (le code vit dans le repo principal)

Claire (le sous-module est un dépôt séparé)

Utilisez git submodule quand vous avez besoin :

  • D’un verrouillage de version strict (utiliser exactement le commit X de la bibliothèque Y)
  • D’une séparation claire entre projets (véritablement indépendants)
  • De plusieurs projets partageant la même dépendance à des versions différentes
  • D’un dépôt principal le plus petit possible pour votre workflow

Utilisez git subtree quand vous voulez :

  • Aucune étape d’initialisation supplémentaire
  • Que les nouveaux membres puissent cloner et lancer les tests directement
  • Moins de concepts Git à apprendre pour l’équipe
  • « Vendeuriser » des dépendances avec des mises à jour occasionnelles

Aucune approche n’est systématiquement meilleure. Le choix dépend de la valeur que vous accordez à la simplicité versus le contrôle strict des versions. Mon avis : pour des équipes data où la plupart se concentrent sur l’analyse plutôt que sur les arcanes de Git, subtree réduit les frictions. Pour les équipes plateforme gérant des services avec des exigences de dépendances, les sous-modules sont pertinents.

Quand ne pas utiliser git subtree

N’utilisez pas git subtree quand la taille du dépôt est une contrainte forte. Subtree gonfle la taille de votre repo puisqu’il inclut le contenu complet et l’historique. Si vous approchez des limites de votre plateforme Git ou si la vitesse réseau est critique, les sous-modules sont plus légers.

N’utilisez pas non plus git subtree si vous avez besoin d’un verrouillage strict des versions. Vous devez utiliser exactement la version 2.3.1 de la bibliothèque, et rien d’autre. Les sous-modules épinglent des commits précis ; subtree fusionne des plages de commits.

N’utilisez pas git subtree si le dépôt externe change très fréquemment. Le subtree est mis à jour quotidiennement avec des changements importants. Des pulls constants créent un historique de merges brouillon. Demandez-vous si vous avez vraiment besoin de l’embarquer, ou si un gestionnaire de packages serait plus propre.

Enfin, n’utilisez pas git subtree si une séparation organisationnelle est requise. Le contenu du subtree a des contrôles d’accès, des licences ou une propriété différents. Les conserver dans des dépôts séparés clarifie les frontières.

Avantages de git subtree

Expérience de clonage en un seul dépôt : exécutez git clone, et c’est tout. Tout ce dont vous avez besoin est là. Cela réduit les frictions d’onboarding quand tout le monde n’est pas expert Git.

Moins de pièces mobiles que les sous-modules : pas d’étape d’initialisation séparée. Pas d’états HEAD détachés à expliquer. Pas de « votre sous-module n’est pas à jour ». Le workflow se rapproche des opérations Git classiques (add, pull, push).

Simplicité en CI/CD : vos scripts d’intégration continue sont plus simples. Avec subtree, vous clonez et lancez les tests. Avec les sous-modules, vous clonez, initialisez récursivement, puis lancez les tests. Cette étape supplémentaire casse les builds quand on l’oublie.

Fonctionne bien pour des équipes au niveau Git hétérogène : les profils data juniors n’ont pas besoin de comprendre la mécanique des sous-modules. Ils utilisent des commandes Git familières et tout fonctionne.

Idéal pour « vendoriser » avec des mises à jour ponctuelles : vous incluez la version 1.2 d’une bibliothèque. Six mois plus tard, la version 1.3 apporte un correctif utile. Intégrez-la en une commande. Vous n’êtes pas coincé avec un code copié non maintenu, sans pour autant suivre en continu l’amont.

Limites et compromis de git subtree

Le dépôt grossit sensiblement : votre repo contient votre code et celui du subtree avec tout son historique (sauf si vous utilisez --squash, ce qui aide). Un subtree de 50 Mo ajoute environ 50 Mo à votre repo.

Pour de gros subtrees ou plusieurs subtrees, cela s’additionne. Évaluez si la commodité vaut l’espace disque et le temps de clonage.

  • Complexité du graphe d’historique : même avec --squash, les commits de merge des mises à jour du subtree ajoutent de la complexité à l’historique. Si vous mettez souvent à jour le subtree, votre graphe devient chargé.

  • Confusion en cas de divergence amont : si vous modifiez des fichiers du subtree et que l’amont les modifie aussi, des conflits surviennent. Les résoudre peut être déroutant car vous fusionnez le dépôt de quelqu’un d’autre dans un sous-répertoire du vôtre.

  • Des écueils côté push : git subtree push doit réécrire l’historique, ce qui peut être lent pour de gros subtrees. Si vous avez fait de nombreux commits dans le dossier du subtree, le push peut prendre du temps. Soyez patient.

  • Les conflits de merge existent toujours : si vous et l’amont modifiez le même fichier, vous devrez résoudre les conflits. Ce n’est pas propre au subtree, et subtree ne les empêche pas.

  • Discipline requise : utiliser des valeurs --prefix différentes ou des options --squash incohérentes crée des problèmes. L’équipe doit documenter et suivre le même workflow.

Ces limites ne sont pas bloquantes, mais il faut les connaître. Le compromis : un workflow plus simple en échange d’un repo plus gros et d’un historique potentiellement plus complexe.

Erreurs courantes avec git subtree

Lorsque vous commencez avec git subtree, attention à ces écueils fréquents. Une fois identifiés, ils sont faciles à éviter.

Utilisation incohérente de --squash

Vous avez utilisé --squash lors de l’ajout du subtree mais l’avez oublié pendant un git subtree pull. Votre historique mélange désormais des commits écrasés et non écrasés du subtree, ce qui le rend confus.

La solution : documentez-le dans le README de votre projet. Écrivez par exemple : « Toujours utiliser --squash avec le subtree shared-utils. »

Mauvais préfixe ou changement de préfixe

Vous avez ajouté le subtree avec --prefix=libs/shared-utils mais tenté plus tard un pull avec --prefix=libs/utils. Cela ne fonctionne pas car Git ne retrouve pas le subtree.

La solution : utilisez exactement le même préfixe à chaque fois. Envisagez de le documenter dans un script, par exemple :

# scripts/update-subtree.sh
#!/bin/bash
git subtree pull --prefix=libs/shared-utils \
  https://github.com/yourteam/shared-utils.git \
  main \
  --squash

S’attendre à ce que subtree se comporte comme submodule

Vous pensiez pouvoir faire cd libs/shared-utils && git checkout pour changer de version. Cela ne marche pas, car le contenu d’un subtree est juste des fichiers dans votre repo, pas un dépôt Git séparé.

La solution : comprenez que subtree fusionne des commits spécifiques. Pour « rétrograder » un subtree, vous devez retrouver et fusionner un commit plus ancien ou revert les commits de mise à jour du subtree.

Ne pas documenter le workflow

Les membres de l’équipe ne savent pas s’il faut utiliser --squash, quelle URL distante utiliser, ni quand renvoyer des changements. Si chacun fait différemment, l’historique devient incohérent.

La solution : ajoutez une section à votre README :

## Mettre à jour shared-utils

## Pour récupérer les derniers changements :
git subtree pull --prefix=libs/shared-utils https://github.com/yourteam/shared-utils.git main --squash

## Pour renvoyer des changements :
git subtree push --prefix=libs/shared-utils https://github.com/yourteam/shared-utils.git main

Enfin, modifier les fichiers du subtree sans anticiper

Vous éditez des fichiers dans libs/shared-utils pour votre projet spécifique, mais ne renvoyez jamais ces changements. Six mois plus tard, vous faites un pull et rencontrez des conflits parce que vos modifications entrent en collision avec celles de l’amont.

La solution : si vous modifiez des fichiers du subtree, soit poussez ces changements vers shared-utils s’ils sont utiles à tous, soit acceptez de devoir gérer des conflits lors des mises à jour.

Conclusion

Git subtree échange une taille de dépôt plus élevée et un historique plus complexe contre une plus grande simplicité de workflow. Pour des équipes data où la plupart se concentrent sur l’analyse plutôt que sur l’infrastructure Git, subtree réduit les frictions. Les nouveaux arrivants clonent le repo et peuvent commencer tout de suite.

Pour aller plus loin sur les workflows Git, explorez notre Introduction to Git.


Oluseye Jeremiah's photo
Author
Oluseye Jeremiah
LinkedIn

Rédacteur technique spécialisé dans l'IA, la ML et la science des données, rendant les idées complexes claires et accessibles.

Sujets
Git

Apprenez Git avec DataCamp

Cours

Introduction à Git

2 h
98.2K
Maîtrisez Git, l’outil incontournable pour gérer vos versions et collaborer efficacement sur vos projets logiciels et données.
Afficher les détailsRight Arrow
Commencer Le Cours
Voir plusRight Arrow