Git 🌙
Chapters â–Ÿ 2nd Edition

7.9 Utilitaires Git - Rerere

Rerere

La fonctionalitĂ© git rerere est une fonction un peu cachĂ©e. Le nom vient de l’anglais reuse recorded resolution (« rĂ© utiliser les rĂ© solutions en re gistrĂ©es ») et comme son nom l’indique, cela permet de demander Ă  Git de se souvenir comment vous avez rĂ©solu un conflit sur une section de diff de maniĂšre que la prochaine fois qu’il rencontre le mĂȘme conflit, il le rĂ©solve automatiquement pour vous.

Il existe pas mal de scĂ©narios pour lesquels cette fonctionalitĂ© peut se montrer efficace. Un exemple mentionnĂ© dans la documentation cite le cas d’une branche au long cours qui finira par fusionner proprement mais ne souhaite pas montrer des fusions intermĂ©diaires. Avec rerere activĂ©, vous pouvez fusionner de temps en temps, rĂ©soudre les conflits, puis sauvegarder la fusion. Si vous faites ceci en continu, alors la derniĂšre fusion devrait ĂȘtre assez facile parce que rerere peut quasiment tout faire automatiquement pour vous.

La mĂȘme tactique peut ĂȘtre utilisĂ©e si vous souhaitez rebaser plusieurs fois une branche tout en ne souhaitant pas avoir Ă  gĂ©rer les mĂȘmes conflits de rebasage Ă  chaque fois. Ou si vous voulez prendre la branche que vous avez fusionnĂ©e et si vous avez eu Ă  corriger des conflits, puis dĂ©cidez de la rebaser pour finir - vous souhaitez sĂ»rement ne pas avoir Ă  recorriger les mĂȘmes conflits.

Une autre situation similaire apparaĂźt quand vous fusionnez ensemble de temps en temps une sĂ©rie de branches thĂ©matiques Ă©volutives dans un sommet testable, comme le projet Git lui-mĂȘme le fait souvent. Si les tests Ă©chouent, vous pouvez rembobiner vos fusions et les rejouer en Ă©cartant la branche qui a provoquĂ© l’erreur sans devoir rĂ©soudre Ă  nouveau tous les conflits.

Pour activer la fonctionnalité rerere, vous devez simplement lancer le paramétrage :

$ git config --global rerere.enabled true

Vous pouvez aussi l’activer en crĂ©ant le rĂ©pertoire .git/rr-cache dans un dĂ©pĂŽt spĂ©cifique, mais l’activation par ligne de commande reste plus claire et permet d’activer la fonction globalement.

Voyons maintenant un exemple similaire au précédent. Supposons que nous avons un fichier qui contient ceci :

#! /usr/bin/env ruby

def hello
  puts 'hello world'
end

Dans une branche, nous changeons « hello » en « hola », puis dans une autre branche nous changeons « world » en « mundo », comme précédemment.

rerere1

Quand nous fusionnons les deux branches ensemble, nous obtenons un conflit de fusion :

$ git merge i18n-world
Fusion automatique de hello.rb
CONFLIT (contenu): Conflit de fusion dans hello.rb
Recorded preimage for 'hello.rb'
La fusion automatique a échoué ; réglez les conflits et validez le résultat.

Vous devriez avoir notĂ© la prĂ©sence d’un nouvelle ligne Recorded preimage for FILE (« Enregistrement de la prĂ©-image pour FICHIER »). À part ce dĂ©tail, cela ressemble Ă  un conflit de fusion tout Ă  fait normal. À ce stade, rerere peut dĂ©jĂ  nous dire un certain nombre de choses. Normalement, vous lanceriez un git status pour voir l’état actuel des conflits.

$ git status
# On branch master
# Unmerged paths:
#   (use "git reset HEAD <file>..." to unstage)
#   (use "git add <file>..." to mark resolution)
#
#	both modified:      hello.rb
#

Cependant, git rerere vous indiquera aussi les conflits pour lesquels il a enregistré la pré-image grùce à git rerere status :

$ git rerere status
hello.rb

Et git rerere diff montrera l’état actuel de la rĂ©solution ‑ quel Ă©tait le conflit de dĂ©part et comment vous l’avez rĂ©solu.

