Beginner’s Guide to Interactive Rebasing

Ursprünglich veröffentlicht von Stride am 16. Januar 2018 52,731 liest

Anfang des Jahres habe ich zum ersten Mal eine interaktive Rebase gemacht und war beeindruckt, was man damit alles machen kann. Ich fand es anfangs auch ein wenig kompliziert. Ich hoffe, dass dieser Leitfaden dazu beiträgt, einige Unsicherheiten zu beseitigen.

Auch, weil es so mächtig ist und man im Grunde die Geschichte umschreiben kann, eine kleine Warnung, bevor wir beginnen: Es gibt viele Ansichten über Git und darüber, ob Rebasing eine gute Idee ist oder nicht. Dieser Beitrag wird nicht in diese Diskussionen eintauchen und ist lediglich dazu gedacht, die Grundlagen der Verwendung von interaktivem Rebasing zu erläutern.

TL;DR

  • Interaktives Rebasing kann zum Ändern von Commits auf viele Arten verwendet werden, wie z.B. Bearbeiten, Löschen und Squashing.
  • Um Git mitzuteilen, wo es mit dem interaktiven Rebase beginnen soll, verwenden Sie den SHA-1 oder den Index des Commits, der dem zu ändernden Commit unmittelbar vorausgeht.
  • Wenn Git während eines interaktiven Rebase bei einem Commit pausiert, den Sie zum Bearbeiten markiert haben, unterscheidet sich der Arbeitsablauf nicht von einem normalen Commit-Prozess – Sie stellen Dateien bereit und übertragen sie dann. Der einzige Unterschied ist, dass Sie den Befehl git commit --amend statt git commit verwenden.
  • Interaktives Rebase erzeugt neue SHA-1’s, daher ist es am besten, interaktives Rebase für Commits zu verwenden, die Sie nicht in einen entfernten Zweig verschoben haben.

Das Problem

In diesem Beispiel werden wir eine Situation durchspielen, in der wir in einem Feature-Zweig gearbeitet haben und ein paar Commits haben, die wir ändern oder löschen wollen. So sieht unser Git-Protokoll aus:

Aus dem obigen Git-Protokoll ergeben sich die beiden Commits, die wir ändern möchten:
4a4d705 – In diesem Commit haben wir versehentlich einen Merge-Konflikt begangen
6c01350 – In diesem Commit haben wir den Merge-Konflikt entfernt

Wir möchten zu 4a4d705 zurückgehen, den Merge-Konflikt im Commit entfernen und dann 6c01350 löschen, da der Merge-Konflikt behoben ist und wir diesen Commit nicht mehr benötigen. Dies wird unsere Commit-Historie aus zwei Gründen verbessern:

  1. Wir werden keine kaputten Commits haben (Merge-Konflikte)
  2. Wir werden nur sinnvolle Commits haben, keine Commits, die sich nur auf die Behebung eines übersehenen Merge-Konflikts beziehen

Die Lösung

Diese Situation ist ein guter Kandidat für interaktives Rebasing. Scott Chacon beschreibt in seinem Buch Pro Git das interaktive Rebasing wie folgt: „Manchmal kann die behobene Sache … nicht an den nicht ganz perfekten Commit, den sie behebt, angepasst werden, weil dieser Commit tief in einer Patch-Serie vergraben ist. Genau dafür ist interaktives Rebase da: Verwenden Sie es nach vielen , indem Sie Commits neu anordnen und bearbeiten und mehrere Commits zu einem zusammenfassen.“

Welche Commits wollen wir ändern?

Um ein interaktives Rebase zu starten, müssen wir Git mitteilen, welche Commits wir ändern wollen. Dazu verweisen wir auf den Commit, der unmittelbar vor dem frühesten Commit liegt, den wir ändern wollen. Oder einfach ausgedrückt, wir referenzieren „den letzten Commit, den wir unverändert beibehalten wollen“, so Scott Chacon.

Schauen wir uns unser Beispiel an, um es besser zu verstehen. Es gibt zwei Möglichkeiten, diesen Commit zu referenzieren:
Nach SHA-1 – Der letzte Commit, den wir unverändert beibehalten wollen, hat einen SHA-1 von 528f82e, so dass wir diesen in unseren interaktiven Rebase-Befehl übergeben können.
Nach Index – Der letzte Commit, den wir unverändert beibehalten wollen, hat eine Indexposition von 3 (Git verwendet eine Null-basierte Indizierung), so dass wir HEAD~3 an unseren interaktiven Rebase-Befehl übergeben können.

