Skip to content

Latest commit

 

History

History
681 lines (518 loc) · 28.6 KB

advanced-merging.asc

File metadata and controls

681 lines (518 loc) · 28.6 KB

Fusion avancée

La fusion avec Git est généralement plutôt facile. Puisque Git rend facile la fusion d’une autre branche plusieurs fois, cela signifie que vous pouvez avoir une branche à très longue durée de vie que vous pouvez mettre à jour au fil de l’eau, en résolvant souvent les petits conflits plutôt que d’être surpris par un énorme conflit à la fin de la série.

Cependant, il arrive quelques fois des conflits compliqués. À la différence d’autres systèmes de contrôle de version, Git n’essaie pas d’être plus intelligent que de mesure pour la résolution des conflits. La philosophie de Git, c’est d’être malin pour déterminer lorsque la fusion est sans ambiguïté mais s’il y a un conflit, il n’essaie pas d’être malin pour le résoudre automatiquement. De ce fait, si vous attendez trop longtemps pour fusionner deux branches qui divergent rapidement, vous rencontrerez des problèmes.

Dans cette section, nous allons détailler ce que certains de ces problèmes peuvent être et quels outils Git vous offre pour vous aider à gérer ces situations délicates. Nous traiterons aussi quelques types de fusions différents, non-standard, ainsi que la manière de mémoriser les résolutions que vous avez déjà réalisées.

Conflits de fusion

Bien que nous avons couvert les bases de la résolution de conflits dans ch03-git-branching.asc, pour des conflits plus complexes, Git fournit quelques outils pour vous aider à vous y retrouver et à mieux gérer les conflits.

Premièrement, si c’est seulement possible, essayer de démarrer d’un répertoire de travail propre avant de commencer une fusion qui pourrait engendrer des conflits. Si vous avez un travail en cours, validez-le dans une branche temporaire ou remisez-le. Cela vous permettra de défaire tout ce que vous pourrez essayer. Si vous avez des modifications non sauvegardées dans votre répertoire de travail quand vous essayez une fusion, certaines des astuces qui vont suivre risque de vous faire perdre ce travail.

Parcourons ensemble un exemple très simple. Nous avons un fichier Ruby super simple qui affiche « hello world ».

#! /usr/bin/env ruby

def hello
  puts 'hello world'
end

hello()

Dans notre dépôt, nous créons une nouvelle branche appelée whitespace et nous entamons la transformation de toutes les fins de ligne Unix en fin de lignes DOS, ce qui revient à modifier chaque ligne, mais juste avec des caractères invisibles. Ensuite, nous changeons la ligne « hello world » en « hello mundo ».

$ git checkout -b whitespace
Basculement sur la nouvelle branche 'whitespace'

$ unix2dos hello.rb
unix2dos: converting file hello.rb to DOS format ...
$ git commit -am 'converted hello.rb to DOS'
[whitespace 3270f76] converted hello.rb to DOS
 1 file changed, 7 insertions(+), 7 deletions(-)

$ vim hello.rb
$ git diff -w
diff --git a/hello.rb b/hello.rb
index ac51efd..e85207e 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,7 +1,7 @@
 #! /usr/bin/env ruby

 def hello
-  puts 'hello world'
+  puts 'hello mundo'^M
 end

 hello()

$ git commit -am 'hello mundo change'
[whitespace 6d338d2] hello mundo change
 1 file changed, 1 insertion(+), 1 deletion(-)

À présent, nous rebasculons sur master et nous ajoutons une documentation de la fonction.

$ git checkout master
Basculement sur la branche 'master'

$ vim hello.rb
$ git diff
diff --git a/hello.rb b/hello.rb
index ac51efd..36c06c8 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,5 +1,6 @@
 #! /usr/bin/env ruby

+# prints out a greeting
 def hello
   puts 'hello world'
 end

$ git commit -am 'document the function'
[master bec6336] document the function
 1 file changed, 1 insertion(+)

Et maintenant, nous essayons de fusionner notre branche whitespace et nous allons générer des conflits dûs aux modifications de fins de ligne.

