Git 🌙
Chapters â–Ÿ 2nd Edition

3.2 Les branches avec Git - Branches et fusions : les bases

Branches et fusions : les bases

Prenons un exemple simple faisant intervenir des branches et des fusions (merges) que vous pourriez trouver dans le monde réel. Vous effectuez les tùches suivantes :

  1. vous travaillez sur un site web ;

  2. vous créez une branche pour un nouvel article en cours ;

  3. vous commencez Ă  travailler sur cette branche.

À cette Ă©tape, vous recevez un appel pour vous dire qu’un problĂšme critique a Ă©tĂ© dĂ©couvert et qu’il faut le rĂ©gler au plus tĂŽt. Vous faites donc ce qui suit :

  1. vous basculez sur la branche de production ;

  2. vous créez une branche pour y ajouter le correctif ;

  3. aprĂšs l’avoir testĂ©, vous fusionnez la branche du correctif et poussez le rĂ©sultat en production ;

  4. vous rebasculez sur la branche initiale et continuez votre travail.

Branches

Commençons par supposer que vous travaillez sur votre projet et avez déjà quelques commits sur la branche master.

Historique de _commits_ simple
Figure 18. Historique de commits simple

Vous avez dĂ©cidĂ© de travailler sur le problĂšme numĂ©rotĂ© #53 dans l’outil de gestion des tĂąches que votre entreprise utilise, quel qu’il soit. Pour crĂ©er une branche et y basculer tout de suite, vous pouvez lancer la commande git checkout avec l’option -b :

$ git checkout -b iss53
Switched to a new branch "iss53"

Cette commande est un raccourci pour :

$ git branch iss53
$ git checkout iss53
CrĂ©ation d’un nouveau pointeur de branche
Figure 19. CrĂ©ation d’un nouveau pointeur de branche

Vous travaillez sur votre site web et validez vos modifications. Ce faisant, la branche iss53 avance parce que vous l’avez extraite (c’est-à-dire que votre pointeur HEAD pointe dessus) :

$ vim index.html
$ git commit -a -m "ajout d'un pied de page [problĂšme 53]"
La branche iss53 a avancé avec votre travail
Figure 20. La branche iss53 a avancé avec votre travail

À ce moment-lĂ , vous recevez un appel qui vous apprend qu’il y a un problĂšme sur le site web qu’il faut rĂ©soudre immĂ©diatement. Avec Git, vous n’avez pas Ă  dĂ©ployer en mĂȘme temps votre correctif et les modifications dĂ©jĂ  validĂ©es pour iss53 et vous n’avez pas non plus Ă  vous fatiguer Ă  annuler ces modifications avant de pouvoir appliquer votre correctif sur ce qu’il y a en production. Tout ce que vous avez Ă  faire, c’est simplement de rebasculer sur la branche master.

Cependant, avant de le faire, notez que si votre copie de travail ou votre zone d’index contiennent des modifications non validĂ©es qui sont en conflit avec la branche que vous extrayez, Git ne vous laissera pas changer de branche. Le mieux est d’avoir votre copie de travail propre au moment de changer de branche. Il y a des moyens de contourner ceci (prĂ©cisĂ©ment par le remisage et l’amendement de commit) dont nous parlerons plus loin, au chapitre Remisage et nettoyage. Pour l’instant, nous supposons que vous avez validĂ© tous vos changements et que vous pouvez donc rebasculer vers votre branche master :

$ git checkout master
Switched to branch 'master'

À cet instant, votre rĂ©pertoire de copie de travail est exactement dans l’état dans lequel vous l’aviez laissĂ© avant de commencer Ă  travailler sur le problĂšme #53 et vous pouvez vous consacrer Ă  votre correctif. C’est un point important Ă  garder en mĂ©moire : quand vous changez de branche, Git rĂ©initialise votre rĂ©pertoire de travail pour qu’il soit le mĂȘme que la derniĂšre fois que vous avez effectuĂ© un commit sur cette branche. Il ajoute, retire et modifie automatiquement les fichiers de maniĂšre Ă  s’assurer que votre copie de travail soit identique Ă  ce qu’elle Ă©tait lors de votre dernier commit sur cette branche.

Vous avez ensuite un correctif Ă  faire. Pour ce faire, crĂ©ons une branche hotfix sur laquelle travailler jusqu’à rĂ©solution du problĂšme :

$ git checkout -b hotfix
Switched to a new branch 'hotfix'
$ vim index.html
$ git commit -a -m "correction de l'adresse email incorrecte"
[hotfix 1fb7853] "correction de l'adresse email incorrecte"
 1 file changed, 2 insertions(+)
Branche de correctif basée sur `master`
Figure 21. Branche de correctif basée sur master

Vous pouvez lancer vos tests, vous assurer que la correction est efficace et la fusionner dans la branche master pour la déployer en production. Vous réalisez ceci au moyen de la commande git merge :

