Resetting, checking out & reverting
The git reset, git checkout, and git revert commands are some of the most useful tools in your Git toolbox. They all let you undo some kind of change in your repository, and the first two commands can be used to manipulate either commits or individual files.
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.
It helps to think about each command in terms of their effect on the three state management mechanisms of a Git repository: the working directory, the staged snapshot, and the commit history. These components are sometimes known as "The three trees" of Git. We explore the three trees in depth on the git reset page. Keep these mechanisms in mind as you read through this article.
Beim Checkout wird der HEAD
-Ref-Pointer zu einem bestimmten Commit verschoben. Dieser Vorgang wird im folgenden Beispiel veranschaulicht:
Zugehöriges Material
Verschieben eines vollständigen Git-Repositorys
Lösung anzeigen
Git kennenlernen mit Bitbucket Cloud
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.
Git reset vs revert vs checkout reference
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 |
---|---|---|
| Umfang Commit-Ebene | Häufige Anwendungsfälle Discard commits in a private branch or throw away uncommitted changes |
| Umfang Dateiebene | Häufige Anwendungsfälle Entfernen einer Datei aus der Staging-Umgebung |
| Umfang Commit-Ebene | Häufige Anwendungsfälle Wechseln zwischen Branches oder Ansehen alter Snapshots |
| Umfang Dateiebene | Häufige Anwendungsfälle Verwerfen von Änderungen im Arbeitsverzeichnis |
| Umfang Commit-Ebene | Häufige Anwendungsfälle Rückgängigmachen von Commits in einem öffentlichen Branch |
| Umfang Dateiebene | Häufige Anwendungsfälle Nicht verfügbar |
Commit level operations
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.
Reset a specific commit
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.
It’s easier to think of these modes as defining the scope of a git reset
operation. For further detailed information visit the git reset page.
Ä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.
Undo public commits with revert
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.
File-level operations
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.
Git reset a specific file
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.
Git checkout file
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
You should now have all the tools you could ever need to undo changes in a Git repository. The git reset, git checkout, and git revert commands can be confusing, but when you think about their effects on the working directory, staged snapshot, and commit history, it should be easier to discern which command fits the development task at hand.
Diesen Artikel teilen
Nächstes Thema
Lesenswert
Füge diese Ressourcen deinen Lesezeichen hinzu, um mehr über DevOps-Teams und fortlaufende Updates zu DevOps bei Atlassian zu erfahren.