Eine Anleitung zum Rückgängigmachen von Fehlern mit Git (Teil 2)

Veröffentlicht: 2022-03-10
Kurzzusammenfassung ↬ Fehler. Diese grausamen Schurken machen auch vor der schönen Welt der Softwareentwicklung nicht halt. Aber obwohl wir Fehler nicht vermeiden können, können wir lernen, sie rückgängig zu machen! Dieser Artikel zeigt Ihnen die richtigen Werkzeuge für Ihre tägliche Arbeit mit Git. Vielleicht möchten Sie auch den ersten Artikel der Serie lesen.

In diesem zweiten Teil unserer Serie „Fehler mit Git rückgängig machen“ werden wir der Gefahr wieder mutig ins Auge blicken: Ich habe vier neue Weltuntergangsszenarien vorbereitet – darunter natürlich einige clevere Möglichkeiten, unseren Hals zu retten! Aber bevor wir eintauchen: Werfen Sie einen Blick auf die vorherigen Artikel zu Git, um noch mehr Selbstrettungsmethoden zu finden, die Ihnen helfen, Ihre Fehler mit Git rückgängig zu machen!

Lass uns gehen!

Wiederherstellen eines gelöschten Zweigs mithilfe des Reflogs

Haben Sie jemals einen Zweig gelöscht und kurz darauf festgestellt, dass Sie das nicht hätten tun sollen? Für den unwahrscheinlichen Fall, dass Sie dieses Gefühl nicht kennen, kann ich Ihnen sagen, dass es kein gutes ist. Eine Mischung aus Traurigkeit und Wut beschleicht Sie, während Sie an all die harte Arbeit denken, die in die Commits dieses Zweigs geflossen ist, all den wertvollen Code, den Sie jetzt verloren haben.

Glücklicherweise gibt es eine Möglichkeit, diesen Zweig von den Toten zurückzubringen – mit Hilfe eines Git-Tools namens „Reflog“. Wir hatten dieses Tool im ersten Teil unserer Serie verwendet, aber hier ist eine kleine Auffrischung: Das Reflog ist wie ein Tagebuch, in dem Git jede Bewegung des HEAD-Zeigers in Ihrem lokalen Repository notiert. Mit anderen, weniger nerdigen Worten: Jedes Mal, wenn Sie auschecken, festschreiben, zusammenführen, rebasen, Rosinen auswählen usw., wird ein Journaleintrag erstellt. Das macht den Reflog zu einem perfekten Sicherheitsnetz, wenn etwas schief geht!

Schauen wir uns ein konkretes Beispiel an:

 $ git branch * feature/login master

Wir können sehen, dass wir derzeit unsere feature/login ausgecheckt haben. Nehmen wir an, dass dies der Zweig ist, den wir (versehentlich) löschen werden. Zuvor müssen wir jedoch zu einem anderen Branch wechseln, da wir unseren aktuellen HEAD-Branch nicht löschen können!

 $ git checkout master $ git branch -d feature/login

Unser wertvoller Feature-Zweig ist jetzt weg – und ich gebe Ihnen eine Minute, um (a) die Schwere unseres Fehlers zu verstehen und (b) ein wenig zu trauern. Nachdem Sie die Tränen weggewischt haben, müssen wir einen Weg finden, diesen Zweig zurückzubringen! Lassen Sie uns das Reflog öffnen (einfach durch Eingabe von git reflog ) und sehen, was es für uns bereithält:

Git’s Reflog protokolliert alle wichtigen Aktionen in unserem lokalen Repository
Git's Reflog protokolliert alle wichtigen Aktionen in unserem lokalen Repository. (Große Vorschau)

Hier sind einige Kommentare, die Ihnen helfen sollen, die Ausgabe zu verstehen:

  • Zunächst einmal müssen Sie wissen, dass das Reflog seine Einträge chronologisch sortiert: Die neuesten Einträge stehen ganz oben in der Liste.
  • Das oberste (und damit neueste) Element ist der Befehl git checkout , den wir vor dem Löschen des Zweigs ausgeführt haben. Es wird hier im Reflog protokolliert, weil es eine dieser „HEAD-Zeigerbewegungen“ ist, die das Reflog so pflichtbewusst aufzeichnet.
  • Um unseren schwerwiegenden Fehler rückgängig zu machen, können wir einfach zum Zustand davor zurückkehren – was auch sauber und übersichtlich im Reflog festgehalten wird!

