Zurücksetzen, Auschecken und Rückgängigmachen
Die Befehle git reset
, git checkout
und git revert
zählen zu den nützlichsten Werkzeugen in deinem Git-Repertoire. Sie dienen alle dazu, eine bestimmte Art Änderung in deinem Repository rückgängig zu machen. Die beiden ersten Befehle kannst du entweder auf Commits oder einzelne Dateien anwenden.
Das sie sehr ähnlich sind, kommt es bei der Auswahl des richtigen Befehls für das jeweilige Entwicklungsszenario leicht zu Verwechslungen. In diesem Artikel vergleichen wir die geläufigsten Konfigurationen von git reset
, git checkout
und git revert
. Jetzt kannst du hoffentlich mit all diesen Befehlen sicher durch dein Repository navigieren.
Es ist hilfreich, sich vorzustellen, wie sich ein Befehl auf die drei Mechanismen des Statusmanagements in einem Git-Repository auswirkt: auf das Arbeitsverzeichnis, den gestagten Snapshot und den Commit-Verlauf. Diese Komponenten werden auch als "die drei Bäume von Git" bezeichnet. Auf der Seite zu git reset
gehen wir genauer auf die drei Bäume ein. Behalte diese Mechanismen beim weiteren Lesen im Hinterkopf.
Beim Checkout wird der HEAD
-Ref-Pointer zu einem bestimmten Commit verschoben. Dieser Vorgang wird im folgenden Beispiel veranschaulicht:

In diesem Beispiel sehen wir eine Reihe an Commits im main
-Branch. Die HEAD
-Referenz und die main
-Branch-Referenz verweisen derzeit auf Commit d. Führen wir nun git checkout b
aus.

Das ist ein Update am "Commit-Verlauf"-Baum. Der Befehl git checkout
ist auf Commit- oder Dateiebene anwendbar. Bei einem Checkout auf Dateiebene wird der Inhalt der Datei mit dem des entsprechenden Commit aktualisiert.
Beim Rückgängigmachen mit "revert" wird ein neuer Commit erstellt, der den angegebenen Commit umkehrt. git revert
ist nur auf Commit-Ebene anwendbar und funktioniert nicht auf Dateiebene.
Beim Zurücksetzen mit "reset" werden die "drei Bäume" auf den Zustand des Repositorys zurückgesetzt, der einem bestimmten Commit entspricht. Für die drei Bäume gibt es drei verschiedene Arten des Zurücksetzens.
"Checkout" und "reset" werden normalerweise verwendet, um lokale oder private Änderungen rückgängig zu machen. Dadurch wird der Verlauf eines Repositorys verändert, was beim Pushen zu gemeinsam genutzten Remote-Repositorys zu Konflikten führen kann. Das Rückgängigmachen mit "revert" gilt als sichere Methode zum Zurücksetzen öffentlicher Änderungen, da ein neuer Verlauf erstellt wird, der remote gemeinsam genutzt werden kann. Der ursprüngliche Verlauf, der eventuell von Mitgliedern des Remote-Teams genutzt wird, wird so nicht überschrieben.
Im Überblick: git reset vs. revert vs. checkout
In der folgenden Tabelle werden die häufigsten Anwendungen für diese Befehle zusammengefasst. Im Laufe deiner Arbeit mit Git wirst du sicherlich ein paar dieser Befehle gebrauchen können. Halte diesen Überblick also stets griffbereit.
Befehl | Umfang | Häufige Anwendungsfälle |
---|---|---|
git reset | Commit-Ebene | Verwerfen von Commits in einem privaten Branch oder von noch nicht committeten Änderungen |
git reset | Dateiebene | Entfernen einer Datei aus der Staging-Umgebung |
git checkout | Commit-Ebene | Wechseln zwischen Branches oder Ansehen alter Snapshots |
git checkout | Dateiebene | Verwerfen von Änderungen im Arbeitsverzeichnis |
git revert | Commit-Ebene | Rückgängigmachen von Commits in einem öffentlichen Branch |
git revert | Dateiebene | Nicht verfügbar |
Vorgänge auf Commit-Ebene
Mit den Parametern, die du auf git reset
und git checkout
anwendest, legst du den jeweiligen Umfang fest. Wenn du als Parameter keinen Dateipfad angibst, wird der Befehl auf ganze Commits angewendet. Darum geht es in diesem Abschnitt. Beachte, dass es zu git revert
kein Gegenstück gibt, das auf Dateiebene funktioniert.
Einen bestimmten Commit mit "reset" zurücksetzen
Auf Commit-Ebene ist das Zurücksetzen eine Möglichkeit, die Spitze eines Branch zu einem anderen Commit zu verschieben. Hiermit können Commits vom aktuellen Branch entfernt werden. Der folgende Befehl verschiebt z. B. den hotfix
Branch zwei Commits zurück.
git checkout hotfix git reset HEAD~2
Die beiden Commits am Ende von hotfix
sind nun verwaiste Commits, d. h. von diesem Branch losgelöst. Sie werden bei der nächsten Speicherbereinigung von Git gelöscht. Anders ausgedrückt: Damit gibst du an, dass diese Commits verworfen werden sollen. Das lässt sich wie folgt veranschaulichen:

