Les branches orphelines de Git
Les branches orphelines ne sont pas une nouveauté, mais une fonctionnalité avancée plutôt méconnue de Git. Je pense que cela est principalement dû au fait que nous n’en avons que peu besoin au quotidien.
Par contre, il y a des situations où les branches orphelines sont réellement utiles et il faut savoir qu’elles existent. D’autant plus qu’il est très simple d’en créer. Je vous propose d’explorer un peu tout ça.
Pourquoi créer des branches orphelines ?
Commençons par une situation simple : vous souhaitez avoir une branche dédiée pour votre documentation. C’est quelque chose que nous voyons souvent dans les projets open-source qui utilisent GitHub Pages pour héberger leur documentation par exemple.
Il est logique, qu’en soit, votre projet et sa documentation soient deux projets distincts. Ils sont liés, mais cela s’arrête là. Il n’y aurait pas de sens à bâtir la documentation en partant du même historique Git que pour les sources du projet.
Autre situation : vous souhaitez recommencer un projet dans une nouvelle technologie.
Si vous souhaitez commiter petit à petit votre travail, sans pour autant pourrir la branche principale de votre projet qui est toujours utilisée, la solution toute désignée est de créer une nouvelle branche. Mais personnellement cette solution ne me convient pas totalement.
En créant une nouvelle branche, elle hérite de tout l’historique des branches existantes. Cela ne me convient pas parce que si mon travail abouti, les anciens commits ne m’intéressent plus. Il n’y a plus de continuité entre le travail précédent et celui avec la nouvelle technologie. Pour autant, je ne veux pas supprimer les branches et l’historique existant pour l’instant parce que je ne sais pas si mon travail va réellement aboutir.
Dernière situation : vous souhaitez fusionner deux repositories qui n’ont pas le même historique. Cela peut arriver pour plusieurs raisons. Mettez deux développeurs non-habitués à Git sur le même projet sans les aider et vous pourriez obtenir deux repositories différents quelques semaines plus tard assez facilement.
Comprendre les branches orphelines
Je pense que vous avez déjà compris le principe. Nous ne voulons pas avoir d’historique lorsque nous créons une branche. L’idée, maintenant, est de voir comment le faire et comment cela peut nous aider à résoudre les problèmes cités plus haut.
Pour créer une branche orpheline avec Git nous n’allons pas utiliser la commande git branch
comme d’habitude, mais uniquement la commande git checkout
:
$ git checkout --orphan nom_de_la_branche
Switched to a new branch 'nom_de_la_branche'
L’argument --orphan
remplace ici totalement -b
que l’on utilise normalement pour créer des branches avec git checkout
. Ne les utilisez pas ensemble, votre commande échouera.
Après avoir exécuté cette commande, nous nous retrouvons sur la nouvelle branche qui, vous pouvez le vérifier avec git log
, ne possède pas d’historique.
$ git log
fatal: your current branch 'nom_de_la_branche' does not have any commits yet
Il n’y a même pas un seul commit. C’est nous qui allons devoir le faire. Pour nous aider, Git a préparé le dossier de travail avec les fichiers du projet. Ils correspondent au HEAD
de la branche précédente. Ce qui veut dire qu’ils sont normalement à la version la plus récente.
Par contre, si Git n’a pas commité pour nous, il a déjà ajouté tous les fichiers à la staging area. Vous pouvez le vérifier en faisant git status
.
En fonction de la situation dans laquelle vous vous trouvez (ajout d’une branche de documentation, départ de zéro, etc) vous allez devoir décider de garder ou pas les fichiers du projet.
Repartir de zéro sur une nouvelle branche
Si vous devez repartir d’une base vierge sur cette branche, supprimez localement les fichiers :
rm -rf ./*
rm -f .env .eslintrc
La première commande supprime tout ce qui est visible, la deuxième supprime au cas par cas les fichiers cachés que vous lui spécifiez.
rm -rf .*
: tous vos fichiers et dossiers cachés seront supprimés, dont le répertoire .git
, ce qui rendrait inutile ce que nous avons fait jusqu’à maintenant.Ensuite, nous pouvons supprimer les fichiers de la stagin area.
git add .
Nous sommes maintenant dans un répertoire de travail propre, sans aucun fichier. Il est donc possible d’ajouter tous les nouveaux fichiers dont vous avez besoin sur cette branche comme un projet Jekyll pour votre documentation, etc.
Ces trois dernières commandes peuvent être remplacées par une autre commande de Git.
git rm -rf .
Cette commande plus courte fait la même chose et vous évite de faire des erreurs.
Vous pouvez maintenant ajouter vos nouveaux fichiers dans le dossier – ceux de votre documentation par exemple –, les ajouter à l’index et commiter le tout comme vous le feriez habituellement.
Si vous souhaitez voir un projet réel sur lequel on a appliqué tout ce que nous venons de voir, comparez les branches gh-pages
et master
de UiKit de Ferpection. Elles n’ont pas du tout le même historique ni le même contenu.
Merger deux repositories avec des historiques différents
Maintenant imaginons que les fichiers que vous souhaitiez ajouter à cette branche sont déjà commités sur un autre repository Git. C’est très bien, cela veut dire que vous avez aussi l’historique de ces fichiers. Bien-sûr vous voulez les garder si possible.
Le problème, c’est que le premier repository, celui sur lequel nous travaillons depuis le début, ne possède pas du tout le même historique. Git ne nous laissera jamais merger deux branches qui n’ont pas le même historique.
Ce qui tombe bien parce que nous venons justement de voir une commande qui nous permet de créer et switcher sur une branche qui ne possède aucun historique. Si nous n’avons aucun historique le repository distant peut le créer pour nous.
Ajouter l’adresse du nouveau repository à notre repository local. Pour faciliter la compréhension, appelons notre repository actuel repo1
et le repository distant que nous souhaitons merger le repo2
.
$ git remote add repo2 url-repo2
$ git fetch repo2
Il ne reste plus qu’à merger la branche principale du repo2
avec la branche orpheline du repo1
.
git merge repo2/master
git push origin HEAD
Le git push
est bien-sûr optionnel. Dans le repo1
se trouve maintenant deux projets avec des historiques différents. Vérifiez-le avec la commande git log
.