$ git merge whitespace
Fusion automatique de hello.rb
CONFLIT (contenu) : Conflit de fusion dans hello.rb
La fusion automatique a échoué ; réglez les conflits et validez le résultat.
Abandonner une fusion

Nous avons ici plusieurs options. Une première consiste à sortir de cette situation. Vous ne vous attendiez peut-être pas à rencontrer un conflit et vous ne souhaitez pas encore le gérer, alors vous pouvez simplement faire marche arrière avec git merge --abort.

$ git status -sb
## master
UU hello.rb

$ git merge --abort

$ git status -sb
## master

L’option git merge --abort essaie de vous ramener à l’état précédent la fusion. Les seuls cas dans lesquels il n’y parvient pas parfaitement seraient ceux pour lesquels vous aviez déjà auparavant des modifications non validées ou non remisées dans votre répertoire de travail au moment de la fusion. Sinon, tout devrait se passer sans problème.

Si, pour une raison quelconque, vous vous trouvez dans une situation horrible et que vous souhaitez repartir à zéro, vous pouvez aussi lancer git reset --hard HEAD ou sur toute autre référence où vous souhaitez revenir. Souvenez-vous tout de même que cela va balayer toutes les modifications de votre répertoire de travail, donc assurez-vous de n’avoir aucune modification de valeur avant.

Ignorer les caractères invisibles

Dans ce cas spécifique, les conflits sont dûs à des espaces blancs. Nous le savons parce que le cas est simple, mais cela reste assez facile à déterminer dans les cas réels en regardant les conflits parce que chaque ligne est supprimée puis réintroduite modifiée. Par défaut, Git voit toutes ces lignes comme modifiées et il ne peut pas fusionner les fichiers.

La stratégie de fusion par défaut accepte quand même des arguments, et certains d’entre eux traitent le cas des modifications impliquant les caractères blancs. Si vous vous rendez compte que vous avez de nombreux conflits de caractères blancs lors d’une fusion, vous pouvez simplement abandonner la fusion et en relancer une en utilisant les options -Xignore-all-space ou -Xignore-space-change. La première option ignore complètement tous les espaces tandis que la seconde traite les séquences d’un ou plusieurs espaces comme équivalentes.

$ git merge -Xignore-all-space whitespace
Fusion automatique de hello.rb
Merge made by the 'recursive' strategy.
 hello.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Puisque dans ce cas, les modifications réelles n’entraient pas en conflit, une fois les modifications d’espaces ignorées, tout fusionne parfaitement bien.

Ça sauve la vie si vous avez dans votre équipe une personne qui reformate tous les espaces en tabulations ou vice-versa.

Re-fusion manuelle d’un fichier

Bien que Git gère le pré-traitement d’espaces plutôt bien, il existe d’autres types de modifications que Git ne peut pas gérer automatiquement, mais dont la fusion peut être scriptable. Par exemple, supposons que Git n’ait pas pu gérer les espaces et que nous ayons dû résoudre le problème à la main.

Ce que nous devons réellement faire est de passer le fichier que nous cherchons à fusionner à travers dos2unix avant d’essayer de le fusionner réellement. Comment pourrions-nous nous y prendre ?

Premièrement, nous entrons dans l’état de conflit de fusion. Puis, nous voulons obtenir des copies de la version locale (ours), de la version distante (theirs, celle qui vient de la branche à fusionner) et de la version commune (l’ancêtre commun depuis lequel les branches sont parties). Ensuite, nous voulons corriger au choix la version locale ou la distante et réessayer de fusionner juste ce fichier.

Obtenir les trois versions des fichiers est en fait assez facile. Git stocke toutes ces versions dans l’index sous forme d’étapes (stages), chacune associée à un nombre. Stage 1 est l’ancêtre commun, stage 2 est notre version, stage 3 est la version de MERGE_HEAD, la version qu’on cherche à fusionner (theirs).

Vous pouvez extraire une copie de chacune de ces versions du fichier en conflit avec la commande git show et une syntaxe spéciale.

$ git show :1:hello.rb > hello.common.rb
$ git show :2:hello.rb > hello.ours.rb
$ git show :3:hello.rb > hello.theirs.rb