Hinweis – Wenn Sie nur wenige Commits haben, die Sie interaktiv rebasen wollen, ist die Verwendung des Index wahrscheinlich für die meisten Leute einfacher. Wenn Sie jedoch viele Commits haben, ist es wahrscheinlich einfacher, den SHA-1 zu verwenden, damit Sie nicht den ganzen Weg durch das Git-Protokoll zählen müssen.

Interaktives Rebasing starten

Ausgehend von unserem Beispiel werden wir entweder:

$ git rebase -i 528f82e

oder

$ git rebase -i HEAD~3

ausführen, was dieses Fenster in Vim öffnet:

Beachte, dass die Commits in der umgekehrten Reihenfolge von git log sind. In git log steht die jüngste Übertragung ganz oben. In dieser Ansicht steht der jüngste Commit ganz unten. Beachten Sie auch, dass die Kommentare unten eine hilfreiche Liste der gültigen Befehle enthalten, die wir bei jeder Übertragung verwenden können.

Wenn Sie Vim nicht kennen, klicken Sie einfach auf jedes Wort pick, das Sie bearbeiten möchten, und drücken Sie dann die Taste <i> (für den Einfügemodus). Wenn Sie mit dem Tippen fertig sind, drücken Sie die Taste <esc>, um den Einfügemodus zu verlassen.

In unserem Beispiel haben wir den Befehl edit für die Übergabe, die wir ändern wollen, und den Befehl drop für die Übergabe, die wir löschen wollen, geändert. Dann führen wir :wq aus, um zu speichern und das Vim-Fenster zu beenden.

Ändern Sie die Übergabe

Zurück im Terminal sehen wir diese Meldung:

Das macht Sinn, denn wir sind bei 4a4d705 stehen geblieben. Dies ist der älteste Commit in der Reihe der Commits, die wir ändern wollen. Wir beginnen mit diesem Commit und arbeiten uns durch alle Commits bis zum letzten.

Zur Erinnerung: 4a4d705 war der Commit mit dem Merge-Konflikt, den wir versehentlich committed haben. Wenn wir unseren Editor öffnen, sehen wir den Merge-Konflikt dort:

So beheben wir den Merge-Konflikt in der Datei, aber was machen wir jetzt? Im Zweifelsfall git status:

Cool! Das ist tatsächlich hilfreich. Wir sehen, dass wir gerade 4a4d705 bearbeiten, und wir sehen die nächsten beiden Commits, die nach diesem Commit bearbeitet werden sollen.

Der Rest der Nachricht erklärt uns einen bekannten Arbeitsablauf. Git sagt uns, dass wir git commit --amend ausführen müssen, wenn wir den Commit ändern wollen. Dies entspricht im Wesentlichen unserem typischen git commit, das wir in einem normalen Arbeitsablauf verwenden. Am Ende dieser Meldung sehen wir, dass unsere Datei geändert wurde und die Änderungen widerspiegelt, die wir gerade vorgenommen haben, um den Merge-Konflikt zu beseitigen. Wir müssen die Datei vor der Übergabe bereitstellen. Dies unterscheidet sich nicht von einem normalen Arbeitsablauf.

Alles, was wir tun, ist git add tempChanger.js, um die bearbeitete Datei bereitzustellen und dann git commit --amend, um die bereitgestellte Datei zu übertragen! Dies öffnet nun wieder ein Vim-Fenster mit der Übergabemeldung:

Wir können die Übergabemeldung entweder bearbeiten oder unverändert lassen. Wir entscheiden uns dafür, die Commit-Nachricht unverändert zu lassen und geben :wq ein, um zu speichern und das Fenster zu verlassen.

Wir haben jetzt unsere alte Commit-Nachricht bearbeitet. Und was nun? Führen Sie git status aus:

Interaktives Rebase fortsetzen

Wir haben nichts mehr an dem Commit zu ändern, also machen wir weiter!

Wir führen git rebase --continue aus und sehen folgende Meldung:

Woah, wir sind fertig? Aber was ist mit den anderen beiden Commits? Nun, der nächste zu bearbeitende Commit war 6c01350. Diesen Commit haben wir zum Löschen markiert (drop), als wir den interaktiven Rebase gestartet haben. Git löschte ihn automatisch und ging zum nächsten Commit, 41aa9d2, über. Diese Übergabe wurde bei der ersten interaktiven Wiederherstellung nie geändert. Sein Standardbefehl war pick, was bedeutet, dass der Commit verwendet wird. Git wendete diesen Commit an, und da dies der letzte Commit war, wurde der interaktive Rebase abgeschlossen.