Versuchen wir es also, indem wir einen neuen Zweig (mit dem Namen unseres „verlorenen“ Zweigs) erstellen, der bei diesem „vorher“-Status-SHA-1-Hash beginnt:

 $ git branch feature/login 776f8ca

Und voila! Sie werden erfreut sein zu sehen, dass wir unseren scheinbar verlorenen Zweig jetzt wiederhergestellt haben!

Wenn Sie eine Git-Desktop-GUI wie „Tower“ verwenden, können Sie eine nette Abkürzung nehmen: Drücken Sie einfach CMD + Z auf Ihrer Tastatur, um den letzten Befehl rückgängig zu machen – selbst wenn Sie gerade einen Zweig gewaltsam gelöscht haben!

Eine Desktop-GUI wie Tower kann das Rückgängigmachen von Fehlern erleichtern.
Mehr nach dem Sprung! Lesen Sie unten weiter ↓

Verschieben eines Commits in einen anderen Branch

In vielen Teams gibt es eine Übereinkunft, develop nicht auf langlaufende Branches wie main oder Develop zu verpflichten: Branches wie diese sollten nur durch Integrationen (z. B. Merges oder Rebases) neue Commits erhalten . Und doch sind Fehler natürlich unvermeidlich: Manchmal vergessen und binden wir uns trotzdem an diese Zweige! Wie können wir also das Chaos beseitigen, das wir angerichtet haben?

Verschieben eines Commits in seinen richtigen Ziel-Zweig
Unser Commit ist im falschen Zweig gelandet. Wie können wir es in den richtigen Zielzweig verschieben? (Große Vorschau)

Glücklicherweise können diese Arten von Problemen leicht behoben werden. Krempeln wir die Ärmel hoch und machen uns an die Arbeit.

Der erste Schritt besteht darin, zum richtigen Zielzweig zu wechseln und dann den Commit mit dem cherry-pick Befehl zu verschieben:

 $ git checkout feature/login $ git cherry-pick 776f8caf

Sie haben jetzt das Commit auf dem gewünschten Zweig, wo es ursprünglich hätte sein sollen. Fantastisch!

Aber eines bleibt noch zu tun: Wir müssen den Ast aufräumen, wo er zuerst versehentlich gelandet ist! Der cherry-pick Befehl hat sozusagen eine Kopie des Commit erstellt – aber das Original ist immer noch auf unserem lang laufenden Zweig vorhanden:

Eine Kopie des Commits im richtigen Branch, aber das Original wird immer noch als im falschen Branch angezeigt
Wir haben erfolgreich eine Kopie des Commits im richtigen Branch erstellt, aber das Original ist immer noch hier – im falschen Branch. (Große Vorschau)

Das bedeutet, dass wir zu unserem lang laufenden Zweig zurückwechseln und git reset verwenden müssen, um ihn zu entfernen:

 $ git checkout main $ git reset --hard HEAD~1

Wie Sie sehen können, verwenden wir hier den Befehl git reset , um den fehlerhaften Commit zu löschen. Der Parameter HEAD~1 weist Git an, „eine Revision hinter HEAD zurückzugehen“, wodurch effektiv das oberste (und in unserem Fall: unerwünschte) Commit aus der Historie dieses Zweigs gelöscht wird.

Und voila: Der Commit ist jetzt dort, wo er ursprünglich hätte sein sollen, und unser langjähriger Zweig ist sauber – als wäre unser Fehler nie passiert!

Bearbeiten der Nachricht eines alten Commit

Es ist allzu leicht, einen Tippfehler in eine Commit-Nachricht zu schmuggeln – und ihn erst viel später zu entdecken. In einem solchen Fall kann die gute alte Option --amend von git commit nicht verwendet werden, um dieses Problem zu beheben, da sie nur für den allerletzten Commit funktioniert. Um einen älteren Commit zu korrigieren, müssen wir auf ein Git-Tool namens „Interactive Rebase“ zurückgreifen.