Si vous voulez rentrer un peu plus dans le dur, vous pouvez aussi utiliser la commande de plomberie ls-files -u pour récupérer les SHA-1 des blobs Git de chacun de ces fichiers.

$ git ls-files -u
100755 ac51efdc3df4f4fd328d1a02ad05331d8e2c9111 1	hello.rb
100755 36c06c8752c78d2aff89571132f3bf7841a7b5c3 2	hello.rb
100755 e85207e04dfdd5eb0a1e9febbc67fd837c44a1cd 3	hello.rb

La syntaxe :1:hello.rb est juste un raccourcis pour la recherche du SHA-1 de ce blob.

À présent que nous avons le contenu des trois étapes dans notre répertoire de travail, nous pouvons réparer manuellement la copie distante pour résoudre le problème d’espaces et re-fusionner le fichier avec la commande méconnue git merge-file dont c’est l’exacte fonction.

$ dos2unix hello.theirs.rb
dos2unix: converting file hello.theirs.rb to Unix format ...

$ git merge-file -p \
    hello.ours.rb hello.common.rb hello.theirs.rb > hello.rb

$ git diff -w
diff --cc hello.rb
index 36c06c8,e85207e..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,8 -1,7 +1,8 @@@
  #! /usr/bin/env ruby

 +# prints out a greeting
  def hello
-   puts 'hello world'
+   puts 'hello mundo'
  end

  hello()

À ce moment, nous avons un fichier joliment fusionné. En fait, cela fonctionne même mieux que l’option ignore-all-space parce que le problème d’espace est corrigé avant la fusion plutôt que simplement ignoré. Dans la fusion ignore-all-space, nous avons en fait obtenu quelques lignes contenant des fins de lignes DOS, ce qui a mélangé les styles.

Si vous voulez vous faire une idée avant de finaliser la validation sur ce qui a réellement changé entre un côté et l’autre, vous pouvez demander à git diff de comparer le contenu de votre répertoire de travail que vous êtes sur le point de valider comme résultat de la fusion avec n’importe quelle étape. Détaillons chaque comparaison.

Pour comparer votre résultat avec ce que vous aviez dans votre branche avant la fusion, en d’autres termes, ce que la fusion a introduit, vous pouvez lancer git diff --ours

$ git diff --ours
* Unmerged path hello.rb
diff --git a/hello.rb b/hello.rb
index 36c06c8..44d0a25 100755
--- a/hello.rb
+++ b/hello.rb
@@ -2,7 +2,7 @@

 # prints out a greeting
 def hello
-  puts 'hello world'
+  puts 'hello mundo'
 end

 hello()

Donc nous voyons ici que ce qui est arrivé à notre branche, ce que nous introduisons réellement dans ce fichier avec cette fusion, n’est qu’une ligne modifiée.

Si nous voulons voir le résultat de la fusion modifiée depuis la version distante, nous pouvons lancer git diff --theirs. Dans cet exemple et le suivant, nous devons utiliser -w pour éliminer les espaces parce que nous le comparons à ce qui est dans Git et non pas notre version nettoyée hello.theirs.rb du fichier.

$ git diff --theirs -w
* Unmerged path hello.rb
diff --git a/hello.rb b/hello.rb
index e85207e..44d0a25 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,5 +1,6 @@
 #! /usr/bin/env ruby

+# prints out a greeting
 def hello
   puts 'hello mundo'
 end

Enfin, nous pouvons voir comment le fichier a été modifié dans les deux branches avec git diff --base.

$ git diff --base -w
* Unmerged path hello.rb
diff --git a/hello.rb b/hello.rb
index ac51efd..44d0a25 100755
--- a/hello.rb
+++ b/hello.rb
@@ -1,7 +1,8 @@
 #! /usr/bin/env ruby

+# prints out a greeting
 def hello
-  puts 'hello world'
+  puts 'hello mundo'
 end

 hello()

À ce point, nous pouvons utiliser la commande git clean pour éliminer les fichiers supplémentaires maintenant inutiles que nous avons créés pour notre fusion manuelle.