Mit git reset
lassen sich auf einfache Weise Änderungen rückgängig machen, die noch nicht mit anderen geteilt wurden. Dies ist der Befehl erster Wahl, wenn du mit der Arbeit an einem Feature begonnen hast und dir plötzlich denkst: "Ach, du meine Güte, was mache ich hier eigentlich? Ich sollte besser ganz von vorne anfangen."
Neben dem Verschieben des aktuellen Branch, kannst du über git reset
mit den folgenden Zusätzen auch den Snapshot aus der Staging-Umgebung und/oder das Arbeitsverzeichnis ändern:
--soft
: Der Snapshot der Staging-Umgebung und das Arbeitsverzeichnis bleiben unverändert.--mixed
: Der Snapshot aus der Staging-Umgebung wird zur Übereinstimmung mit dem angegebenen Commit aktualisiert, aber das Arbeitsverzeichnis bleibt unberührt. Dies ist die Standardoption.--hard
: Der gestagte Snapshot und das Arbeitsverzeichnis werden auf den angegebenen Commit zurückgesetzt.
Die Vorstellung, dass diese Flags den Umfang des git reset
-Vorgangs festlegen, erleichtert dir womöglich das Verständnis. Ausführlichere Erklärungen findest du auf der Seite git reset
.
Ältere Commits auschecken
Mit dem Befehl git checkout
wird das Repository aktualisiert, um dem Zustand an einer bestimmten Stelle des Projektverlaufs zu entsprechen. Wenn du dabei einen Branch-Namen angibst, kannst du zwischen verschiedenen Branches wechseln.
git checkout hotfix
Der obige Befehl verschiebt den HEAD
lediglich auf einen anderen Branch und aktualisiert das Arbeitsverzeichnis entsprechend. Das birgt die Gefahr, dass lokale Änderungen überschrieben werden. Deshalb zwingt dich Git, alle Änderungen im Arbeitsverzeichnis, die während des Checkouts verloren gehen, zu committen oder zu stashen. Mit git checkout
werden – im Gegensatz zu git reset
– Branches nicht verschoben.