Wenn wir mehr Commits zu bearbeiten hätten, wären wir einfach zum nächsten Commit übergegangen und hätten mit der Änderung begonnen, wie wir es oben getan haben. Der Zyklus geht weiter, bis keine Commits mehr übrig sind.

Der Auswurfknopf

Es ist erwähnenswert, dass wir jederzeit abbrechen können, wenn wir an irgendeinem Punkt unserer interaktiven Rebase etwas vermasseln und nicht wissen, wie wir es beheben können. Wir können jederzeit git rebase --abort im Terminal eingeben, und die interaktive Umstellung wird abgebrochen, ohne dass Änderungen gespeichert werden. Wir müssten dann das interaktive Rebase erneut starten.

Nach dem interaktiven Rebase

Unser Git-Protokoll sieht jetzt wie folgt aus:

Sie werden feststellen, dass sich einige Dinge gegenüber dem interaktiven Rebase geändert haben:

  • Wir haben nicht mehr den Commit 6c01350 mit der Commit-Meldung „Remove merge conflict“. Dies ist der Commit, den wir in unserem interaktiven Rebase gelöscht haben.
  • Unser Commit, den wir bearbeitet haben, 4a4d705, hat einen anderen SHA-1, 2b7443d.
  • Unser jüngster Commit aus unserem ursprünglichen Git-Protokoll, 41aa9d2, hat ebenfalls einen neuen SHA-1, 2b95e92. Dieser Commit wurde nicht geändert, sondern einfach auf den Commit davor angewendet 2b7443d.

Nebeneffekte des interaktiven Rebasings

Die beiden jüngsten Commits in unserem Git-Protokoll werden von Git als völlig neue Commits betrachtet, da sie neue SHA-1s haben. Dies gilt sogar für unseren letzten Commit 2b95e92, bei dem sich weder die Commit-Nachricht noch die Dateien geändert haben. Dies weist auf einen wichtigen Punkt beim interaktiven Rebasing hin: Wenn Sie einen Commit ändern, haben dieser und alle nachfolgenden Commits neue SHA-1’s.

Dies hat keine Auswirkungen, wenn die von Ihnen geänderten Commits nicht in einen entfernten Zweig gepusht wurden. Wenn Sie jedoch tatsächlich einen interaktiven Rebase an Commits durchgeführt haben, die bereits in einen entfernten Zweig verschoben wurden, und dann Ihren Zweig erneut verschoben haben, würden Sie sehen:

Technisch könnten Sie dies umgehen, indem Sie git push --force verwenden, aber das ist sehr gefährlich. Wenn der entfernte Zweig Commits von anderen Personen hat, aber Ihr lokaler Zweig diese Commits noch nicht hat, löschen Sie effektiv deren Commits.

Eine andere Lösung ist die Verwendung von git push --force-with-lease, die nur Ihre Commits ändert, aber nicht die von anderen, obwohl dies auch problematisch sein kann. Wenn zum Beispiel ein anderer Entwickler die Commits, die neue SHA-1s erhalten haben, bereits in seinem lokalen Zweig hat, wird er, wenn er den entfernten Zweig zieht, Merge-Konflikte mit jedem dieser Commits haben.

Wann --force-with-lease zu verwenden ist, geht über den Rahmen dieses Blogposts hinaus, aber es wäre am besten, andere Mitglieder Ihres Teams zu konsultieren, bevor Sie dies tun. Sie können mehr über git push --force-with-lease hier lesen.

Das Wichtigste aus diesem Abschnitt ist, dass es viel einfacher und sicherer ist, interaktives Rebasing für Commits zu verwenden, die noch nicht in einen entfernten Zweig gepusht wurden.

Geschrieben von Blake DeBoer. Ursprünglich veröffentlicht auf Dev.to

Senior, Lead oder Principal Entwickler in NYC? Stride stellt ein! Möchten Sie Ihr technisches Team aufstocken? See how we do it! www.stridenyc.com

Tags

Join Hacker Noon

Erstelle dein kostenloses Konto, um dein individuelles Leseerlebnis freizuschalten.

Leave a Reply