$ git clean -f
Suppression de hello.common.rb
Suppression de hello.ours.rb
Suppression de hello.theirs.rb
Examiner les conflits

Peut-être ne sommes-nous pas heureux de la résolution actuelle, ou bien l’édition à la main d’un côté ou des deux ne fonctionne pas correctement et nécessite plus de contexte.

Modifions un peu l’exemple. Pour cet exemple, nous avons deux branches à longue durée de vie qui comprennent quelques commits mais créent des conflits de contenu légitimes à la fusion.

$ git log --graph --oneline --decorate --all
* f1270f7 (HEAD, master) update README
* 9af9d3b add a README
* 694971d update phrase to hola world
| * e3eb223 (mundo) add more tests
| * 7cff591 add testing script
| * c3ffff1 changed text to hello mundo
|/
* b7dcc89 initial hello world code

Nous avons maintenant trois commits uniques qui n’existent que sur la branche master et trois autres sur la branche mundo. Si nous essayons de fusionner la branche mundo, nous obtenons un conflit.

$ git merge mundo
Fusion automatique de hello.rb
CONFLIT (contenu): Conflit de fusion dans hello.rb
La fusion automatique a échoué ; réglez les conflits et validez le résultat.

Nous souhaitons voir ce qui constitue le conflit de fusion. Si nous ouvrons le fichier, nous verrons quelque chose comme :

#! /usr/bin/env ruby

def hello
<<<<<<< HEAD
  puts 'hola world'
======
  puts 'hello mundo'
>>>>>>> mundo
end

hello()

Les deux côtés de la fusion on ajouté du contenu au fichier, mais certains commits ont modifié le fichier au même endroit, ce qui a causé le conflit.

Explorons quelques outils que vous avez à disposition pour déterminer comment ce conflit est apparu. Peut-être le moyen de résoudre n’est-il pas évident. Il nécessite plus de contexte.

Un outil utile est git checkout avec l’option --conflict. Il va re-extraire le fichier et remplacer les marqueurs de conflit. Cela peut être utile si vous souhaitez éliminer les marqueurs et essayer de résoudre le conflit à nouveau.

Vous pouvez passer en paramètre à --conflict, soit diff3 soit merge (le paramètre par défaut). Si vous lui passez diff3, Git utilisera une version différente des marqueurs de conflit, vous fournissant non seulement les versions locales (ours) et distantes (theirs), mais aussi la version « base » intégrée pour vous fournir plus de contexte.

$ git checkout --conflict=diff3 hello.rb

Une fois que nous l’avons lancé, le fichier ressemble à ceci :

#! /usr/bin/env ruby

def hello
<<<<<<< ours
  puts 'hola world'
||||||| base
  puts 'hello world'
======
  puts 'hello mundo'
>>>>>>> theirs
end

hello()

Si vous appréciez ce format, vous pouvez le régler comme défaut pour les futur conflits de fusion en renseignant le paramètre merge.conflictstyle avec diff3.

$ git config --global merge.conflictstyle diff3

La commande git checkout peut aussi accepter les options --ours et --theirs, qui peuvent servir de moyen rapide de choisir unilatéralement une version ou une autre sans fusion.

Cela peut être particulièrement utile pour les conflits de fichiers binaires où vous ne pouvez que choisir un des côté, ou des conflits où vous souhaitez fusionner certains fichiers depuis d’autres branches - vous pouvez fusionner, puis extraire certains fichiers depuis un côté ou un autre avant de valider le résultat.

Journal de fusion

Un autre outil utile pour la résolution de conflits de fusion est git log. Cela peut vous aider à obtenir du contexte ce qui a contribué aux conflits. Parcourir un petit morceau de l’historique pour se rappeler pourquoi deux lignes de développement ont touché au même endroit dans le code peut s’avérer quelque fois très utile.

Pour obtenir une liste complète de tous les commits uniques qui ont été introduits dans chaque branche impliquée dans la fusion, nous pouvons utiliser la syntaxe « triple point » que nous avons apprise dans ch07-git-tools.asc.

