Vergleich

git reset vs. git revert: Commit rückgängig machen, ohne die Team-Historie zu zerstören

Aktualisiert: · Skillaria.Top

git reset verschiebt den Branch-Zeiger auf einen anderen Commit und schreibt die Historie um – daher nur für lokale, noch nicht gepushte Commits geeignet. git revert erzeugt einen neuen Commit mit den gegenläufigen Änderungen und lässt die Historie unangetastet. Damit ist es der sichere Weg, einen Commit zurückzunehmen, der schon auf dem Server liegt.

Kurz gesagt: git reset spult den Branch zurück und tut so, als hätte es die überflüssigen Commits nie gegeben. git revert lässt die Historie, wie sie ist, und fügt einen neuen Commit hinzu, der die Änderungen eines alten aufhebt. Ersteres eignet sich für die lokale Arbeit, Letzteres für alles, was bereits im gemeinsamen Repository gelandet ist. Wenn Sie sich nur diese eine Regel merken, vermeiden Sie die meisten Git-Unfälle.

Im Folgenden sehen wir uns an, wie beide Befehle funktionieren, worin sich die reset-Modi unterscheiden, wie Sie einen Commit nach dem Push rückgängig machen, wie Sie „verlorene“ Commits zurückholen und was git clean damit zu tun hat.

Drei Ebenen, die Undo-Befehle verändern

Um den Unterschied zu verstehen, erinnern Sie sich an die drei Bereiche von Git: das Arbeitsverzeichnis (Dateien auf der Platte), den Index (Staging Area, also das, was in den nächsten Commit kommt) und die Historie (die Commits, auf die Branch und HEAD zeigen). Jeder Undo-Befehl wirkt auf eine eigene Kombination dieser Bereiche.

BefehlHistorieIndexArbeitsverzeichnisSicher auf gemeinsamem Branch
git restore dateineinneinjaja
git restore --staged dateineinjaneinja
git reset --softjaneinneinnein
git reset --mixed (Standard)jajaneinnein
git reset --hardjajajanein
git revertfügt Commit hinzu——ja
git cleanneinneinlöscht untracked Dateienja, aber unwiderruflich

So funktioniert git reset

Der Befehl git reset <commit> verschiebt den Zeiger des aktuellen Branches (und mit ihm HEAD) auf den angegebenen Commit. Die Commits „danach“ werden physisch nicht gelöscht, sind im Branch aber nicht mehr sichtbar. Der Modus bestimmt, was mit Index und Dateien passiert.

git reset --soft: Commit zurücknehmen, Änderungen bleiben im Index

Der Branch wird verschoben, Index und Dateien bleiben unverändert. Alle Änderungen aus den zurückgenommenen Commits liegen fertig gestaged vor. Ideal, um eine Commit-Message neu zu schreiben oder die letzten Commits zu einem zusammenzufassen.

git reset --soft HEAD~3
git commit -m "feat: Registrierungsformular"

Aus den letzten drei Commits ist einer geworden, der Dateiinhalt hat sich nicht verändert.

git reset --mixed: Commit und Index zurücksetzen

Der Standardmodus: git reset HEAD~1 ist gleichbedeutend mit git reset --mixed HEAD~1. Der Branch wird verschoben, der Index zurückgesetzt, die Änderungen bleiben als ungestaged im Arbeitsverzeichnis. Praktisch, wenn Sie zu viel committet haben und neu entscheiden wollen, was Sie in welchen Portionen hinzufügen.

git reset HEAD~1
git status
# Changes not staged for commit:
#   modified:   app.py
#   modified:   config.yml

git reset --hard: komplett zurückrollen

Branch, Index und Arbeitsverzeichnis werden auf den Stand des angegebenen Commits gebracht. Alle nicht committeten Änderungen an getrackten Dateien gehen ohne Warnung verloren. Das ist der einzige reset-Modus, der Ihre Arbeit tatsächlich vernichten kann.

git reset --hard HEAD~1          # letzten Commit und alle Änderungen verwerfen
git reset --hard origin/main     # Branch auf den Stand des Servers bringen

Vor --hard lohnt sich ein git status und, falls etwas Wertvolles dabei ist, ein git stash. Committetes lässt sich zurückholen (mehr dazu unten), nicht Committetes nicht.

git reset für eine einzelne Datei

Mit einem Pfad bewegt reset den Branch nicht, sondern ändert nur den Index: git reset app.py nimmt die Datei aus dem Staging. Das moderne Pendant ist git restore --staged app.py – es ist verständlicher. Die Flags --soft und --hard funktionieren mit Pfaden nicht.

So funktioniert git revert