$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
 index.html | 2 ++
 1 file changed, 2 insertions(+)

Vous noterez la mention fast-forward lors de cette fusion (merge). Comme le commit C4 pointĂ© par la branche hotfix que vous avez fusionnĂ©e Ă©tait directement devant le commit C2 sur lequel vous vous trouvez, Git a simplement dĂ©placĂ© le pointeur (vers l’avant). Autrement dit, lorsque l’on cherche Ă  fusionner un commit qui peut ĂȘtre atteint en parcourant l’historique depuis le commit d’origine, Git se contente d’avancer le pointeur car il n’y a pas de travaux divergents Ă  fusionner — ceci s’appelle un fast-forward (avance rapide).

Votre modification est maintenant dans l’instantanĂ© (snapshot) du commit pointĂ© par la branche master et vous pouvez dĂ©ployer votre correctif.

Avancement du pointeur de `master` sur `hotfix`
Figure 22. Avancement du pointeur de master sur hotfix

AprĂšs le dĂ©ploiement de votre correctif super-important, vous voilĂ  prĂȘt Ă  retourner travailler sur le sujet qui vous occupait avant l’interruption. Cependant, vous allez avant cela effacer la branche hotfix dont vous n’avez plus besoin puisque la branche master pointe au mĂȘme endroit. Vous pouvez l’effacer avec l’option -d de la commande git branch :

$ git branch -d hotfix
Deleted branch hotfix (3a0874c).

Maintenant, vous pouvez retourner travailler sur la branche qui contient vos travaux en cours pour le problÚme #53 :

$ git checkout iss53
Switched to branch "iss53"
$ vim index.html
$ git commit -a -m 'Nouveau pied de page terminé [issue 53]'
[iss53 ad82d7a] Nouveau pied de page terminé [issue 53]
1 file changed, 1 insertion(+)
Le travail continue sur `iss53`
Figure 23. Le travail continue sur iss53

Il est utile de noter que le travail rĂ©alisĂ© dans la branche hotfix n’est pas contenu dans les fichiers de la branche iss53. Si vous avez besoin de les y rapatrier, vous pouvez fusionner la branche master dans la branche iss53 en lançant la commande git merge master, ou vous pouvez retarder l’intĂ©gration de ces modifications jusqu’à ce que vous dĂ©cidiez plus tard de rapatrier la branche iss53 dans master.

Fusions (Merges)

Supposons que vous ayez dĂ©cidĂ© que le travail sur le problĂšme #53 Ă©tait terminĂ© et prĂȘt Ă  ĂȘtre fusionnĂ© dans la branche master. Pour ce faire, vous allez fusionner votre branche iss53 de la mĂȘme maniĂšre que vous l’avez fait plus tĂŽt pour la branche hotfix. Tout ce que vous avez Ă  faire est d’extraire la branche dans laquelle vous souhaitez fusionner et lancer la commande git merge:

$ git checkout master
Switched to branch 'master'
$ git merge iss53
Merge made by the 'recursive' strategy.
README |    1 +
1 file changed, 1 insertion(+)

Le comportement semble lĂ©gĂšrement diffĂ©rent de celui observĂ© pour la fusion prĂ©cĂ©dente de la branche hotfix. Dans ce cas, Ă  un certain moment, l’historique de dĂ©veloppement a divergĂ©. Comme le commit sur la branche sur laquelle vous vous trouvez n’est plus un ancĂȘtre direct de la branche que vous cherchez Ă  fusionner, Git doit effectuer quelques actions. Dans ce cas, Git rĂ©alise une simple fusion Ă  trois sources (three-way merge), en utilisant les deux instantanĂ©s pointĂ©s par les sommets des branches ainsi que leur plus proche ancĂȘtre commun.

Trois instantanés utilisés dans une fusion classique
Figure 24. Trois instantanés utilisés dans une fusion classique

Au lieu d’avancer simplement le pointeur de branche, Git crĂ©e un nouvel instantanĂ© qui rĂ©sulte de la fusion Ă  trois sources et crĂ©e automatiquement un nouveau commit qui pointe dessus. On appelle ceci un commit de fusion (merge commit) qui est spĂ©cial en cela qu’il a plus d’un parent.

Un _commit_ de fusion
Figure 25. Un commit de fusion

À prĂ©sent que votre travail a Ă©tĂ© fusionnĂ©, vous n’avez plus besoin de la branche iss53. Vous pouvez fermer le ticket dans votre outil de suivi des tĂąches et supprimer la branche :

$ git branch -d iss53

Conflits de fusions (Merge conflicts)

Quelques fois, le processus ci-dessus ne se dĂ©roule pas aussi bien. Si vous avez modifiĂ© diffĂ©remment la mĂȘme partie du mĂȘme fichier dans les deux branches que vous souhaitez fusionner, Git ne sera pas capable de rĂ©aliser proprement la fusion. Si votre rĂ©solution du problĂšme #53 a modifiĂ© la mĂȘme section de fichier que le hotfix, vous obtiendrez un conflit qui ressemblera Ă  ceci :

