Git Peaks #3 Les branches
L’une des forces de Git est de nous permettre de créer, modifier et voyager entre différentes versions parallèles d’un projet, tel le Quinn Mallory de l’IT (si vous n’avez pas cette référence, honte à vous). Afin d’éviter de nous perdre dans le cosmos, voyons comment manipuler le continuum spacio-temporel pour voyager à travers différentes versions d’un projet.
Après le dernier Git Peaks #2 consacré aux commits, et particulièrement sur la manière de créer un historique propre et compréhensible, prenons un peu de hauteur en nous attardant sur le cas des branches !
Pourquoi les branches ?
Les branches dans Git permettent de faire évoluer plusieurs versions d’un projet en parallèle, sans que l’un n’ait d’incidence sur les autres. Il est ensuite possible de fusionner différentes versions, de mettre à jour une version par rapport à une autre, ou bien de laisser tomber une version, définitivement ou non (le fameux effet bac à sable de Git).
À titre d’exemple, la branche master représente souvent la version stable du projet, qui sera distribuée aux utilisateurs. Ensuite, plusieurs stratégies existent, où de nouvelles branches vont permettre de travailler sur une nouvelle fonctionnalité, corriger un bug ou perfectionner la version beta, sans pour autant toucher à la version stable du projet. Ainsi, master reste stable et disponible à tout moment. Lorsque le travail sur une branche parallèle est terminé, testé et approuvé, il peut être fusionné avec la branche master. La version stable du projet contient désormais les nouveaux ajouts développés dans la branche parallèle.
Manipuler les branches Git
Quelques commandes Git permettent de manipuler les branches et de se déplacer à travers les différentes versions du projet. Les actions principales sont de créer une branche et de changer de branche courante. Mais tout d’abord, un peu de repérage.
Se repérer dans le multivers
Nous ne présentons plus git status
qui permet d’avoir un état général du repository, et justement en premier lieu la branche courante.
$ git status On branch master Your branch is up to date with 'origin/master'. nothing to commit, working tree clean
Pour avoir une vision plus précise des branches existantes, git branch
permet de les lister, accompagnées des branches « distances » (présentes sur le serveur) grâce à l’option -a
. La branche courante est toujours précédée d’une étoile.
$ git branch * master $ git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/cpp-hello-world remotes/origin/master
Créer une branche Git et se déplacer
Pour créer une nouvelle branche, on utilise de nouveau branch
suivie du nom que l’on souhaite donner. La nouvelle branche sera à sa création identique à la branche courante.
$ git branch new-feature $ git branch * master new-feature $ git log 480b8fd (HEAD -> master, origin/master, origin/HEAD, new-feature) Merge branch 'python-hello-world' Wed Oct 14 15:27:22 2020 +0200 Jules Chevalier <jchevalier@peaks.fr> </jchevalier@peaks.fr>
On remarque que l’on est toujours sur la branche master
. Pour changer de branche et passer sur la nouvelle branche, on va utiliser git checkout
.
$ git status On branch master Your branch is up to date with 'origin/master'. nothing to commit, working tree clean $ git checkout new-feature Switched to branch 'new-feature' $ git status On branch new-feature nothing to commit, working tree clean
Pour effectuer la création d’une branche et la bascule vers la nouvelle branche en une seule commande, on peut utiliser l’option
-b
de Git.$ git checkout -b new-feature Switched to a new branch 'new-feature'
La nouvelle branche va donc évoluer indépendamment de master
et les nouvelles modifications ne seront apportées qu’à cette branche. Grâce à ce système, on peut donc changer de version à la volée, en mettant le travail en cours en attente, puis revenir plus tard à cette version sans rien perdre, et sans manipulation complexe.
Pour illustrer tout cela, un petit exemple de travail sur deux branches indépendantes.
$ git status On branch new-feature nothing to commit, working tree clean # Ajout d'un nouveau fichier et commit sur new-feature $ touch new-feature.txt $ git status On branch new-feature Untracked files: (use "git add ..." to include in what will be committed) new-feature.txt nothing added to commit but untracked files present (use "git add" to track) $ git add new-feature.txt $ git commit -m'chore: add test file' [new-feature 23a538e] chore: add test file 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 new-feature.txt # Le nouveau commit est bien présent $ git log 23a538e (HEAD -> new-feature) chore: add test file Tue Feb 2 16:10:55 2021 +0100 Jules Chevalier <jchevalier@peaks.fr> 480b8fd (origin/master, origin/HEAD, new-feature, master) Merge branch 'python-hello-world' Wed Oct 14 15:27:22 2020 +0200 Jules Chevalier <jchevalier@peaks.fr> # Mais n'apparaît pas sur la branche master, qui est restée intacte $ git checkout master Switched to branch 'master' $ git log 480b8fd (HEAD -> master, origin/master, origin/HEAD, new-feature) Merge branch 'python-hello-world' Wed Oct 14 15:27:22 2020 +0200 Jules Chevalier <jchevalier@peaks.fr> </jchevalier@peaks.fr></jchevalier@peaks.fr></jchevalier@peaks.fr>
Pousser sa nouvelle branche
Comme toujours, l’objectif premier de Git est de sauvegarder et partager son travail. Pour cela, pousser une branche sur un serveur Git (Gitlab, Github…) permet de sécuriser les modifications avant toute opération supplémentaire. Une fois de plus c’est vers git push
que l’on se tourne, à quelques détails près.
Si l’on se contente de git push
notre nouvelle branche, git nous averti qu’il ne connaît pas la destination de la branche, et est donc dans l’incapacité de terminer l’opération.
Pourquoi est-ce que la question ne s’est pas posée précédemment avec
master
? Parce que la branche master a été récupérée directement du serveur, lors dugit clone
, et a été associée à son pendant serveur, que l’on nommeorigin/master
, appelée upstream branch.
$ git checkout new-feature Switched to branch 'new-feature'
$ git push fatal: The current branch new-feature has no upstream branch. To push the current branch and set the remote as upstream, use git push --set-upstream origin new-feature
Pour palier cette situation, il faut indiquer directement à Git le nom de la branche à pousser sur le serveur avec git push origin new-feature
. On peut, avec l’option -u
indiquer à Git que l’on veut désormais lier la branche locale à son pendant distant, évitant de refaire cette précision à chaque push
.
$ git push -u origin new-feature [...] To git.peaks.fr:jchevalier/git-example.git * [new branch] new-feature -> new-feature Branch 'new-feature' set up to track remote branch 'new-feature' from 'origin'.
Maintenant que la nouvelle branche est sauvegardée, voyons comment rapatrier ses modifications dans la branche principale master
.
Fusiooooooon /o/\o\
De façon évidente, l’utilisation de branches distinctes prend tout son sens par la fusion de celles-ci. Lorsque le travail sur une branche est terminé, il est temps de la fusionner avec une branche principale, afin de rapatrier le travail fait « en parallèle » dans cette nouvelle branche. Cette fusion se fait en deux temps : bascule vers la branche cible, et fusion des modifications de la source vers la cible. La bascule se fait toujours avec git checkout
, et la fusion se fait avec git merge
suivi du nom de la branche source.
$ git checkout master Switched to branch 'master' Your branch is up to date with 'origin/master'. $ git merge new-feature Updating 480b8fd..23a538e Fast-forward new-feature.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 new-feature.txt $ git log 23a538e (HEAD -> master, origin/new-feature, new-feature) chore: add test file Tue Feb 2 16:10:55 2021 +0100 Jules Chevalier <jchevalier@peaks.fr> 480b8fd (origin/new-feature, origin/master, origin/HEAD, new-feature) Merge branch 'python-hello-world' Wed Oct 14 15:27:22 2020 +0200 Jules Chevalier <jchevalier@peaks.fr> </jchevalier@peaks.fr></jchevalier@peaks.fr>
On peut voir suite à la fusion des branches que master
a bien récupéré le commit créé dans new-feature
.
Conclusion
Les branches donnent clairement toute sa puissance à Git. Grâce à elles, il est possible de travailler en parallèle sur plusieurs versions du projet, sans interférences, tout en bénéficiant de la sauvegarde et du partage du travail effectué. Une version du projet reste stable en toute circonstance, pendant que les nouvelles fonctionnalités et les bugs sont traités dans des versions parallèles simultanément.
Il y a bien évidemment beaucoup à ajouter pour balayer l’étendue des possibilités mais aussi des difficultés apportées par l’utilisation des branches. Un article entier serait nécessaire pour parler de la gestion des conflits, lorsque l’on souhaite fusionner des branches dont les modifications ne sont pas compatibles, ou encore sur les différents modèles d’utilisation des branches, fameux Workflow Git donc GitFlow est le plus connu…