Der Befehl git rerere ist eine eher versteckte Funktion.
Der Name steht für „reuse recorded resolution“ (dt. „gespeicherte Ergebnisse wiederverwenden“). Der Name bedeutet, dass du Git auffordern kannst sich zu erinnern, wie du einen bestimmten Konflikt in der Vergangenheit gelöst hast. Wenn Git das nächste Mal den gleichen Konflikt sieht, kann es ihn automatisch lösen.
Es gibt eine Reihe von Szenarien, in denen diese Funktionalität wirklich nützlich sein kann.
Eines der Beispiele, das in der Dokumentation erwähnt wird, ist sicher zu stellen, dass ein langlebiger Feature-Branch am Ende sauber gemerged wird. Dabei willst du jedoch nicht, dass eine Menge zwischenzeitlicher Merge-Commits deine Commit-Historie durcheinander bringt.
Wenn rerere aktiviert ist, kannst du ab und zu einen Merge starten, die Konflikte lösen und dann den Merge-Prozess stoppen.
Falls du das kontinuierlich tust, sollte der finale Merge recht unkompliziert sein, denn rerere kann alles für dich automatisch erledigen.
Dieselbe Vorgehensweise kann angewendet werden, wenn du einen Branch rebased, damit du dich nicht jedes Mal mit denselben Konflikten beim Rebase auseinandersetzen musst. Oder wenn du einen Branch, den du schon gemerged und eine Reihe von Konflikten behoben hast dich dann jedoch ein Rebase entscheidest. Dann musst du wahrscheinlich nicht alle Konflikte nochmal lösen.
Eine weitere Einsatzmöglichkeit von `rerere` ist, wenn man eine Reihe sich fortentwickelnden Feature-Branches gelegentlich zu einem testbarem Head zusammenfügt, so wie es das Git-Projekt oft selbst praktiziert. Wenn die Tests fehlschlagen, kannst du die Merges rückgängig machen und sie ohne den fehlerhaften Feature-Branch, erneut starten, ohne die Konflikte erneut auflösen zu müssen.
Um die Funktion rerere zu aktivieren, musst du nur die folgende Config-Einstellung verwenden:
$ git config --global rerere.enabled trueDu kannst sie auch einschalten, indem du das Verzeichnis .git/rr-cache in einem konkreten Repository erstellst. Die Konfigurationseinstellung ist allerdings eindeutiger und aktiviert diese Funktion global.
Sehen wir uns nun ein einfaches Beispiel an, das unserem vorherigen ähnlich ist.
Nehmen wir an, wir haben eine Datei namens hello.rb, die so aussieht:
#! /usr/bin/env ruby
def hello
puts 'hello world'
endIn dem einen Branch ändern wir das Wort „hello“ in „hola“, in dem anderen Branch ändern wir „world“ in „mundo“, wie gehabt.
Wenn wir beiden Branches mergen, bekommen wir einen Merge-Konflikt:
$ git merge i18n-world
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Recorded preimage for 'hello.rb'
Automatic merge failed; fix conflicts and then commit the result.Beachte die neue Zeile Recorded preimage for FILE.
Der Rest sollte genauso wie bei ein normaler Merge-Konflikt aussehen.
An dieser Stelle kann rerere uns ein paar Dinge sagen.
Normalerweise kannst du an diesem Punkt git status ausführen, um alle Konflikte zu sehen:
$ 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
#Allerdings wird dir git rerere auch mitteilen, was es im Pre-Merge Status mit git rerere status aufgezeichnet hat:
$ git rerere status
hello.rbEin git rerere diff zeigt den aktuellen Status der Lösung – womit du angefangen hast und welche Lösung du gefunden hast.
$ 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
endAußerdem (und das hat nicht wirklich etwas mit rerere zu tun) kannst du git ls-files -u verwenden, um dir die in Konflikt stehenden Dateien anzusehen (inklusive der vorherigen, linken und rechten Version):
$ git ls-files -u
100644 39804c942a9c1f2c03dc7c5ebcd7f3e3a6b97519 1 hello.rb
100644 a440db6e8d1fd76ad438a49025a9ad9ce746f581 2 hello.rb
100644 54336ba847c3758ab604876419607e9443848474 3 hello.rbJetzt kannst du es einfach zu puts 'hola mundo' auflösen. Dann kannst du noch einmal git rerere diff starten, um zu sehen, woran rerere sich erinnern wird:
$ 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'
endDas heißt im Grunde genommen: wenn Git in einer hello.rb Datei, die „hello mundo“ auf der einen Seite und „hola world“ auf der anderen Seite enthält, einen Konflikt erkennt und ihn zu „hola mundo“ auflöst.
Jetzt können wir ihn als gelöst markieren und committen:
$ git add hello.rb
$ git commit
Recorded resolution for 'hello.rb'.
[master 68e16e5] Merge branch 'i18n'Du kannst sehen, dass es die „Lösung für DATEI gespeichert hat“ (Recorded resolution for FILE).
Machen wir jetzt diesen Merge rückgängig und legen ihn stattdessen dann auf unseren Branch master.
Wir können unseren Branch zurücksetzen, indem wir git reset anwenden, wie wir es in ch07-git-tools.asc beschrieben haben.
$ git reset --hard HEAD^
HEAD is now at ad63f15 i18n the helloUnser Merge wurde rückgängig gemacht. Lass uns jetzt den Feature-Branch rebasen.
$ git checkout i18n-world
Switched to branch 'i18n-world'
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: i18n one word
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging hello.rb
CONFLICT (content): Merge conflict in hello.rb
Resolved 'hello.rb' using previous resolution.
Failed to merge in the changes.
Patch failed at 0001 i18n one wordNun haben wir den erwarteten Merge-Konflikt, aber schaue dir die Zeile Resolved FILE using previous resolution an.
Wenn wir die Datei betrachten, sehen wir, dass der Konflikt bereits gelöst ist. Es gibt keine Marker für den Merge-Konflikt in der Datei.
#! /usr/bin/env ruby
def hello
puts 'hola mundo'
endZudem wird dir git diff zeigen, wie es erneut automatisch gelöst wurde:
$ 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'
endDu kannst den Status der Konfliktdatei auch mit git checkout wiederherstellen:
$ git checkout --conflict=merge hello.rb
$ cat hello.rb
#! /usr/bin/env ruby
def hello
<<<<<<< ours
puts 'hola world'
=======
puts 'hello mundo'
>>>>>>> theirs
endEin Beispiel dafür haben wir in ch07-git-tools.asc kennengelernt.
Vorerst sollten wir das Problem dadurch lösen, dass wir git rerere noch einmal starten:
$ git rerere
Resolved 'hello.rb' using previous resolution.
$ cat hello.rb
#! /usr/bin/env ruby
def hello
puts 'hola mundo'
endWir haben die Datei automatisch mit der mit rerere zwischengespeicherten Lösung erneut gelöst.
Du kannst es nun hinzufügen und den Rebase fortsetzen, um ihn fertigzustellen.
$ git add hello.rb
$ git rebase --continue
Applying: i18n one wordWenn du also viele Re-Merges machst oder einen Topic-Branch mit deinem Branch master aktuell halten willst, ohne dass eine Unmenge von Merges durchgeführt werden sollen. Oder wenn du häufig einen Rebase machst, solltest du rerere aktivieren, um dir das Leben ein wenig leichter zu machen.