$ git log --oneline --left-right HEAD...MERGE_HEAD
< f1270f7 update README
< 9af9d3b add a README
< 694971d update phrase to hola world
> e3eb223 add more tests
> 7cff591 add testing script
> c3ffff1 changed text to hello mundo

Voilà une belle liste des six commits impliqués, ainsi que chaque ligne de développement sur laquelle chaque commit se trouvait.

Néanmoins, nous pouvons simplifier encore plus ceci pour fournir beaucoup plus de contexte. Si nous ajoutons l’option --merge à git log, il n’affichera que les commits de part et d’autre de la fusion qui modifient un fichier présentant un conflit.

$ git log --oneline --left-right --merge
< 694971d update phrase to hola world
> c3ffff1 changed text to hello mundo

Si nous lançons cela avec l’option -p à la place, vous obtenez les diffs limités au fichier qui s’est retrouvé en conflit. Cela peut s’avérer vraiment utile pour vous donner le contexte nécessaire à la compréhension de la raison d’un conflit et à sa résolution intelligente.

Format de diff combiné

Puisque Git indexe tous les résultats de fusion couronnés de succès, quand vous lancez git diff dans un état de conflit de fusion, vous n’obtenez que ce qui toujours en conflit à ce moment. Il peut s’avérer utile de voir ce qui reste à résoudre.

Quand vous lancez git diff directement après le conflit de fusion, il vous donne de l’information dans un format de diff plutôt spécial.

$ git diff
diff --cc hello.rb
index 0399cd5,59727f0..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,7 -1,7 +1,11 @@@
  #! /usr/bin/env ruby

  def hello
++<<<<<<< HEAD
 +  puts 'hola world'
++=======
+   puts 'hello mundo'
++>>>>>>> mundo
  end

  hello()

Ce format s’appelle « diff combiné » (combined diff) et vous fournit deux colonnes d’information sur chaque ligne. La première colonne indique que la ligne est différente (ajoutée ou supprimée) entre la branche « ours » et le fichier dans le répertoire de travail. La seconde colonne fait de même pour la branche « theirs » et la copie du répertoire de travail.

Donc dans cet exemple, vous pouvez voir que les lignes <<<<<<< et >>>>>>> sont dans la copie de travail mais n’étaient dans aucun des deux côtés de la fusion. C’est logique parce que l’outil de fusion les a collés ici pour donner du contexte, mais nous devrons les retirer.

Si nous résolvons le conflit et relançons git diff, nous verrons la même chose, mais ce sera un peu plus utile.

$ vim hello.rb
$ git diff
diff --cc hello.rb
index 0399cd5,59727f0..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,7 -1,7 +1,7 @@@
  #! /usr/bin/env ruby

  def hello
-   puts 'hola world'
 -  puts 'hello mundo'
++  puts 'hola mundo'
  end

  hello()

Ceci nous montre que « hola world » était présent de notre côté mais pas dans la copie de travail, que « hello mundo » était présent de l’autre côté mais pas non plus dans la copie de travail et que finalement, « hola mundo » n’était dans aucun des deux côtés, mais se trouve dans la copie de travail. C’est particulièrement utile lors d’une revue avant de valider la résolution.

Vous pouvez aussi l’obtenir depuis git log pour toute fusion pour visualiser comment quelque chose a été résolu après coup. Git affichera ce format si vous lancez git show sur un commit de fusion, ou si vous ajoutez une option --cc à git log -p (qui par défaut ne montre que les patchs des commits qui ne sont pas des fusions).

$ git log --cc -p -1
commit 14f41939956d80b9e17bb8721354c33f8d5b5a79
Merge: f1270f7 e3eb223
Author: Scott Chacon <[email protected]>
Date:   Fri Sep 19 18:14:49 2014 +0200

    Merge branch 'mundo'

    Conflicts:
        hello.rb

diff --cc hello.rb
index 0399cd5,59727f0..e1d0799
--- a/hello.rb
+++ b/hello.rb
@@@ -1,7 -1,7 +1,7 @@@
  #! /usr/bin/env ruby

  def hello
