Comment séparer un dépôt Git en 2 ?
Il y a plusieurs façons de procéder, chacune avec ses avantages et inconvénients.
Déjà, ce qu'il faut savoir, c'est qu'avec git, tout est possible sans ne jamais rien perdre.
En pratique, voici comment faire
Il est possible de détacher un sous-dossier de son projet initial et d'en faire un dépôt à part entière tout en conservant l'historique des modifications faites dans ce sous-dossier.
Voici un tutoriel simple, en français, vous permettant de réaliser ceci avec la commande git subtree split -P nom_du_sous_dossier -b nom_de_la_branche.
Cette commande permet, dans le dépôt initial, de construire une branche ne contenant qu'un sous-dossier du dépôt initial, et ceci localement (avec conservation de l'historique des modifications des fichiers de ce sous-dossier).
Il suffit ensuite, toujours localement :
- de créer un nouveau dossier, hors du dépôt initial, et d'initialiser ce nouveau dépôt vide via la commande
git init --bare - d'importer la branche créée précédemment dans ce nouveau dépôt via la commande
git pull chemin_local_du_depot_initial nom_de_la_branche(avec son historique de modification) - d'ajouter un remote pour pousser son contenu vers un nouveau dépôt distant via les commandes
git remote add origin url_du_nouveau_depotsuivie degit push origin -u main - enfin, de retourner dans le dépôt initial local, de supprimer le sous-dossier de ce dépôt via la commande
git rm -rf nom_du_sous_dossieret enfin de mettre à jour le dépôt distant via les commandesgit commit -m "suppression du sous-dossier nom_du_sous_dossier devenu un dépôt à part entière"suivie degit push.
Et si vous souhaitez conservez l'inclusion de ce nouveau dépôt dans le dépôt initial ?
En fait, il existe a minima deux façons de procéder :
- via la commande
git submodule - via la commande
git subtree
mais quelle méthode choisir ?
Disons que tout dépend de votre besoin...
... et que, chacune des méthodes proposées a ses avantages et ses inconvénients (sinon, ce serait trop simple !)
Voici 2 liens qui expliquent bien les différences avec des exemples concrets que l'on peut tester localement pour mieux comprendre leur fonctionnement interne et se faire une idée de quelle commande répond le mieux à notre besoin (cela dépend des projets) :
Pour résumer cela, voici ce que l'on peut noter :
-
Les submodules
- ont l'avantage d'être faciles à mettre en oeuvre.
- mais manquent de souplesse : le sous-module SM du projet global PG est une copie exacte du dépôt SM dans un sous-dossier du dépôt PG et on ne peut pas y déroger (c'est souvent ce que l'on veut mais si on veut réutiliser le dépôt SM dans d'autres projets, tous ces projets auront une copie exacte de SM sauf à en dériver une branche particulière dédiée à chaque projet à customiser).
- et il faut penser à vérifier s'il y a des mises à jour du sous-module SM dans le projet PG, à faire pour chaque sous-module du projet (ce n'est pas automatique, par exemple un simple
git clonede PG ne récupérera pas SM, il faut cloner SM manuellement). - et ceci est valable pour chaque personne qui travaille sur le dépôt PG (devoir vérifier souvent s'il y a des updates de SM à faire remonter dans PG sinon les personnes travaillent sur des versions différentes)
- voici la documentation officielle de la commande
git submodule, elle est assez conséquente.
-
Les subtrees
- sont plus difficiles à mettre en place (plus de lignes de commandes à taper).
- pourrissent l'historique du projet global PG lorsque des mises à jour sont faites dans le sous-projet SP car le sous-projet est vu comme une branche à fusionner dans la branche courante de PG (avec l'option
--squash, on peut limiter le rapatriement des updates de SP dans PG à seulement 2 commits, apparition de la branche SP puis fusion dans la branche courante de PG). - mais permettent de modifier dans PG des fichiers de SP (genre fichiers de configuration) sans devoir faire remonter ces modifications dans le dépôt distant SP (la modification peut restée contenue dans le dépôt distant PG et ainsi ne pas affecter d'autres dépôts qui incluraient SP).
- et chaque personne qui travaille sur PG dispose de la même version de code sans devoir vérifier les mises à jour de SP (elles remontent automatiquement et proposent de fusionner dans PG : charge au maintener d'accepter ou non ces updates).
- voici la documentation officielle de la commande
git subtree, elle correspond à la doc de la commandeman git-subtree.
Mais comme ces deux commandes ont des avantages et inconvénients différents, il existe depuis quelques temps une nouvelle commande git subrepo qu'il faut installer au préalable pour pouvoir l'utiliser.
-
subrepo fait un mixte des deux commandes précédentes en reprenant les avantages de ces deux philosophies :
- facile à mettre en oeuvre (1 seule ligne de commande :
git subrepo clone dossier_local_du_sous_projet sous_dossier_local_où_inclure_le_sous_projet_dans_le projet_global). - n'ajoute qu'un seul commit au projet global PG lors de l'inclusion du sous projet SP avec une explication claire de ce qu'il récupère dans PG :
git pushdans PG ne fait avancer PG que d'un commit. - n'ajoute qu'un seul commit au projet global PG lors de la récupération d'une mise à jour du sous projet SP :
git subrepo pull sous_dossier_local_où_est_inclus_le_sous_projet_dans_le_projet_globalsuivi degit pushdans PG ne fait avancer que d'un commit. Si on veut le détail de ce qui a été modifié dans SP alors il faut aller voir directement dans SP, pas dans PG. - permet de personnaliser la branche SP dans la branche courante PG sans reporter les modifications dans le dépôt distant SP et ceci en un seul commit.
- cette nouvelle commande
git subrepoest expliquée en détail dans le guide de la commandegit subtreeévoqué ci-dessus
- facile à mettre en oeuvre (1 seule ligne de commande :
Il faut passer un peu de temps pour voir comment ça marche, si ça répond bien à vos attentes, etc, mais le guide permet de tester et comprendre ce que l'on peut faire en manipulant des exemples concrets.
Comment faire cette séparation et inclusion en même temps avec la nouvelle commande git subrepo
- ne pas utiliser la commande
git subtree splitévoquée en début de cet article. - d'abord créer un nouveau dossier, hors du dépôt initial, et initialiser ce nouveau dépôt vide via la commande
git init --bare(je n'ai pas testé mais a priori, je ferai plutôt un nouveau dépôt vide sur le serveur). - retourner dans le projet global et alimenter le nouveau dépôt créé précédemment avec un sous-dossier du dépôt global via la commande
git subrepo init sous_dossier_qui_deviendra_le_sous_projet_dans_le_projet_global --remote=chemin_du_nouveau_depot_pour_le_sous_projet(l'historique de modification de ce sous-dossier sera importé dans le nouveau dépôt). - pousser ce contenu vers le nouveau dépôt via la commande
git subrepo push sous_dossier_qui_est_devenu_le_sous_projet_dans_le_projet_global.
Bon courage pour les expérimentations avant mise en pratique.
Auteur : Bruno Mercier - Licence CC-BY-NC-SA-4.0