Git leicht gemacht: Verlauf mit Rebase ändern – Teil I
Or Kamara
9. Juni 2020
0 Min. LesezeitVor Kurzem habe ich an einem umfangreichen Feature gearbeitet, das mehrere Änderungen in vielen Bereichen des Codes umfasste. Damit der PR für dieses Feature gut lesbar blieb, habe ich die gesamte Funktionalität auf mehrere Commits aufgeteilt. Schon bald merkte ich, dass diese Aufteilung nicht ausreichte: In jedem dieser Commits musste ich noch zahlreiche Änderungen vornehmen – etwa wegen Problemen, die ich gefunden hatte, oder aufgrund von CR-Fixes.
Zu diesem Zeitpunkt kannte ich bereits die Grundlagen des magischen interaktiven git-rebase-Befehls, mit dem sich der Verlauf „umschreiben“ lässt. Die nötigen Änderungen waren jedoch komplexer, als es meine Grundkenntnisse zuließen. Deshalb begann ich, mich näher mit den Optionen dieses Befehls zu beschäftigen.
Falls Sie den Befehl noch gar nicht kennen: So sieht die Ausgabe nach dem Ausführen von git rebase -i BRANCH aus:
Ich erinnere mich noch genau daran, wie ich diesen Befehl zum ersten Mal ausgeführt habe. Meine Reaktion war ungefähr: Okayyyy, was geht denn hier vor? Jemand hatte mir eine interaktive und einfache Möglichkeit versprochen, meine Commits zu ändern, aber ich sah nur eine sehr komplizierte Oberfläche … Ich musste mir wirklich Zeit nehmen, um mich über jede einzelne Option zu informieren, denn nachdem ich diesen Bildschirm verlassen hatte, hätte ich all meine Arbeit verlieren können (#ScaredDeveloper).
Selbst den Bildschirm zu schließen und alles abzubrechen, war nicht einfach … Ich musste googeln, wie das geht, und fand heraus, dass ich einfach alle Commit-Zeilen löschen musste, damit nichts geändert wird – so einfach (und hässlich). Mein erster Gedanke war: „Moment, ich vertraue nicht unbedingt allem, was ich bei Google lese. Was, wenn ich das mache und meine ganze Arbeit verschwindet?“
Mir wurde klar, dass mir nichts anderes übrig blieb – also begann ich, diese Befehle genauer zu untersuchen.
In diesem Artikel möchte ich auf verschiedene Probleme eingehen, die Entwickler:innen bei der Arbeit an PRs mit mehreren Commits lösen müssen. Die einzelnen Bestandteile dieser Befehle erkläre ich nicht, denn es gibt zahlreiche Artikel, die erläutern, was im Hintergrund passiert. Stattdessen stelle ich einige Szenarien vor, die uns allen bei der Arbeit mit git begegnen können, und zeige jeweils die passende Lösung mit git rebase -i.
Da das Thema recht umfangreich ist, teile ich diesen Blogbeitrag in zwei Teile auf. In diesem Teil erkläre ich die Grundlagen von Rebase und stelle zwei gängige Szenarien vor. Im zweiten Teil zeige ich vier weitere typische Szenarien und ziehe ein Fazit.
Aber genug der Vorrede – starten wir mit dem ersten Teil!
Rebase kurz erklärt
Betrachten wir folgendes Szenario: Sie und ein Freund arbeiten an einem spannenden neuen Projekt. Sie haben gerade das Repo erstellt und den ersten Commit mit dem Namen A angelegt. Jetzt geht es ans Programmieren. Um die Arbeit effizienter zu gestalten, teilen Sie sie auf: Sie sind für das Haupt-Feature zuständig, Ihr Freund entwickelt das Framework der App.
Sie starten also beide beim selben Punkt: Commit A. Nach fünf Stunden intensiver Arbeit möchten Sie Ihre Änderungen fertigstellen und in master zusammenführen. Ihr Freund führt als Erster einen Merge durch und erstellt dabei auf Basis von A drei neue Commits: B + C + D. Nun sind Sie an der Reihe, Ihre gesamte Arbeit aus dem FeatureBranch in master zu übernehmen: die Commits E + F + G. So sieht das aus:
Um die von Ihnen entwickelte Funktionalität zu testen, müssen Sie einige Funktionen des Frameworks verwenden, genauer gesagt aus Commit D. Mit anderen Worten: Sie müssen die Basis Ihres Branches von Commit A auf Commit D ändern – so, als hätten Sie Ihren Branch von Anfang an auf dem neuesten Stand erstellt:
Genau dafür brauchen wir git rebase: um eine Reihe von Commits auf eine neue Basis zu verschieben. Im Hintergrund erstellt git neue Commits und wendet sie auf die angegebene neue Basis an.
Genauer gesagt geht es in diesem Artikel um den interaktiven Modus.
Wenn Sie rebase mit dem Flag -i ausführen, können Sie einzelne Commits im Rahmen des grundlegenden Rebase-Prozesses ändern – hier beginnt die Magie! Im interaktiven Modus können Sie für jeden Ihrer Commits die gewünschte Aktion festlegen: zusammenführen, aufteilen, umbenennen, neu anordnen und mehr.
Anhand gängiger Szenarien, in denen git rebase nützlich sein kann, sehen wir uns die einzelnen Optionen genauer an.
Typische Szenarien (und wie Sie damit umgehen!)
Sehen wir uns einige typische Szenarien an, in denen Sie den Verlauf Ihres Codes ändern möchten, und erfahren Sie, wie das mit Git geht:
1. Eine alte Commit-Nachricht ändern (reword)
Sie haben mehrere Codeänderungen vorgenommen und perfekt auf verschiedene Commits verteilt. Dann fällt Ihnen auf, dass eine der Commit-Nachrichten einen Tippfehler enthält:
Wie Sie wahrscheinlich sehen, lautet die Nachricht des zweiten Commits 3nd commit (hoppla) – das korrigieren wir jetzt.
Wäre der Tippfehler im letzten Commit, könnten wir den Befehl git commit --amend verwenden. Da er sich jedoch in der Mitte unseres git-Graphen befindet, müssen wir etwas Komplizierteres tun.
In diesem Fall verwenden wir den Befehl git rebase -i master (master kann auch ein anderer Branch sein) und wählen für den zweiten Commit (a21bd1d) die Option reword:
Nachdem Sie die Datei gespeichert haben, öffnet sich ein neuer Editor, in dem Sie die Commit-Nachricht bearbeiten können:
Ändern Sie nun die erste Zeile in die gewünschte neue Commit-Nachricht, feat: 2nd commit, und speichern Sie die Datei erneut.
Der neue git-Graph ist jetzt korrigiert:
2. Den Inhalt eines alten Commits ändern (squash, fixup)
Sie haben CR-Kommentare von einem Kollegen erhalten und müssen einige kleine Änderungen vornehmen: eine neue Datei hinzufügen und den Inhalt einer vorhandenen Datei ändern.
Wie Sie wahrscheinlich wissen, sollte jeder Commit einen logischen Zusammenhang abbilden und nicht einfach nur Dateiänderungen enthalten. Wir sollten vermeiden, Commit-Nachrichten wie CR fixes oder in unserem Fall sogar really small CR fixes zu verwenden. Besser ist es, alle Commits zusammenzuführen, sodass jeder Commit am Ende alle relevanten Änderungen enthält – einschließlich der CR-Fixes.
Wie geht das? Hier kommen die Flags squash und fixup ins Spiel.
Erstellen Sie zunächst temporäre Commits mit den erforderlichen Änderungen:
Im obigen Beispiel haben wir zwei neue Commits erstellt:
5641a86– ändert den Inhalt der Datei, die wir im ersten Commit erstellt haben17a73c1– fügt dem zweiten Commit eine neue Datei hinzu
Wir möchten jedoch vermeiden, dass der git-Graph so aussieht. Stattdessen wollen wir 5641a86 mit 271c66e und 17a73c1 mit e2952e7 zusammenführen. Dazu führen wir den Befehl git rebase -i master aus. Wie beim letzten Mal sehen wir zunächst diesen Bildschirm:
Ändern wir nun die Reihenfolge der Zeilen entsprechend der gewünschten Reihenfolge der Commits und verwenden die Option squash für die Commits 5641a86 und 17a73c1:
Aufgrund dieser Änderungen führt git die Commits aus – von 271c66e bis fdb5f71 – und fügt den Inhalt der mit squash markierten Commits in den jeweils vorherigen Commit ein. Der git-Graph sieht nun genau so aus, wie wir es wollten!
Anstelle von squash können wir auch fixup verwenden. Beide Optionen bewirken im Wesentlichen dasselbe. Der Unterschied: fixup verwirft die Commit-Protokollnachricht.
Ein wichtiger Hinweis: Beim Zusammenführen von Commits kann es manchmal zu Code-Konflikten kommen. Wenn git Konflikte feststellt, müssen Sie diese beheben, die Änderungen stagen und den Rebase-Vorgang mit git rebase --continue fortsetzen. Wenn Sie unsicher sind, können Sie jederzeit git rebase --abort ausführen. Dieser Befehl beendet den Rebase-Vorgang und setzt Ihr Repository auf den ursprünglichen Stand zurück – also auf den Stand vor dem Ausführen von git rebase -i.
Zusammenfassung
Das war der erste Teil unserer Anleitung dazu, wie Sie Ihren git-Verlauf mit Rebase ändern können! Im zweiten Teil behandeln wir vier weitere typische Szenarien und geben Tipps für den Umgang damit.
Starten Sie mit Capture the Flag
Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.