Eine Commit-Nachricht, die es wert ist, geändert zu werden
Hier ist eine Commit-Nachricht, die es wert ist, geändert zu werden. (Große Vorschau)

Zuerst müssen wir Interactive Rebase mitteilen, welchen Teil des Commit-Verlaufs wir bearbeiten möchten. Dies geschieht, indem wir ihm einen Commit-Hash zuführen: den übergeordneten Commit desjenigen, den wir manipulieren möchten.

 $ git rebase -i 6bcf266b

Daraufhin öffnet sich ein Editorfenster. Es enthält eine Liste aller Commits nach dem, den wir als Grundlage für das Interactive Rebase im Befehl bereitgestellt haben:

Zeigt den Bereich der Commits, die wir zur Bearbeitung in unserer interaktiven Rebase-Sitzung ausgewählt haben
Die Auswahl an Commits, die wir zur Bearbeitung in unserer interaktiven Rebase-Sitzung ausgewählt haben. (Große Vorschau)

Hier ist es wichtig, dass Sie nicht Ihrem ersten Impuls folgen: In diesem Schritt bearbeiten wir die Commit-Nachricht noch nicht . Stattdessen teilen wir Git nur mit, welche Art von Manipulation wir mit welchem ​​Commit(s) durchführen wollen. Praktischerweise gibt es eine Liste mit Aktionsschlüsselwörtern, die in den Kommentaren unten in diesem Fenster vermerkt sind. Für unseren Fall markieren wir Zeile #1 mit reword (wodurch das Standardpick pick wird).

In diesem Schritt müssen Sie nur noch speichern und das Editorfenster schließen. Im Gegenzug öffnet sich ein neues Editor-Fenster, das die aktuelle Nachricht des von uns markierten Commits enthält. Und jetzt ist es endlich an der Zeit, unsere Änderungen vorzunehmen!

Hier ist der gesamte Ablauf für Sie auf einen Blick:

Verwenden von Interactive Rebase, um die Nachricht eines alten Commits von Anfang bis Ende zu bearbeiten.

Korrigieren eines fehlerhaften Commit (auf sehr elegante Weise)

Abschließend werfen wir einen Blick auf fixup , das Schweizer Taschenmesser der Undoing-Tools. Einfach ausgedrückt, ermöglicht es Ihnen, einen fehlerhaften/unvollständigen/falschen Commit nachträglich zu beheben. Es ist aus zwei Gründen wirklich ein wunderbares Werkzeug:

  1. Es spielt keine Rolle, was das Problem ist.
    Möglicherweise haben Sie vergessen, eine Datei hinzuzufügen, sollten etwas gelöscht haben, eine falsche Änderung vorgenommen haben oder einfach einen Tippfehler gemacht haben. fixup funktioniert in all diesen Situationen!
  2. Es ist äußerst elegant.
    Unsere normale, instinktive Reaktion auf einen Fehler in einem Commit besteht darin, einen neuen Commit zu erstellen, der das Problem behebt. Diese Arbeitsweise, so intuitiv sie auch erscheinen mag, lässt Ihren Commit-Verlauf sehr bald sehr chaotisch aussehen. Sie haben „Original“-Commits und dann diese kleinen „Pflaster“-Commits, die die Dinge beheben, die bei den Original-Commits schief gelaufen sind. Ihr Verlauf ist übersät mit kleinen, bedeutungslosen Pflaster-Commits, was es schwierig macht, zu verstehen, was in Ihrer Codebasis passiert ist.
Ihr Commit-Verlauf kann sehr schwer zu lesen sein, wenn Sie ständig kleine Fehler mit sogenannten Band-Aid-Commits beheben
Das ständige Beheben kleiner Fehler mit „Pflaster-Commits“ macht Ihre Commit-Historie sehr schwer lesbar. (Große Vorschau)

Hier kommt das fixup ins Spiel. Es ermöglicht Ihnen, immer noch dieses korrigierende Pflaster durchzuführen. Aber hier kommt die Magie: Es wendet es dann auf das ursprüngliche, kaputte Commit an (repariert es auf diese Weise) und verwirft dann das hässliche Band-Aid-Commit vollständig!

