Git Reset Hard erklärt: Commits unwiderruflich löschen (Vorsicht!)

Riskante Git-Historie korrigieren? Anleitung für git reset --hard. Gefahren von Datenverlust & --force push! Plus: Wann git revert besser ist.

Git Reset Hard erklärt: Commits unwiderruflich löschen (Vorsicht!) hero image

Wichtiger Hinweis vorab: git reset --hard ist ein mächtiges, aber auch gefährliches Werkzeug. Es schreibt die Git-Historie unwiderruflich um und verwirft Änderungen endgültig. In Kombination mit git push --force kann es zu erheblichen Problemen in Team-Projekten führen. Dieser Befehl sollte nur mit Bedacht genutzt werden und niemals leichtfertig auf gemeinsam genutzten Branches wie main oder master ohne explizite Absprache im Team! Oft gibt es sicherere Alternativen wie git revert.

Notwendigkeit eines Git-Resets

Manchmal ändert man seinen Code und plötzlich funktioniert irgendwas nicht mehr. Leider ist er schon in das Master-Branch der Repository gepusht.

Oder man hat aus Versehen sensible Daten wie API-Keys oder Passwörter in sein Repository gepusht (hierbei ist es dennoch empfehlenswert, die API-Keys oder Passwörter zu ändern).

Ich sehe, dass mein Code zu einem bestimmten Zeitpunkt nicht mehr funktioniert.

Fehlgeschlagener Build-Prozess

Ich möchte einen anderen Zustand wiederherstellen und alles dazwischen unwiederbringlich löschen.

Was ist Git Reset?

git reset ermöglicht es, Änderungen rückgängig zu machen, indem der aktuelle Branch-Pointer auf einen anderen Commit verschoben wird. Dies hat den Effekt, dass der Zustand des Repositorys auf einen früheren Zeitpunkt zurückgespult wird.

Es gibt verschiedene Modi, die mit reset verwendet werden können:

  • --soft: Verschiebt den Branch-Pointer, lässt aber den Staging-Bereich und das Arbeitsverzeichnis unverändert
  • --mixed: Verschiebt den Branch-Pointer, und setzt den Staging-Bereich zurück, aber nicht das Arbeitsverzeichnis (dies ist der Standardmodus)
  • --hard: Verschiebt den Branch-Pointer und setzt BEIDE, den Staging-Bereich und das Arbeitsverzeichnis, zurück

Je nach verwendetem Modus kann reset Commits verwerfen, Änderungen rückgängig machen oder sogar Arbeit dauerhaft löschen.

Identifizierung des letzten bekannten guten Commits

Vor Durchführung eines harten Resets, muss der letzte gute Zustand ermittelt werden. Dies ist der Commit, bei dem der Code wie erwartet funktionierte bzw. bevor problematische Änderungen vorgenommen wurden.

Letzter erfolgreicher Build

Ich schaue mir in meiner Repository-Verwaltung (in diesem Fall GitHub) an, welche ID zum damaligen Zustand bestand.

Commit ID

Ausführen des Git-Reset-Befehls

Mit der Commit-ID kann ich nun den Git-Reset durchführen. Dafür öffne ich das Terminal und navigiere zu meinem lokalen Git-Repository. Dann führe ich den folgenden Befehl aus:

git reset --hard <commit-id>

Ersetze <commit-id> durch die tatsächliche ID des Commits, zu dem du zurückkehren möchtest. Zum Beispiel:

git reset --hard ac55a49123456789

Die --hard Option bedeutet, dass nicht nur der Branch-Pointer verschoben wird (wie bei --soft oder --mixed), sondern auch der Staging-Bereich und das Arbeitsverzeichnis auf den Zustand des Ziel-Commits zurückgesetzt werden. Alle Änderungen in diesen Bereichen, die noch nicht committet waren, gehen dabei verloren! Es sollte sichergestellt werden, dass keine ungespeicherten lokalen Änderungen mehr benötigt werden.

Übertragen der Änderungen auf das Remote-Repository

Nachdem git reset --hard lokal ausgeführt wurde, unterscheidet sich die lokale Commit-Historie von der des Remote-Repositorys (z.B. auf GitHub). Ein normaler git push wird fehlschlagen, da Git erkennt, dass Commits “fehlen”.

Um die Änderungen dennoch zu pushen, muss ein Force Push verwendet werden:

git push --force
# Oder die sicherere Variante:
git push --force-with-lease