Du kannst beliebige Commits auch auschecken, indem du die Commit-Referenz anstelle des Branches anwendest. Das Ergebnis ist genau dasselbe wie beim Auschecken eines Branches: Die HEAD
-Referenz wird zum angegebenen Commit verschoben. Der folgende Befehl zum Beispiel checkt den überübergeordneten Commit des aktuellen Commits aus:
git checkout HEAD~2
Das ist nützlich, wenn du einen schnellen Blick in eine ältere Version deines Projekts werfen willst. Da jedoch keine Branch-Referenz zum aktuellen HEAD
besteht, befindest du dich in einem Zustand mit losgelöstem HEAD
(im Englischen "detached HEAD"). Das kann riskant sein, wenn du einen neuen Commit hinzufügen willst. Wenn du dann zu einem anderen Branch wechselst, kannst du anschließend nicht mehr zum HEAD zurückkehren. Daher solltest du immer einen neuen Branch erstellen, bevor du Commits zu einem losgelösten HEAD
hinzufügst.
Öffentliche Commits mit "revert" rückgängig machen
Mit "revert" machst du einen Commit rückgängig, indem ein neuer Commit erstellt wird. Das ist eine sichere Methode zum Rückgängigmachen von Änderungen, denn dabei ist ausgeschlossen, dass der Commit-Verlauf überschrieben wird. Zum Beispiel werden mit folgendem Befehl die Änderungen im zweitletzten Commit ermittelt, ein neuer Commit erstellt, der diese Änderung rückgängig macht, und der neue Commit dem bestehenden Projekt angehängt.
git checkout hotfix git revert HEAD~2
Das lässt sich wie folgt veranschaulichen:
Im Gegensatz hierzu wird mit git reset
der bestehende Commit-Verlauf verändert. Deshalb sollte git revert
zum Rückgängigmachen von Änderungen an einem öffentlichen Branch genutzt werden und git reset
sollte dem Rückgängigmachen von Änderungen an einem privaten Branch vorbehalten bleiben.
Du kannst dir git revert
auch als Tool zum Rückgängigmachen von committeten Änderungen und git reset HEAD
zum Rückgängigmachen von nicht committeten Änderungen merken.
Bei git revert
, wie auch schon bei git checkout
, besteht die Gefahr, dass Dateien im Arbeitsverzeichnis überschrieben werden. Daher musst du Änderungen, die während des "revert"-Vorgangs verloren gehen könnten, committen oder stashen.
Vorgänge auf Dateiebene
Die Befehle git reset
und git checkout
akzeptieren auch einen optionalen Dateipfad als Parameter. Dies ändert ihr Verhalten erheblich. Anstatt auf ganze Snapshots werden sie nur auf einzelne Dateien angewendet.
Eine bestimmte Datei mit "git reset" zurücksetzen
In Kombination mit einem Dateipfad aktualisiert git reset
den Snapshot aus der Staging-Umgebung, damit dieser mit der Version des angegebenen Commits übereinstimmt. Mit diesem Befehl wird z. B. die Version von foo.py
im zweitletzten Commit abgerufen und in die Staging-Umgebung überführt, damit sie in den nächsten Commit aufgenommen wird.
git reset HEAD~2 foo.py
Wie die Version von git reset
auf Commit-Ebene wird dies eher mit HEAD
verwendet als mit einem beliebigen Commit. Das Ausführen von git reset HEAD foo.py
entfernt foo.py
aus der Staging-Umgebung. Die darin enthaltenen Änderungen sind weiterhin im Arbeitsverzeichnis vorhanden.
Die Optionen --soft
, --mixed
und --hard
wirken sich nicht auf die Version auf Dateiebene von git reset
aus, da der Snapshot in der Staging-Umgebung immer aktualisiert wird und das Arbeitsverzeichnis niemals aktualisiert wird.
Eine Datei mit "git checkout" auschecken
Das Auschecken einer Datei ähnelt der Nutzung von git reset
mit einem Dateipfad, allerdings wird statt dem Staging-Bereich das Arbeitsverzeichnis aktualisiert. Anders als bei der Commit-Level-Version dieses Befehls wird die HEAD
Referenz nicht bewegt, sodass du nicht zwischen Branches wechseln kannst.
Der folgende Befehl sorgt beispielsweise dafür, dass foo.py
im Arbeitsverzeichnis an den zweitletzten Commit angeglichen wird:
git checkout HEAD~2 foo.py
Genauso wie bei der Ausführung von git checkout
auf Commit-Ebene können hiermit alte Versionen eines Projekts eingesehen werden, aber der Anwendungsbereich ist auf die spezifizierte Datei beschränkt.
Wenn du die ausgecheckte Datei in die Staging-Umgebung verschiebst und committest, führt dies dazu, dass du sie auf die alte Dateiversion zurücksetzt. Beachte, dass dabei alle nachfolgenden Änderungen an der Datei entfernt werden, während beim Befehl git revert
nur die Änderungen rückgängig gemacht werden, die vom angegebenen Commit eingeführt wurden.
Wie git reset
wird dies üblicherweise mit HEAD
als Commit-Referenz verwendet. Beispielsweise werden mit git checkout HEAD foo.py
die Änderungen an foo.py
verworfen, die sich nicht in der Staging-Umgebung befinden. Dieses Verhalten ähnelt git reset HEAD --hard
, bezieht sich aber nur auf die spezifizierte Datei.
Zusammenfassung
Du solltest nun alle Werkzeuge kennen, die du zum Rückgängigmachen von Änderungen in einem Git-Repository benötigst. Die Befehle git reset
, git checkout
und git revert
können Verwirrung stiften. Doch wenn du weißt, welche Auswirkungen sie auf das Arbeitsverzeichnis, den gestagten Snapshot und den Commit-Verlauf haben, kannst du dir leichter merken, welcher Befehl für deine Entwicklungsaufgabe der passende ist.