-   puts 'hola world'
 -  puts 'hello mundo'
++  puts 'hola mundo'
  end

  hello()

Défaire des fusions

Comme vous savez créer des commits de fusion à présent, vous allez certainement en faire par erreur. Un des grands avantages de l’utilisation de Git est qu’il n’est pas interdit de faire des erreurs, parce qu’il reste toujours possible (et très souvent facile) de les corriger.

Les commits de fusion ne font pas exception. Supposons que vous avez commencé à travailler sur une branche thématique, que vous l’avez accidentellement fusionnée dans master et qu’en conséquence votre historique ressemble à ceci :

_Commit_ de fusion accidentel
Figure 1. Commit de fusion accidentel

Il existe deux façons d’aborder ce problème, en fonction du résultat que vous souhaitez obtenir.

Correction des références

Si le commit de fusion non désiré n’existe que dans votre dépôt local, la solution la plus simple et la meilleure consiste à déplacer les branches pour qu’elles pointent où on le souhaite. La plupart du temps, en faisant suivre le git merge malencontreux par un git reset --hard HEAD~, on remet les pointeurs de branche dans l’état suivant :

Historique après `git reset --hard HEAD~`
Figure 2. Historique après git reset --hard HEAD~

Nous avons détaillé reset dans ch07-git-tools.asc et il ne devrait pas être très difficile de comprendre ce résultat. Voici néanmoins un petit rappel : reset --hard réalise généralement trois étapes :

  1. Déplace la branche pointée par HEAD ; dans notre cas, nous voulons déplacer master sur son point avant la fusion (C6),

  2. Faire ressembler l’index à HEAD,

  3. Faire ressembler le répertoire de travail à l’index.

Le défaut de cette approche est qu’elle ré-écrit l’historique, ce qui peut être problématique avec un dépôt partagé. Reportez-vous à ch03-git-branching.asc pour plus d’information ; en résumé si d’autres personnes ont déjà les commits que vous ré-écrivez, il vaudrait mieux éviter un reset. Cette approche ne fonctionnera pas non plus si d’autres commits ont été créés depuis la fusion ; déplacer les références des branches éliminera effectivement ces modifications.

Inverser le commit

Si les déplacements des pointeurs de branche ne sont pas envisageables, Git vous donne encore l’option de créer un nouveau commit qui défait toutes les modifications d’un autre déjà existant. Git appelle cette option une « inversion » (revert), et dans ce scénario particulier, vous l’invoqueriez comme ceci :

$ git revert -m 1 HEAD
[master b1d8379] Revert "Merge branch 'topic'"

L’option -m 1 indique quel parent est le principal et devrait être conservé. Si vous invoquez une fusion dans HEAD (git merge topic), le nouveau commit a deux parents : le premier est HEAD (C6), et le second est le sommet de la branche en cours de fusion (C4). Dans ce cas, nous souhaitons défaire toutes les modifications introduites dans le parent numéro 2 (C4), tout en conservant tout le contenu du parent numéro 1 (C6).

L’historique avec le commit d’inversion ressemble à ceci :

Historique après `git revert -m 1`
Figure 3. Historique après git revert -m 1

Le nouveau commit ^M a exactement le même contenu que C6, et partant de là, c’est comme si la fusion n’avait pas eu lieu, mis à part que les commits qui ne sont plus fusionnés sont toujours dans l’historique de HEAD. Git sera confus si vous tentez de re-fusionner topic dans master :

$ git merge topic
Already up-to-date.

Il n’y a rien dans topic qui ne soit pas déjà joignable depuis master. Pire encore, si vous ajoutez du travail à topic et re-fusionnez, Git n’ajoutera que les modifications depuis la fusion inversée :

Historique avec une mauvaise fusion
Figure 4. Historique avec une mauvaise fusion

Le meilleur contournement de ceci est de dé-inverser la fusion originale, puisque vous voulez ajouter les modifications qui ont été annulées, puis de créer un nouveau commit de fusion :

$ git revert ^M
[master 09f0126] Revert "Revert "Merge branch 'topic'""
$ git merge topic
Historique après re-fusion de la fusion annulée
Figure 5. Historique après re-fusion de la fusion annulée

