Die Welt der Softwareentwicklung bietet eine unendliche Anzahl von Möglichkeiten, sich zu vermasseln: falsche Dinge löschen, in Sackgassen coden, Commit-Nachrichten mit Tippfehlern übersäen, sind nur einige wenige der Fülle.
Glücklicherweise haben wir jedoch ein wunderbares Sicherheitsnetz in Form von Git, wenn wir mit Versionskontrolle arbeiten. Nicht, dass Sie und ich ein Sicherheitsnetz bräuchten, natürlich, denn wir machen nie Fehler, oder? Klar, klar. Aber zum Wohle aller anderen lassen Sie uns einen Rundgang durch einige der „Undo“-Werkzeuge in Git machen, die uns vor uns selbst retten können.
Den letzten Commit korrigieren
Einen Commit zu vermasseln, ist allzu einfach. Klassisches Beispiel: Ein Tippfehler in einer Commit-Nachricht. Noch eins? Vergessen, eine Änderung in den Staging-Bereich aufzunehmen. Und in vielen Fällen bemerken wir unseren Fehler sofort – natürlich, nachdem wir die Eingabetaste gedrückt haben.
Glücklicherweise macht Git es lächerlich einfach, den allerletzten Commit zu korrigieren. Nehmen wir an, wir hätten gerade die folgende Zeile eingegeben
git commit -m "Massage full of typohs"
Und (als ob dieses orthografische Durcheinander nicht schlimm genug wäre) nehmen wir an, wir hätten *auch* vergessen, eine weitere geänderte Datei in den Staging-Bereich aufzunehmen. Wir können beide Fehler mit den folgenden zwei Befehlen korrigieren
git add forgotten-changes.js
git commit --amend -m "A sensible message"
Die magische Zutat ist die Option --amend: Wenn Sie sie auf einen Commit anwenden, korrigiert Git den allerletzten Commit – mit allen gestagten Änderungen und der neuen Nachricht.
Ein kurzes Wort der Warnung, jedoch: Verwenden Sie --amend nur bei Commits, die noch nicht in ein Remote-Repository gepusht wurden. Der Grund dafür ist, dass Git den ursprünglichen, fehlerhaften Commit durch die geänderte Version *ersetzt*. Danach sieht es so aus, als hätte der ursprüngliche Commit nie stattgefunden. Ja, das ist gut, um Fehler zu vertuschen, aber nur, wenn wir diesen Fehler noch nicht auf dem Remote-Server *veröffentlicht* haben.
Lokale Änderungen rückgängig machen
Jeder hatte schon solche Tage: den ganzen Vormittag hacken, nur um sich einzugestehen, dass die letzten Stunden reine Zeitverschwendung waren. Man muss neu anfangen und viel (oder alles) davon rückgängig machen.
Aber das ist einer der Gründe, warum wir Git überhaupt benutzen – um Dinge ausprobieren zu können, ohne die Angst, etwas kaputt zu machen.
Nehmen wir eine Beispielsituation in Augenschein
git status
modified: about.html
deleted: imprint.html
modified: index.html
Nehmen wir nun an, dies ist einer dieser verschwendeten Hack-Tage. Wir hätten die Finger von about.html lassen sollen und imprint.html nicht löschen sollen. Was wir jetzt wollen, ist, unsere aktuellen Änderungen an diesen Dateien zu *verwerfen* – während wir die brillante Arbeit an index.html behalten. Der Befehl git checkout kann in diesem Fall helfen. Stattdessen müssen wir spezifischer werden, welche Dateien wir auschecken, so:
git checkout HEAD about.html imprint.html
Dieser Befehl stellt sowohl about.html als auch imprint.html wieder in ihren letzten Commit-Zustand her. Puh, wir sind einer blauen Beule entkommen!
Wir könnten das noch einen Schritt weiter treiben und *spezifische einzelne Zeilen* in einer geänderten Datei *verwerfen*, anstatt das Ganze zu verwerfen! Ich gebe zu, es ist ziemlich kompliziert, das auf der Kommandozeile zu machen, aber die Verwendung eines Desktop-Git-Clients wie Tower ist eine großartige Möglichkeit, dies zu erreichen.

