-
1. Erste Schritte
-
2. Git Grundlagen
-
3. Git Branching
- 3.1 Branches auf einen Blick
- 3.2 Einfaches Branching und Merging
- 3.3 Branch-Management
- 3.4 Branching-Workflows
- 3.5 Remote-Branches
- 3.6 Rebasing
- 3.7 Zusammenfassung
-
4. Git auf dem Server
- 4.1 Die Protokolle
- 4.2 Git auf einem Server einrichten
- 4.3 Erstellung eines SSH-Public-Keys
- 4.4 Einrichten des Servers
- 4.5 Git-Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Von Drittanbietern gehostete Optionen
- 4.10 Zusammenfassung
-
5. Verteiltes Git
-
6. GitHub
-
7. Git Tools
- 7.1 Revisions-Auswahl
- 7.2 Interaktives Stagen
- 7.3 Stashen und Bereinigen
- 7.4 Deine Arbeit signieren
- 7.5 Suchen
- 7.6 Den Verlauf umschreiben
- 7.7 Reset entzaubert
- 7.8 Fortgeschrittenes Merging
- 7.9 Rerere
- 7.10 Debuggen mit Git
- 7.11 Submodule
- 7.12 Bundling
- 7.13 Replace (Ersetzen)
- 7.14 Anmeldeinformationen speichern
- 7.15 Zusammenfassung
-
8. Git einrichten
- 8.1 Git Konfiguration
- 8.2 Git-Attribute
- 8.3 Git Hooks
- 8.4 Beispiel für Git-forcierte Regeln
- 8.5 Zusammenfassung
-
9. Git und andere VCS-Systeme
- 9.1 Git als Client
- 9.2 Migration zu Git
- 9.3 Zusammenfassung
-
10. Git Interna
-
A1. Anhang A: Git in anderen Umgebungen
- A1.1 Grafische Schnittstellen
- A1.2 Git in Visual Studio
- A1.3 Git in Visual Studio Code
- A1.4 Git in IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine
- A1.5 Git in Sublime Text
- A1.6 Git in Bash
- A1.7 Git in Zsh
- A1.8 Git in PowerShell
- A1.9 Zusammenfassung
-
A2. Anhang B: Git in deine Anwendungen einbetten
- A2.1 Die Git-Kommandozeile
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Anhang C: Git Kommandos
- A3.1 Setup und Konfiguration
- A3.2 Projekte importieren und erstellen
- A3.3 Einfache Snapshot-Funktionen
- A3.4 Branching und Merging
- A3.5 Projekte gemeinsam nutzen und aktualisieren
- A3.6 Kontrollieren und Vergleichen
- A3.7 Debugging
- A3.8 Patchen bzw. Fehlerkorrektur
- A3.9 E-mails
- A3.10 Externe Systeme
- A3.11 Administration
- A3.12 Basisbefehle
7.9 Git Tools - Rerere
Rerere
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 true
Du 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'
end
In 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.rb
Ein 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
end
Auß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.rb
Jetzt 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'
end
Das 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 Reset entzaubert beschrieben haben.
$ git reset --hard HEAD^
HEAD is now at ad63f15 i18n the hello
Unser 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 word
Nun 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'
end
Zudem 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'
end
Du 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
end
Ein Beispiel dafür haben wir in Fortgeschrittenes Merging 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'
end
Wir 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 word
Wenn 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.