So machen Sie lokale und entfernte Commits in Git sicher rückgängig
Git-Commits sicher mit Reset, Revert, Reflog und Force-with-Lease rückgängig machen, ohne Arbeit zu verlieren oder gemeinsame Branches zu beschädigen.
So machen Sie lokale und entfernte Commits in Git sicher rückgängig
Einen Git-Commit rückgängig zu machen ist einfach. Das Richtige rückgängig zu machen, ohne Arbeit zu verlieren oder alle anderen im Branch zu überraschen, erfordert jedoch Urteilsvermögen.
Die erste Frage ist nicht "Welchen Befehl führe ich aus?" Sie lautet: "Wer hat diesen Commit gesehen?" Wenn der Commit nur auf Ihrem Laptop existiert, können Sie ihn normalerweise mit git reset oder git commit --amend umschreiben. Wenn der Commit bereits in einen Branch gepusht wurde, den andere verwenden, bevorzugen Sie git revert. Das bewahrt die Historie und erstellt einen neuen Commit, der die schlechte Änderung rückgängig macht.
Bevor Sie die Historie ändern, machen Sie eine schnelle Momentaufnahme Ihres aktuellen Stands:
git status
git branch backup-vor-rueckgaengigmachung
git log --oneline --decorate -5
Dieser temporäre Branch ist eine günstige Versicherung. Wenn Sie zu weit zurücksetzen oder Ihre Meinung ändern, hat der alte Commit immer noch einen Namen.
Wenn der Commit nicht gepusht wurde
Für einen lokalen Commit, den Sie nicht gepusht haben, ist git reset normalerweise das sauberste Werkzeug. Es bewegt Ihren Branch-Zeiger rückwärts. Der von Ihnen gewählte Modus bestimmt, was mit den Dateien passiert.
Verwenden Sie --soft, wenn die Commit-Nachricht falsch war oder Sie eine kleine Datei vergessen haben:
git reset --soft HEAD~1
Ihr letzter Commit verschwindet, aber die Änderungen bleiben gestaged. Sie können die fehlende Datei hinzufügen und erneut committen:
git add fehlende-datei.yml
git commit -m "Deployment-Konfiguration aktualisieren"
Verwenden Sie den standardmäßigen gemischten Reset, wenn Sie die Änderungen zurück in Ihren Arbeitsbaum haben möchten, ungestaged:
git reset HEAD~1
Das ist der alltägliche Befehl "Commit rückgängig machen, aber meine Bearbeitungen behalten". Er ist nützlich, wenn ein Commit eigentlich zwei kleinere Commits sein sollte oder wenn Sie eine Debugging-Print-Anweisung zusammen mit echtem Code committed haben.
Verwenden Sie --hard nur, wenn Sie die lokalen Änderungen wirklich verwerfen möchten:
git reset --hard HEAD~1
Dies setzt getrackte Dateien auf den früheren Commit zurück. Es fragt nicht höflich, ob Sie es ernst meinen. Wenn Sie uncommittete Arbeit in getrackten Dateien haben, kann diese aus dem Arbeitsbaum verschwinden. Überprüfen Sie zuerst git status und stashen oder branchen Sie alles, was Sie möglicherweise zurückhaben möchten.
Für eine kleine Korrektur des letzten lokalen Commits ist git commit --amend oft besser als Reset:
git add korrigierte-datei.js
git commit --amend
Das ersetzt den letzten Commit durch einen neuen. Die gleiche Regel gilt: Ändern Sie frei vor dem Pushen; seien Sie vorsichtig nach dem Pushen.
Wenn der Commit bereits gepusht wurde
Verwenden Sie auf einem gemeinsamen Branch git revert, es sei denn, Sie haben einen starken Grund, die Historie umzuschreiben. Revert erstellt einen neuen Commit, der den entgegengesetzten Patch anwendet.
git revert a1b2c3d
Dieser Befehl öffnet Ihren Editor mit einer generierten Nachricht. Speichern Sie sie, und Git erstellt einen neuen Commit. Der ursprüngliche Commit bleibt in der Historie erhalten, was genau das ist, was Sie auf main, master, develop, Release-Branches und jedem Branch, den Teammitglieder möglicherweise gezogen haben, möchten.
Wenn Sie mehrere aufeinanderfolgende Commits rückgängig machen müssen, können Sie dies in einem gestaffelten Batch tun:
git revert --no-commit HEAD~3..HEAD
git status
git commit -m "Letzte Deployment-Änderungen rückgängig machen"
Lesen Sie den Bereich sorgfältig. HEAD~3..HEAD bedeutet die letzten drei Commits, ohne HEAD~3 selbst. Wenn Sie unsicher sind, listen Sie die Commits zuerst auf:
git log --oneline HEAD~3..HEAD
Merge-Commits erfordern eine zusätzliche Entscheidung. Ein Merge hat mehr als einen Elternteil, daher muss Git wissen, welche Seite als Hauptlinie behandelt werden soll:
git revert -m 1 <merge-commit-sha>
Die meisten Teams verwenden -m 1, wenn sie einen Feature-Branch-Merge vom Ziel-Branch rückgängig machen, aber führen Sie dies nicht blind aus. Betrachten Sie den Merge-Commit mit git show --summary <sha> und bestätigen Sie die Elternreihenfolge.
Wenn Sie das Falsche gepusht haben und es entfernen müssen
Manchmal reicht Revert nicht aus. Wenn Sie ein Geheimnis, eine riesige Binärdatei oder private Kundendaten gepusht haben, entfernt ein Revert sie nur aus dem aktuellen Baum. Die sensiblen Inhalte existieren weiterhin in der Historie. Das wird zu einem Incident-Response-Problem, nicht nur zu einem Git-Aufräumproblem.
Bei Geheimnissen rotieren Sie zuerst die Anmeldeinformationen. Entfernen Sie dann die Daten aus der Historie mit einem geeigneten Historie-Umschreibwerkzeug wie git filter-repo, BFG Repo-Cleaner oder einem host-spezifischen Geheimnisentfernungsprozess. Koordinieren Sie mit dem Repository-Besitzer und gehen Sie davon aus, dass jeder mit Zugriff den schlechten Commit abgerufen haben könnte, bevor Sie ihn entfernt haben.
Für einen normalen fehlerhaften Commit in Ihrem eigenen Feature-Branch kann das Umschreiben des Remote-Branches akzeptabel sein. Setzen Sie lokal zurück und pushen Sie dann mit einem Lease:
git reset --hard HEAD~1
git push --force-with-lease origin mein-feature-branch
--force-with-lease ist die sicherere Form, da es sich weigert, das Remote zu aktualisieren, wenn jemand anderes seit Ihrem letzten Fetch neue Arbeit gepusht hat. Es ist immer noch ein Force-Push. Es schreibt den Branch immer noch um. Verwenden Sie es auf persönlichen oder koordinierten Feature-Branches, nicht leichtfertig auf gemeinsamen Branches.
Eine gute Gewohnheit vor dem Force-Pushen ist:
git fetch origin
git log --oneline --left-right --graph origin/mein-feature-branch...mein-feature-branch
Das zeigt, was nur auf dem Remote und was nur lokal existiert. Wenn die linke Seite Commits von jemand anderem enthält, stoppen Sie und sprechen Sie mit ihnen.
Wiederherstellung mit Reflog
git reflog ist der Befehl, der viele schlechte Nachmittage rettet. Er zeichnet auf, wohin Ihre lokalen HEAD- und Branch-Referenzen kürzlich gezeigt haben. Wenn Sie auf die falsche Stelle zurückgesetzt, einen Branch gelöscht oder den falschen Commit geändert haben, kennt Reflog oft den alten Commit-SHA.
git reflog
Sie könnten so etwas sehen:
7cc8a91 HEAD@{0}: reset: moving to HEAD~1
2b41f0d HEAD@{1}: commit: add retry around deploy step
Um den alten Commit wiederherzustellen, erstellen Sie einen Branch daran:
git branch wiederhergestellte-deploy-arbeit 2b41f0d
Ich bevorzuge es, einen Branch zu erstellen, anstatt sofort einen weiteren harten Reset durchzuführen. Es gibt Ihnen einen stabilen Griff, und Sie können die wiederhergestellte Arbeit in Ruhe inspizieren:
git show wiederhergestellte-deploy-arbeit
git switch wiederhergestellte-deploy-arbeit
Reflog ist lokal. Das Reflog Ihres Teamkollegen wird Ihre verlorenen lokalen Commits nicht enthalten, und Remote-Hosts bieten möglicherweise nicht denselben Wiederherstellungspfad. Es wird auch im Laufe der Zeit gemäß den Garbage-Collection-Einstellungen von Git bereinigt, behandeln Sie es also als aktuelles Sicherheitsnetz, nicht als Archivspeicher.
Ein praktischer Entscheidungsleitfaden
Wenn Sie zu früh committed und nicht gepusht haben, verwenden Sie git reset --soft HEAD~1 oder git reset HEAD~1.
Wenn Sie die falsche Datei committed haben und die Bearbeitungen lokal verschwinden sollen, verwenden Sie git reset --hard HEAD~1 nur nach Überprüfung von git status.
Wenn der schlechte Commit bereits auf einem gemeinsamen Branch ist, verwenden Sie git revert <sha>.
Wenn Ihr Feature-Branch remote ist, aber nur Sie ihn verwenden, ist git reset plus git push --force-with-lease normalerweise akzeptabel.
Wenn der Commit ein Geheimnis oder sensible Daten enthält, verlassen Sie sich nicht auf Revert. Rotieren Sie das Geheimnis, schreiben Sie die Historie mit dem richtigen Werkzeug um und koordinieren Sie die Bereinigung.
Der sicherste Git-Undo-Workflow ist langweilig: zuerst inspizieren, einen Backup-Branch erstellen, den Befehl basierend darauf wählen, ob der Commit geteilt wird, und Reflog verwenden, wenn Sie sich von Ihrem eigenen Wiederherstellungsversuch erholen müssen.