git revert <commit> ermittelt die Änderungen des angegebenen Commits, wendet sie umgekehrt an und erstellt einen neuen Commit. Hinzugefügte Zeilen werden entfernt, entfernte wieder eingefügt. Die Historie wächst nach vorn, nichts wird umgeschrieben – Ihre Kollegen müssen bei sich also nichts reparieren.

git log --oneline
# a3f9c21 (HEAD -> main) docs: README
# 7be41d0 feat: Antwort-Caching
# 1c02e8f fix: Request-Timeout

git revert 7be41d0
# [main 5d8e2aa] Revert "feat: Antwort-Caching"
#  2 files changed, 3 insertions(+), 41 deletions(-)

Git öffnet einen Editor mit einer Nachricht der Form Revert "..." und der Zeile This reverts commit 7be41d0.... Gute Praxis: ergänzen Sie, warum die Änderung zurückgenommen wurde.

Nützliche Flags für revert

  • --no-edit – Standardnachricht ohne Editor übernehmen.
  • -n (--no-commit) – die gegenläufigen Änderungen nur in den Index schreiben, ohne Commit. So können Sie mehrere Commits mit einem einzigen Commit zurücknehmen.
  • git revert A..B – einen Bereich von Commits zurücknehmen (jeweils mit eigenem Revert-Commit, von neu nach alt).
  • -m 1 – Pflicht bei Merge-Commits: legt fest, welcher Parent als Hauptlinie gilt.
git revert --no-commit HEAD~3..HEAD
git commit -m "Revert: drei Commits des Experiments zurückgenommen"

Merge-Commit reverten

Ein Merge hat zwei Parents, und Git weiß nicht, relativ zu welchem die „umgekehrten“ Änderungen berechnet werden sollen. Ohne -m gibt es daher den Fehler is a merge but no -m option was given. Meist ist der Hauptbranch der erste Parent:

git revert -m 1 9f1e2ab

Der Haken: Wollen Sie denselben Branch später erneut mergen, hält Git dessen Commits für bereits gemergt und bringt die Änderungen nicht mit. Die Lösung: Zuerst den Revert-Commit selbst reverten und danach die neuen Änderungen einspielen.

git reset oder git revert – was nehmen?

Kriteriumgit resetgit revert
Was passiert mit der Historiewird umgeschrieben (Commits verschwinden aus dem Branch)ein neuer Commit kommt hinzu
Commit ist bereits gepushtpush --force nötig, bricht die Arbeit der Kollegennormales git push
Commit aus der Mitte der Historie zurücknehmennur zusammen mit allen folgendengezielt möglich
Spur in der Historiebleibt keinebleibt, man sieht, wer was warum zurückgerollt hat
Typisches Szenariolokale Commits vor dem Push aufräumenCommit in main zurückrollen, der die Produktion kaputt gemacht hat

Die einfache Regel: Hat außer Ihnen schon jemand den Commit gesehen – revert, wenn nicht – reset. Ausnahme: Ihr persönlicher Feature-Branch, den niemand gepullt hat. Dort ist es in Ordnung, die Historie umzuschreiben und git push --force-with-lease zu verwenden.

Warum reset --hard auf einem gemeinsamen Branch keine gute Idee ist

Stellen Sie sich vor: Sie führen in main git reset --hard HEAD~2 und git push --force aus. Bei den Kollegen existieren diese zwei Commits weiterhin. Beim nächsten git pull versucht Git, deren Branch mit Ihrem zusammenzuführen – und die „gelöschten“ Commits kehren zurück, diesmal mit einem überflüssigen Merge-Commit. Noch schlimmer, wenn jemand zwischenzeitlich eigene Commits gepusht hat: Der Force-Push überschreibt sie einfach.

Deshalb ist Force-Push auf geschützten Branches (main, release) in den Hosting-Einstellungen meist verboten. Wenn es ohne Umschreiben nicht geht, verwenden Sie git push --force-with-lease: Es verweigert den Push, falls auf dem Server Commits aufgetaucht sind, die Sie noch nicht kennen.

Typische Undo-Szenarien

Letzten Commit rückgängig machen, der noch nicht gepusht ist

git reset --soft HEAD~1   # Änderungen im Index, neu committen möglich
git reset HEAD~1          # Änderungen in den Dateien, Index leer
git reset --hard HEAD~1   # Änderungen verworfen

Wenn Sie nur die Message korrigieren oder eine vergessene Datei ergänzen wollen, brauchen Sie kein reset: git add vergessen.txt und git commit --amend.

Commit nach dem Push rückgängig machen

git revert <hash>
git push

Das ist auch die Antwort auf die häufige Suche nach „git reverse commit“: Einen Befehl git reverse gibt es in Git nicht – den „umgekehrten“ Commit erzeugt git revert.