Für diese *wirklich* schlechten Tage könnten wir die großen Geschütze in Form von
git reset --hard HEAD
Während wir mit checkout nur *spezifische* Dateien wiederhergestellt haben, setzt dieser Befehl unsere *gesamte Arbeitskopie* zurück. Mit anderen Worten, reset stellt das vollständige Projekt in seinem letzten Commit-Zustand wieder her. Ähnlich wie bei --amend gibt es bei der Verwendung von checkout und reset etwas zu beachten: Das Verwerfen lokaler Änderungen mit diesen Befehlen kann nicht rückgängig gemacht werden! Sie wurden nie in das Repository committet, daher ist es nur logisch, dass sie nicht wiederhergestellt werden können. Seien Sie besser sicher, dass Sie sie wirklich loswerden wollen, denn es gibt kein Zurück!
Einen älteren Commit rückgängig machen und rückgängig machen
In vielen Fällen bemerken wir einen Fehler erst viel später, nachdem er längst in das Repository committet wurde.

Wie können wir diesen einen fehlerhaften Commit loswerden? Nun, die Antwort ist, dass wir das nicht sollten… zumindest in den meisten Fällen. Selbst beim „Rückgängigmachen“ von Dingen löscht Git normalerweise keine Daten. Es *korrigiert sie durch Hinzufügen neuer Daten*. Lassen Sie uns sehen, wie das mit unserem „Bösewicht“-Beispiel funktioniert
git revert 2b504bee
Durch die Verwendung von git revert auf diesem fehlerhaften Commit haben wir nichts gelöscht. Ganz im Gegenteil

Git hat automatisch einen *neuen* Commit mit Änderungen erstellt, die die *Auswirkungen* des „schlechten“ Commits *rückgängig machen*. Wenn wir also ursprünglich drei Commits hatten und versuchten, den mittleren zu korrigieren, haben wir jetzt insgesamt vier Commits, wobei ein neuer hinzugefügt wurde, der den mit revert gezielten korrigiert.
Eine frühere Version eines Projekts wiederherstellen
Ein anderer Anwendungsfall ist, wenn wir eine frühere Version unseres Projekts wiederherstellen wollen. Anstatt einfach eine bestimmte Revision in unserer Commit-Historie rückgängig zu machen oder rückgängig zu machen, möchten wir vielleicht wirklich die Zeit zurückdrehen und zu einer bestimmten Revision zurückkehren.
Im folgenden Beispiel-Szenario würden wir alle Commits, die nach „C2“ kamen, als *unerwünscht* erklären. Wir möchten zum Zustand von Commit „C2“ zurückkehren und alles vergessen, was danach kam.

Der benötigte Befehl ist Ihnen bereits (zumindest teilweise) bekannt, basierend auf dem, was wir bereits behandelt haben
git reset --hard 2b504bee
Damit weisen wir git reset den SHA-1-Hash des Commits zu, zu dem wir zurückkehren möchten. Die Commits C3 und C4 verschwinden dann aus der Projektgeschichte.
Wenn Sie in einem Git-Client wie Tower arbeiten, sind sowohl git revert als auch git reset über das Kontextmenü eines Commit-Elements verfügbar.

Commits löschen, gelöschte Branches wiederherstellen, Konflikte behandeln usw. usw. usw.
Natürlich gibt es viele andere Möglichkeiten, Dinge in einem Softwareprojekt zu vermasseln. Aber glücklicherweise bietet Git auch viele weitere Werkzeuge, um das Durcheinander zu beseitigen.
Schauen Sie sich das „Erste-Hilfe-Set für Git“-Projekt an, das ich und andere vom Tower-Team erstellt haben, wenn Sie mehr über die in diesem Beitrag behandelten Szenarien oder andere Themen erfahren möchten, wie z. B. das Verschieben von Commits zwischen Branches, das Löschen alter Commits, das Wiederherstellen gelöschter Branches oder das elegante Behandeln von Merge-Konflikten. Es ist ein völlig kostenloser Leitfaden, der 17 Videos und ein praktisches Cheat-Sheet enthält, das Sie herunterladen und neben Ihrem Computer aufbewahren können.

In der Zwischenzeit viel Spaß beim Rückgängigmachen!
Toller Artikel! Das einzige, was wir jedoch tun, ist, dass wir immer Probleme beheben und niemals rückgängig machen oder wiederherstellen, daher haben wir nie etwas mit diesen Befehlen zu tun, da man sich schnell in Schwierigkeiten bringen kann, wenn man nicht weiß, was man tut.
Danke für das Feedback, John! Es stimmt, dass man mit diesen Befehlen „wissen muss, was man tut“. In vielen Fällen schützt Sie jedoch eine sehr einfache Regel vor dem Schlimmsten: „Manipulieren Sie keine Commit-Historie, die bereits an ein Remote-Repository gepusht wurde.“
Solange Sie diese Tools zur Bereinigung Ihrer lokalen Commit-Historie verwenden, können Sie aufs Gas drücken ;-)