$ git rerere diff
--- a/hello.rb
+++ b/hello.rb
@@ -1,11 +1,11 @@
 #! /usr/bin/env ruby

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

En complĂ©ment (et bien que ça n’ait pas vraiment Ă  voir avec rerere), vous pouvez utiliser ls-files -u pour voir les fichiers en conflit ainsi que les versions prĂ©cĂ©dentes, Ă  droite et Ă  gauche :

$ git ls-files -u
100644 39804c942a9c1f2c03dc7c5ebcd7f3e3a6b97519 1	hello.rb
100644 a440db6e8d1fd76ad438a49025a9ad9ce746f581 2	hello.rb
100644 54336ba847c3758ab604876419607e9443848474 3	hello.rb

Maintenant, vous pouvez le résoudre pour que la ligne de code soit simplement puts 'hola mundo' et vous pouvez relancer la commande rerere diff pour visualiser ce que rerere va mémoriser :

$ git rerere diff
--- a/hello.rb
+++ b/hello.rb
@@ -1,11 +1,7 @@
 #! /usr/bin/env ruby

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

Cela indique simplement que quand Git voit un conflit de section dans un fichier hello.rb qui contient « hello mundo » d’un cĂŽtĂ© et « hola world » de l’autre, il doit rĂ©soudre ce conflit en « hola mundo ».

Maintenant, nous pouvons le marquer comme résolu et le valider :

$ git add hello.rb
$ git commit
Recorded resolution for 'hello.rb'.
[master 68e16e5] Merge branch 'i18n'

Vous pouvez voir qu’il a « enregistrĂ© la rĂ©solution pour FICHIER » (Recorded resolution for FILE).

rerere2

Maintenant, défaisons la fusion et rebasons plutÎt la branche sur la branche master. Nous pouvons déplacer notre branche en arriÚre en utilisant reset comme vu dans Reset démystifié.

$ git reset --hard HEAD^
HEAD is now at ad63f15 i18n the hello

Notre fusion est défaite. Rebasons notre branche thématique.

$ git checkout i18n-world
Basculement sur la branche 'i18n-world'

$ git rebase master
PremiĂšrement, rembobinons head pour rejouer votre travail par-dessus...
Application : i18n world
Utilisation de l'information de l'index pour reconstruire un arbre de base...
M       hello.rb
Retour Ă  un patch de la base et fusion Ă  3 points...
Fusion automatique de hello.rb
CONFLIT (contenu) : Conflit de fusion dans hello.rb
Resolved 'hello.rb' using previous resolution.
Échec d'intĂ©gration des modifications.
Le patch a échoué à 0001 i18n world

Ici, nous avons obtenu le conflit de fusion auquel nous nous attendions, mais des lignes supplémentaires sont apparues, en particulier Resolved FILE using previous resolution (FICHIER résolu en utilisant une résolution précédente). Si nous inspectons le fichier hello.rb, il ne contient pas de marqueur de conflit.

$ cat hello.rb
#! /usr/bin/env ruby

def hello
  puts 'hola mundo'
end

git diff nous montrera comment le conflit a été re-résolu automatiquement :

$ git diff
diff --cc hello.rb
index a440db6,54336ba..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
rerere3

Vous pouvez aussi recrĂ©er l’état de conflit du fichier avec la commande checkout :

$ git checkout --conflict=merge hello.rb
$ cat hello.rb
#! /usr/bin/env ruby

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

Nous avons vu un exemple de ceci dans Fusion avancée. Pour le moment, re-résolvons-le en relançant rerere :

$ git rerere
Resolved 'hello.rb' using previous resolution.
$ cat hello.rb
#! /usr/bin/env ruby

def hello
  puts 'hola mundo'
end

Nous avons re-résolu le conflit du fichier automatiquement en utilisant la résolution mémorisée par rerere. Vous pouvez le valider avec add et terminer de rebaser.

$ git add hello.rb
$ git rebase --continue
Application: i18n one word

Dans les cas oĂč vous souhaitez rĂ©aliser de nombreuses fusions successives d’une branche thĂ©matique ou si vous souhaitez la synchroniser souvent avec master sans devoir gĂ©rer des tas de conflits de fusion, ou encore si vous rebasez souvent, vous pouvez activer rerere qui vous simplifiera la vie.

scroll-to-top