Fixup wendet Ihre Korrekturen auf das ursprüngliche Commit an und entfernt dann das überflüssige Band-Aid-Commit
Fixup wendet Ihre Korrekturen auf das ursprüngliche Commit an und entfernt dann das überflüssige Band-Aid-Commit. (Große Vorschau)

Wir können gemeinsam ein Praxisbeispiel durchgehen! Nehmen wir an, dass der ausgewählte Commit hier defekt ist.

Beheben des ausgewählten falschen Commits auf elegante Weise
Das ausgewählte Commit ist falsch – und wir werden es auf elegante Weise beheben. (Große Vorschau)

Nehmen wir auch an, dass ich Änderungen in einer Datei namens error.html vorbereitet habe, die das Problem lösen werden. Hier ist der erste Schritt, den wir machen müssen:

 $ git add error.html $ git commit --fixup 2b504bee

Wir erstellen einen neuen Commit, aber wir sagen Git, dass dies ein besonderer ist: Es ist ein Fix für einen alten Commit mit dem angegebenen SHA-1-Hash (in diesem Fall 2b504bee ).

Der zweite Schritt besteht nun darin, eine Interactive Rebase-Sitzung zu starten – denn fixup gehört zum großen Toolset von Interactive Rebase.

 $ git rebase -i --autosquash 0023cddd

Zwei Dinge sind es wert, über diesen Befehl erklärt zu werden. Erstens, warum habe ich hier 0023cddd als Revisions-Hash angegeben? Weil wir unsere interaktive Rebase-Sitzung beim übergeordneten Commit unseres kaputten Kollegen starten müssen.

Zweitens, wofür ist die Option --autosquash ? Es nimmt uns viel Arbeit ab! In dem sich nun öffnenden Editorfenster ist bereits alles für uns vorbereitet:

Das interaktive Rebase-Sitzungsfenster
Das interaktive Rebase-Sitzungsfenster (große Vorschau)

Dank der Option --autosquash hat Git bereits die schwere Arbeit für uns erledigt:

  1. Es markierte unser kleines Pflaster-Commit mit dem fixup -Action-Schlüsselwort. Auf diese Weise kombiniert Git es mit dem Commit direkt darüber und verwirft es dann.
  2. Es hat auch die Zeilen entsprechend neu geordnet und unser Band-Aid-Commit direkt unter das Commit verschoben, das wir reparieren möchten (nochmals: fixup funktioniert durch Kombinieren des markierten Commits mit dem darüber !).

Kurz gesagt: Uns bleibt nichts anderes übrig, als die Fenster zu schließen!

Werfen wir einen letzten Blick auf das Endergebnis.

  • Der ehemals defekte Commit ist behoben: Er enthält jetzt die Änderungen, die wir in unserem Band-Aid-Commit vorbereitet haben.
  • Der hässliche Pflaster-Commit selbst wurde verworfen: Der Commit-Verlauf ist sauber und einfach zu lesen – als ob überhaupt kein Fehler aufgetreten wäre.
Ein Beispiel dafür, wie ein sauberer Commit-Verlauf aussieht
Das Endergebnis nach der Verwendung des Fixup-Tools: ein sauberer Commit-Verlauf! (Große Vorschau)

Zu wissen, wie man Fehler rückgängig macht, ist eine Superkraft

Glückwünsche! Sie können jetzt in vielen schwierigen Situationen Ihren Hals retten! Wir können diese Situationen nicht wirklich vermeiden: Egal wie erfahren wir als Entwickler sind, Fehler gehören einfach zum Job. Aber jetzt, da Sie wissen, wie man mit ihnen umgeht, können Sie ihnen mit einem entspannten Herzschlag begegnen.

Wenn Sie mehr über das Rückgängigmachen von Fehlern mit Git erfahren möchten, kann ich Ihnen das kostenlose „First Aid Kit for Git“ empfehlen, eine Reihe von kurzen Videos zu genau diesem Thema.

Haben Sie Spaß daran, Fehler zu machen – und natürlich, sie mit Leichtigkeit rückgängig zu machen!