Branch auf den Stand des Servers zurücksetzen

git fetch origin
git reset --hard origin/main

Hilfreich, wenn sich der lokale Branch nach einem misslungenen Rebase oder Experimenten verheddert hat. Alle lokalen Commits, die nicht auf dem Server sind, verschwinden aus dem Branch.

Commits nach einem missglückten reset wiederherstellen

Git protokolliert jede Bewegung von HEAD im reflog. Commits bleiben nach einem reset mindestens einige Wochen in der Objektdatenbank erhalten (standardmäßig werden nicht erreichbare reflog-Einträge 30 Tage aufbewahrt).

git reflog
# 1c02e8f HEAD@{0}: reset: moving to HEAD~2
# a3f9c21 HEAD@{1}: commit: docs: README
# 7be41d0 HEAD@{2}: commit: feat: Antwort-Caching

git reset --hard a3f9c21   # oder HEAD@{1}

Das reflog ist lokal: Es existiert nur in Ihrem Klon und wird nicht auf den Server übertragen.

git clean: untracked Dateien löschen

git reset --hard rührt Dateien nicht an, die Git nicht trackt: neue, nie gestagete Dateien, Build-Artefakte, Logs. Dafür gibt es git clean. Der Befehl ist unwiderruflich – was er löscht, landet weder im reflog noch im Papierkorb.

git clean -n      # Dry Run: anzeigen, was gelöscht würde
git clean -f      # untracked Dateien löschen
git clean -fd     # dazu untracked Verzeichnisse
git clean -fdx    # plus ignorierte Dateien (.gitignore): node_modules, dist
git clean -i      # interaktive Auswahl

Ohne -f verweigert Git standardmäßig den Dienst (Einstellung clean.requireForce). Beginnen Sie immer mit -n, besonders bei -x: Damit verschwindet auch Ihre lokale .env, falls sie ignoriert wird.

Die Kombination für einen kompletten Reset auf den letzten Commit: git reset --hard && git clean -fd.

Häufige Fehler

  • reset --hard statt --soft. Committetes rettet das reflog, nicht Committetes ist verloren. Die Gewohnheit, vor Experimenten git stash auszuführen, löst das Problem.
  • revert ohne Blick auf Abhängigkeiten. Haben spätere Commits dieselben Zeilen geändert, führt revert zu einem Konflikt. Lösen Sie ihn wie gewohnt: Dateien korrigieren, git add, git revert --continue; anders überlegt – git revert --abort.
  • restore und reset verwechselt. Um Änderungen an einer Datei zu verwerfen, müssen Sie nicht den ganzen Branch resetten: git restore datei.
  • Force-Push nach main. Lieber revert und ein normaler Push, auch wenn die Historie etwas länger wird.

Fazit

git reset ist das Werkzeug für Ihre eigene, noch nicht veröffentlichte Historie: --soft behält die Änderungen im Index, --mixed in den Dateien, --hard wirft alles weg. git revert ist das sichere Rückgängigmachen auf gemeinsamen Branches und hinterlässt eine ehrliche Spur in der Historie. git clean räumt untracked Dateien auf, verzeiht aber keine Fehler. Undo-Szenarien mit restore, reset und revert üben Sie praktisch im Git-Kurs.

Häufige Fragen

Löscht git reset Commits endgültig?

Nein, die Commits bleiben in der Objektdatenbank und sind über git reflog erreichbar, bis die Garbage Collection sie entfernt – bei nicht erreichbaren Einträgen standardmäßig nach 30 Tagen.

Kann man ein git revert rückgängig machen?

Ja, ein Revert-Commit ist ein ganz normaler Commit. Sie können ihn mit einem weiteren git revert zurücknehmen oder, falls er noch nicht gepusht ist, per git reset entfernen.

Was unterscheidet git reset von git checkout?

git reset verschiebt den Zeiger des aktuellen Branches, während git checkout (bzw. git switch) HEAD auf einen anderen Branch oder Commit umstellt, ohne den Branch selbst zu verändern.

Was bedeuten HEAD~1 und HEAD^?

Beide bezeichnen den Parent des aktuellen Commits; HEAD~3 ist der dritte Vorfahre entlang der ersten Parents, HEAD^2 der zweite Parent eines Merge-Commits.

Wie mache ich git clean rückgängig?

Mit Git-Mitteln gar nicht: Die gelöschten untracked Dateien waren nie im Repository gespeichert. Führen Sie deshalb immer zuerst git clean -n aus.

Das Thema im Video lernen

Dieses Thema wird im Kurs „Git vom Anfänger zum versierten Entwickler“ ausführlich mit Praxis behandelt.

Zum Kurs