Chapters ▾ 2nd Edition

7.9 Git Tools - Rerere

Rerere

Die git rerere funksionaliteit is 'n bietjie van 'n versteekte kenmerk. Die naam staan vir “reuse recorded resolution” (hergebruik opgeneemde resolusie) en, soos die naam aandui, laat dit jou toe om Git te vra om te onthou hoe jy 'n brok-konflik (hunk conflict) opgelos het sodat die volgende keer as dit dieselfde konflik sien, Git dit outomaties vir jou kan oplos.

Daar is 'n aantal scenario’s waarin hierdie funksionaliteit regtig handig kan wees. Een van die voorbeelde wat in die dokumentasie genoem word, is wanneer jy wil seker maak dat 'n langlewende onderwerp-tak (topic branch) uiteindelik skoon sal saamsmelt, maar jy wil nie 'n klomp intermediêre saamsmeltingsvasleggings (merge commits) hê wat jou vasleggingsgeskiedenis rommelig maak nie. Met rerere geaktiveer, kan jy af en toe 'n saamsmelting probeer, die konflikte oplos, en dan uit die saamsmelting onttrek. As jy dit deurlopend doen, behoort die finale saamsmelting maklik te wees omdat rerere net alles outomaties vir jou kan doen.

Dieselfde taktiek kan gebruik word as jy 'n tak gerebase wil hou sodat jy nie elke keer met dieselfde herbaseringskonflikte (rebasing conflicts) hoef te doen te kry as jy dit doen nie. Of as jy 'n tak wil neem wat jy saamgesmelt het en 'n klomp konflikte opgelos het en dan besluit om dit eerder te rebase — jy sal waarskynlik nie weer al dieselfde konflikte hoef te doen nie.

Nog 'n toepassing van rerere is waar jy af en toe 'n klomp ontwikkelende onderwerp-takke saamsmelt in 'n toetsbare kop (testable head), soos die Git-projek self dikwels doen. As die toetse misluk, kan jy die saamsmeltings terugspoel (rewind) en dit oordoen sonder die onderwerp-tak wat die toetse laat misluk het, sonder om weer die konflikte te moet heroplos.

Om rerere funksionaliteit te aktiveer, moet jy eenvoudig hierdie konfigurasie-instelling uitvoer:

$ git config --global rerere.enabled true

Jy kan dit ook aanskakel deur die .git/rr-cache gids in 'n spesifieke bewaarplek te skep, maar die konfigurasie-instelling is duideliker en aktiveer daardie kenmerk globaal vir jou.

Kom ons kyk nou na 'n eenvoudige voorbeeld, soortgelyk aan ons vorige een. Kom ons sê ons het 'n lêer genaamd hello.rb wat so lyk:

#! /usr/bin/env ruby

def hello
  puts 'hello world'
end

In een tak verander ons die woord “hello” na “hola”, dan in 'n ander tak verander ons die “world” na “mundo”, net soos voorheen.

Two branches changing the same part of the same file differently
Figure 173. Twee takke wat dieselfde deel van dieselfde lêer verskillend verander

Wanneer ons die twee takke saamsmelt, sal ons 'n saamsmeltingskonflik kry:

$ 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.

Jy behoort die nuwe reël Recorded preimage for 'hello.rb' daarin op te merk. Andersins behoort dit presies soos 'n normale saamsmeltingskonflik te lyk. Op hierdie punt kan rerere vir ons 'n paar dinge vertel. Normaalweg sou jy dalk op hierdie punt git status uitvoer om te sien wat alles gebots het (conflicted):

$ 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
#

git rerere sal jou egter ook vertel waarvoor dit die voor-saamsmelting toestand opgeneem het met git rerere status:

$ git rerere status
hello.rb

En git rerere diff sal die huidige toestand van die resolusie wys — waarmee jy begin het om op te los en waarna jy dit opgelos het.

$ 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

Ook (en dit is nie regtig verwant aan rerere nie), kan jy git ls-files -u gebruik om die botsende lêers en die voor-, linker- en regterweergawes te sien:

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

Nou kan jy dit oplos om net puts 'hola mundo' te wees en jy kan weer git rerere diff uitvoer om te sien wat rerere sal onthou:

$ 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

Dit sê dus basies, wanneer Git 'n brok-konflik in 'n hello.rb lêer sien wat “hello mundo” aan die een kant het en “hola world” aan die ander kant, sal dit dit na “hola mundo” oplos.

Nou kan ons dit as opgelos merk en vaslê:

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

Jy kan sien dat dit aandui "Recorded resolution for 'hello.rb'".

Recorded resolution for FILE
Figure 174. Opgeneemde resolusie vir lêer

Kom ons maak nou daardie saamsmelting ongedaan en rebase dit in plaas daarvan bo-op ons master tak. Ons kan ons tak terugskuif deur git reset te gebruik, soos ons gesien het in Reset Ontmystifiseer (Reset Demystified).

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

Ons saamsmelting is ongedaan gemaak. Kom ons rebase nou die onderwerp-tak.

$ 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 word

Nou het ons dieselfde saamsmeltingskonflik gekry soos ons verwag het, maar kyk na die Resolved 'hello.rb' using previous resolution reël. As ons na die lêer kyk, sal ons sien dat dit reeds opgelos is, daar is geen saamsmeltingskonflikmerkers daarin nie.

#! /usr/bin/env ruby

def hello
  puts 'hola mundo'
end

Ook sal git diff jou wys hoe dit outomaties heropgelos (re-resolved) is:

$ 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
Automatically resolved merge conflict using previous resolution
Figure 175. Outomaties opgeloste saamsmeltingskonflik met behulp van vorige resolusie

Jy kan ook die botsende lêertoestand herskep met git 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

Ons het 'n voorbeeld hiervan gesien in Gevorderde Saamsmelting (Advanced Merging). Vir nou, kom ons heroplos dit deur net weer git rerere uit te voer:

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

def hello
  puts 'hola mundo'
end

Ons het die lêer outomaties heropgelos deur gebruik te maak van die rerere gekaste (cached) resolusie. Jy kan nou byvoeg en voortgaan met die rebase om dit te voltooi.

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

So, as jy baie hersaamsmeltings doen, of 'n onderwerp-tak op datum wil hou met jou master tak sonder 'n magdom saamsmeltings, of jy dikwels rebase, kan jy rerere aanskakel om jou lewe 'n bietjie makliker te maak.