Extreme Vorsicht bei git push --force! Dieser Befehl überschreibt die Historie im Remote-Repository mit der lokalen Version. Wenn Teammitglieder in der Zwischenzeit auf Basis der alten Historie gearbeitet und gepusht haben, können deren Änderungen verloren gehen oder schwerwiegende Merge-Konflikte entstehen.

  • Wann ist --force (mit Bedacht) evtl. OK? Auf rein persönlichen Branches, die niemand anders nutzt, oder nach klarer Absprache und Koordination im gesamten Team.
  • --force-with-lease: Ist etwas sicherer, da es den Push verweigert, wenn jemand anderes in der Zwischenzeit Änderungen auf den Branch gepusht hat. Es schützt aber nicht davor, die Historie für alle anderen umzuschreiben.

Dieser Prozess löscht die problematischen Änderungen und setzt das Repository zurück, als ob die nachfolgenden Commits nie stattgefunden hätten.

Reset

Merge-Konflikte nach einem Git-Reset

In manchen Fällen kann ein Git-Reset zu Konflikten bei der Zusammenführung führen, wenn andere Teammitglieder versuchen, die neuesten Änderungen zu übernehmen. Dies geschieht, weil ihr lokales Repository noch die von Ihnen entfernten Commits enthält.

Um Merge-Konflikte zu lösen, muss ein git pull mit dem --rebase Flag durchgeführt werden:

git pull --rebase

Dieser Befehl wendet ihre lokalen Änderungen auf den Reset-Commit an und ermöglicht es ihnen, alle Konflikte zu lösen und mit der aktualisierten Codebasis weiterzuarbeiten.

Alternative Ansätze

git reset --hard ist selten die beste Lösung für alltägliche Probleme, insbesondere in Teamumgebungen. Es sollten alternative Ansätze in Betracht gezogen werden, die möglicherweise weniger störend und besser für die spezifische Situation geeignet sind.

Git Revert

Mit dem Befehl git revert können spezifische Commits rückgängig gemacht werden, ohne die Commit-Historie zu verlieren. Dieser Befehl erstellt einen neuen Commit, der die Änderungen in den angegebenen Commits rückgängig macht.

Branch erstellen

Eine weitere Alternative zum Zurücksetzen von Git ist das Erstellen eines neuen Branchs ab dem letzten bekannten guten Commit. Auf diese Weise können Änderungen untersucht und getestet werden, ohne den Main-Branch zu beeinflussen.

Um einen neuen Branch zu erstellen, kann folgender Befehl verwendet werden:

git checkout -b <neuer-branch-name> <commit-id>

Der <neuer-branch-name> sollte durch einen aussagekräftigen Namen ersetzt werden, der den Zweck des neuen Branchs beschreibt. Die <commit-id> ist die ID des Commits, von dem aus der neue Branch erstellt wird.

Nach Erstellung des neuen Branchs, können bestimmte Commits aus dem problematischen Branch in den neuen Branch übernommen werden, um die Änderungen zu isolieren und zu testen.

git cherry-pick <commit-id>

Git Stash

Wenn nicht übertragene Änderungen vorhanden sind, die nicht verloren gehen sollen, können sie mit dem Befehl git stash temporär gespeichert werden.

Nach dem Reset können die Änderungen mit git stash pop oder git stash apply wieder abgerufen werden.

Interaktives Rebase

Mit Rebase kann die Commit-Historie neu geschrieben werden. Es ist zwar keine direkte Alternative zum Git-Reset, aber interaktives Rebasing kann dabei helfen, die Commit-Historie aufzuräumen und eine linearere und verständlichere Abfolge von Änderungen zu erstellen.

git rebase -i <commit-id>

Die <commit-id> ist der Commit, von dem aus das interaktive Rebase durchgeführt wird. Es öffnet ein Editorfenster, in dem die Aktionen für jeden Commit festgelegt werden können, wie z. B. pick, squash, edit oder drop.

Interaktives Rebase erfordert ein gutes Verständnis von Git und sollte mit Vorsicht verwendet werden, da es die Commit-Historie umschreibt. Es ist am besten geeignet, um lokale Commits zu bereinigen, bevor sie in ein gemeinsames Repository verschoben werden.

Fazit

Ein git reset --hard gefolgt von git push --force ist ein radikaler Schritt, um die Git-Historie zu korrigieren. Er kann nützlich sein, um lokale Fehler zu bereinigen oder sensible Daten (die dennoch geändert werden sollten!) aus noch nicht weit verbreiteten Commits zu entfernen.

Risiken: Datenverlust und massive Probleme bei der Zusammenarbeit im Team sind reale Gefahren. Für das Rückgängigmachen von Änderungen in geteilten Repositories ist git revert fast immer die sicherere und empfohlene Methode, da sie die Historie intakt lässt.

Die Nutzung von git reset --hard sollte nur als letztes Mittel in Betracht gezogen werden.

Diesen Beitrag teilen:

Diese Website verwendet Cookies. Diese sind notwendig, um die Funktionalität der Website zu gewährleisten. Weitere Informationen finden Sie in der Datenschutzerklärung