$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

Git n’a pas automatiquement crĂ©Ă© le commit de fusion. Il a arrĂȘtĂ© le processus le temps que vous rĂ©solviez le conflit. Si vous voulez vĂ©rifier, Ă  tout moment aprĂšs l’apparition du conflit, quels fichiers n’ont pas Ă©tĂ© fusionnĂ©s, vous pouvez lancer la commande git status :

$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add <file>..." to mark resolution)

    both modified:      index.html

no changes added to commit (use "git add" and/or "git commit -a")

Tout ce qui comporte des conflits et n’a pas Ă©tĂ© rĂ©solu est listĂ© comme unmerged. Git ajoute des marques de rĂ©solution de conflit standards dans les fichiers qui comportent des conflits, pour que vous puissiez les ouvrir et rĂ©soudre les conflits manuellement. Votre fichier contient des sections qui ressemblent Ă  ceci :

<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
======
<div id="footer">
 please contact us at support@github.com
</div>
>>>>>>> iss53:index.html

Cela signifie que la version dans HEAD (votre branche master, parce que c’est celle que vous aviez extraite quand vous avez lancĂ© votre commande de fusion) est la partie supĂ©rieure de ce bloc (tout ce qui se trouve au-dessus de la ligne =======), tandis que la version de votre branche iss53 se trouve en dessous. Pour rĂ©soudre le conflit, vous devez choisir une partie ou l’autre ou bien fusionner leurs contenus vous-mĂȘme. Par exemple, vous pourriez choisir de rĂ©soudre ce conflit en remplaçant tout le bloc par ceci :

<div id="footer">
please contact us at email.support@github.com
</div>

Cette rĂ©solution comporte des Ă©lĂ©ments de chaque section et les lignes <<<<<<<, ======= et >>>>>>> ont Ă©tĂ© complĂštement effacĂ©es. AprĂšs avoir rĂ©solu chacune de ces sections dans chaque fichier comportant un conflit, lancez git add sur chaque fichier pour le marquer comme rĂ©solu. Placer le fichier dans l’index marque le conflit comme rĂ©solu pour Git.

Si vous souhaitez utiliser un outil graphique pour rĂ©soudre ces conflits, vous pouvez lancer git mergetool qui dĂ©marre l’outil graphique de fusion appropriĂ© et vous permet de naviguer dans les conflits :

$ git mergetool

This message is displayed because 'merge.tool' is not configured.
See 'git mergetool --tool-help' or 'git help config' for more details.
'git mergetool' will now attempt to use one of the following tools:
opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc3 codecompare vimdiff emerge
Merging:
index.html

Normal merge conflict for 'index.html':
  {local}: modified file
  {remote}: modified file
Hit return to start merge resolution tool (opendiff):

Si vous souhaitez utiliser un outil de fusion autre que celui par dĂ©faut (Git a choisi opendiff dans ce cas car la commande a Ă©tĂ© lancĂ©e depuis un Mac), vous pouvez voir tous les outils supportĂ©s aprĂšs l’indication « of the following tools: ». Entrez simplement le nom de l’outil que vous prĂ©fĂ©reriez utiliser.

Note

Si vous avez besoin d’outils plus avancĂ©s pour rĂ©soudre des conflits complexes, vous trouverez davantage d’informations au chapitre Fusion avancĂ©e.

AprĂšs avoir quittĂ© l’outil de fusion, Git vous demande si la fusion a Ă©tĂ© rĂ©ussie. Si vous rĂ©pondez par la positive Ă  l’outil, il ajoute le fichier dans l’index pour le marquer comme rĂ©solu. Vous pouvez lancer Ă  nouveau la commande git status pour vĂ©rifier que tous les conflits ont Ă©tĂ© rĂ©solus :

$ git status
On branch master
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:

    modified:   index.html

Si cela vous convient et que vous avez vĂ©rifiĂ© que tout ce qui comportait des conflits a Ă©tĂ© ajoutĂ© Ă  l’index, vous pouvez entrer la commande git commit pour finaliser le commit de fusion. Le message de validation par dĂ©faut ressemble Ă  ceci :

Merge branch 'iss53'

Conflicts:
    index.html
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
#	.git/MERGE_HEAD
# and try again.


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# All conflicts fixed but you are still merging.
#
# Changes to be committed:
#	modified:   index.html
#

Vous pouvez modifier ce message pour inclure les dĂ©tails sur la maniĂšre dont le conflit a Ă©tĂ© rĂ©solu si vous pensez que cela peut ĂȘtre utile lors d’une revue ultĂ©rieure. Indiquez pourquoi vous avez fait ces choix, si ce n’est pas clair.

scroll-to-top