Dans cet exemple, M et ^M s’annulent. ^^M fusionne effectivement les modifications depuis C3 et C4, et C8 fusionne les modifications depuis C7, donc à présent, topic est totalement fusionnée.

Autres types de fusions

Jusqu’ici, nous avons traité les fusions normales entre deux branches qui ont été gérées normalement avec ce qui s’appelle la stratégie « récursive » de fusion. Il existe cependant d’autres manières de fusionner des branches. Traitons en quelques unes rapidement.

Préférence our ou theirs

Premièrement, il existe un autre mode utile que nous pouvons utiliser avec le mode « recursive » normal de fusion. Nous avons déjà vu les options ignore-all-space et ignore-space-change qui sont passées avec -X mais nous pouvons aussi indiquer à Git de favoriser un côté plutôt que l’autre lorsqu’il rencontre un conflit.

Par défaut, quand Git rencontre un conflit entre deux branches en cours de fusion, il va ajouter des marqueurs de conflit de fusion dans le code et marquer le fichier en conflit pour vous laisser le résoudre. Si vous préférez que Git choisisse simplement un côté spécifique et qu’il ignore l’autre côté au lieu de vous laisser fusionner manuellement le conflit, vous pouvez passer -Xours ou -Xtheirs à la commande merge.

Si une des options est spécifiée, Git ne va pas ajouter de marqueurs de conflit. Toutes les différences qui peuvent être fusionnées seront fusionnées. Pour toutes les différences qui génèrent un conflit, Git choisira simplement la version du côté que vous avez spécifié, y compris pour les fichiers binaires.

Si nous retournons à l’exemple « hello world » précédent, nous pouvons voir que la fusion provoque des conflits.

$ git merge mundo
Fusion automatique de  hello.rb
CONFLIT (contenu): Conflit de fusion dans hello.rb
La fusion automatique a échoué ; réglez les conflits et validez le résultat.

Cependant, si nous la lançons avec -Xours ou -Xtheirs, elle n’en provoque pas.

$ git merge -Xours mundo
Fusion automatique de hello.rb
Merge made by the 'recursive' strategy.
 hello.rb | 2 +-
 test.sh  | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)
 create mode 100644 test.sh

Dans ce dernier cas, au lieu d’obtenir des marqueurs de conflit dans le fichier avec « hello mundo » d’un côté et « hola world » de l’autre, Git choisira simplement « hola world ». À part cela, toutes les autres modifications qui ne génèrent pas de conflit sont fusionnées sans problème.

Cette option peut aussi être passée à la commande git merge-file que nous avons utilisée plus tôt en lançant quelque chose comme git merge-file --ours pour les fusions de fichiers individuels.

Si vous voulez faire quelque chose similaire mais indiquer à Git de ne même pas essayer de fusionner les modifications de l’autre côté, il existe une option draconienne qui s’appelle la stratégie de fusion « ours ».

Cela réalisera une fusion factice. Cela enregistrera un nouveau commit de fusion avec les deux branches comme parents, mais ne regardera même pas la branche en cours de fusion. Cela enregistrera simplement le code exact de la branche courante comme résultat de la fusion.

$ git merge -s ours mundo
Merge made by the 'ours' strategy.
$ git diff HEAD HEAD~
$

Vous pouvez voir qu’il n’y a pas de différence entre la branche sur laquelle nous étions précédemment et le résultat de la fusion.

Cela peut s’avérer utile pour faire croire à Git qu’une branche est déjà fusionnée quand on fusionne plus tard. Par exemple, disons que vous avez créé une branche depuis une branche « release » et avez travaillé dessus et que vous allez vouloir réintégrer ce travail dans master. Dans l’intervalle, les correctifs de master doivent être reportés dans la branche release. Vous pouvez fusionner la branche de correctif dans la branche release et aussi faire un merge -s ours de cette branche dans la branche master (même si le correctif est déjà présent) de sorte que lorsque fusionnerez plus tard la branche release , il n’y aura pas de conflit dû au correctif.