git reset
Der Befehl git reset
ist ein komplexes und vielseitiges Werkzeug zum Rückgängigmachen von Änderungen. Diesen Befehl kannst du auf dreierlei Arten aufrufen. Das lässt sich mit folgenden Befehlszeilenargumenten ausdrücken: --soft, --mixed, --hard
. Die drei Argumente entsprechen jeweils den drei Git-internen Mechanismen des Zustandsmanagements, nämlich dem Commit-Baum (HEAD
), dem Staging-Index und dem Arbeitsverzeichnis.
git reset und die drei Bäume von Git
Um die richtige Anwendung von git reset
zu verstehen, müssen wir daher zunächst die internen Statusmanagementsysteme in Git nachvollziehen. Manchmal werden diese Mechanismen die "drei Bäume" von Git genannt. "Baum" ist vielleicht nicht das ideale Wort, denn es handelt sich hier streng genommen nicht um herkömmliche Baumstrukturen von Daten. Git nutzt jedoch Knoten- und Pointer-basierte Datenstrukturen, um den zeitlichen Verlauf von Veränderungen aufzuzeichnen. Am besten lassen sich diese Mechanismen veranschaulichen, wenn wir in einem Repository ein Changeset erstellen und dieses durch die drei Bäume verfolgen.
Zunächst einmal erstellen wir mit dem folgenden Befehl ein neues Repository:
$ mkdir git_reset_test
$ cd git_reset_test/
$ git init .
Initialized empty Git repository in /git_reset_test/.git/
$ touch reset_lifecycle_file
$ git add reset_lifecycle_file
$ git commit -m"initial commit"
[main (root-commit) d386d86] initial commit
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 reset_lifecycle_file
Im Code-Beispiel oben wird ein neues Git-Repository mit einer einzigen leeren Datei, reset_lifecycle_file
, erstellt. Zu diesem Zeitpunkt gibt es in dem Beispiel-Repository nach dem Hinzufügen von reset_lifecycle_file
einen einzigen Commit (d386d86
).
Zugehöriges Material
Git-Merkzettel
Lösung anzeigen
Git kennenlernen mit Bitbucket Cloud
Das Arbeitsverzeichnis
Den ersten Baum, den wir nun betrachten, ist das "Arbeitsverzeichnis". Dieser Baum wird mit dem lokalen Dateisystem synchronisiert und reflektiert unmittelbar die Änderungen an Datei- und Verzeichnisinhalten.
$ echo 'hello git reset' > reset_lifecycle_file
$ git status
On branch main
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: reset_lifecycle_file
In unserem Demo-Repository ändern wir den Inhalt von reset_lifecycle_file
und fügen neuen Inhalt hinzu. Mit dem Befehl git status
sehen wir, dass Git die Änderungen an der Datei erkannt hat. Diese Änderungen gehören derzeit zum ersten Baum, dem "Arbeitsverzeichnis". Mit git status
kannst du Änderungen am Arbeitsverzeichnis anzeigen lassen. Diese werden in Rot mit dem Präfix "geändert" dargestellt.
Staging-Index
Als Nächstes befassen wir uns mit dem Baum, der sich Staging-Index nennt. In diesem Baum werden Änderungen am Arbeitsverzeichnis verfolgt, die mit git add
hinzugefügt wurden, um sie in den nächsten Commit aufzunehmen. Dieser Baum ist ein komplexer interner Caching-Mechanismus. Generell versucht Git, die Implementierungsdetail des Staging-Index vor dem Benutzer zu verbergen.
Um uns den Zustand des Staging-Index genauer anzeigen zu lassen, benötigen wir einen weniger bekannten Git-Befehl: git ls-files
. Der Befehl git ls-files
ist im Grunde genommen ein Werkzeug zur Fehlersuche, mit dem du den Zustand des Staging-Index untersuchen kannst.
git ls-files -s
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 reset_lifecycle_file
Hier haben wir git ls-files
mit der Option -s
bzw. --stage
ausgeführt. Ohne die Option -s
gibt git ls-files
nur eine Namens- und Pfadliste von Dateien aus, die zurzeit zum Index gehören. Mit der Option -s
werden zusätzliche Metadaten für die Dateien im Staging-Index angezeigt. Diese Metadaten enthalten die Modus-Bits, den Objektnamen und die Staging-Nummer der gestagten Inhalte. Hier interessiert uns nur der Objektname, nämlich der zweite Wert (d7d77c1b04b5edd5acfc85de0b592449e5303770
). Das ist ein standardmäßiger Objekt-SHA-1-Hash in Git. Dabei handelt es sich um einen Hash der Dateiinhalte. Im Commit-Verlauf werden eigene Objekt-SHAs zur Erkennung von Pointern auf Commits und Refs gespeichert. Auch im Staging-Index gibt es zum Nachverfolgen von Dateiversionen im Index eigene Objekt-SHAs.
Als Nächstes werden wir die geänderte reset_lifecycle_file
in den Staging-Index befördern.
$ git add reset_lifecycle_file
$ git status
On branch main Changes to be committed:
(use "git reset HEAD ..." to unstage)
modified: reset_lifecycle_file
Hier haben wir die Datei mit git add reset_lifecycle_file
dem Staging-Index hinzugefügt. Wenn wir jetzt git status
ausführen, wird reset_lifecycle_file
unter "Changes to be committed" in grün angezeigt. Beachte unbedingt, dass git status
keine getreue Darstellung des Staging-Index ist. Mit dem Befehl git status
werden Änderungen zwischen dem Commit-Verlauf und dem Staging-Index angezeigt. Betrachten wir den Staging-Index hier einmal genauer.
$ git ls-files -s 100644 d7d77c1b04b5edd5acfc85de0b592449e5303770 0 reset_lifecycle_file
Hier sehen wir, dass der Objekt-SHA für reset_lifecycle_file
von e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
zu d7d77c1b04b5edd5acfc85de0b592449e5303770
aktualisiert wurde.
Commit-Verlauf
Der letzte Baum ist der Commit-Verlauf. Mit dem Befehl git commit
werden Änderungen einem dauerhaften Snapshot im Commit-Verlauf hinzugefügt. In diesem Snapshot wird auch der Zustand des Staging-Index zum Commit-Zeitpunkt aufgenommen.
$ git commit -am"update content of reset_lifecycle_file"
[main dc67808] update content of reset_lifecycle_file
1 file changed, 1 insertion(+)
$ git status
On branch main
nothing to commit, working tree clean
Hier haben wir einen neuen Commit mit der Nachricht "Update des Inhalts von lebenszyklus-reset-datei
" erstellt. Dem Commit-Verlauf wurde ein Changeset hinzugefügt. git status
zeigt uns, dass an dieser Stelle an keinem Baum Änderungen ausstehen. Mit git log
wird der Commit-Verlauf angezeigt. Da wir diesen Changeset nun über die drei Bäume hinweg verfolgt haben, können wir jetzt mit git reset
arbeiten.
Wie funktioniert "git revert"?
An der Oberfläche zeigt git reset
ein ähnliches Verhalten wie git checkout
. Während git checkout
nur auf den HEAD
-Ref-Pointer angewendet wird, verschiebt git reset
den HEAD
-Ref-Pointer und den aktuellen Branch-Ref-Pointer. Das folgende Beispiel veranschaulicht dieses Verhalten:
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
und git reset b
aus und vergleichen.
git checkout b
Mit git checkout
verweist die main
-Referenz weiterhin auf d
. Die HEAD
-Referenz wurde entfernt und verweist jetzt auf Commit b
. Das Repository befindet sich nun in einem Zustand mit "losgelöstem HEAD
" ("detached HEAD").
git reset b
git reset
hingegen verschiebt sowohl den HEAD
als auch die Branch-Refs zum angegebenen Commit.
git reset
aktualisiert nicht nur die Commit-Ref-Pointer, sondern ändert auch den Zustand der drei Bäume. Ref-Pointer werden immer geändert. Das Update betrifft den dritten Baum, nämlich den Commit-Baum. Mit den Befehlszeilenargumenten --soft, --mixed
und --hard
kannst du die Änderungen am Staging-Index und am Arbeitsverzeichnis bestimmen.
Die wichtigsten Optionen
Mit git reset
werden standardmäßig implizit die Argumente --mixed
und HEAD
mitaufgerufen. Wenn du git reset
ausführst, ist das also dasselbe wie git reset --mixed HEAD
. Dabei ist HEAD
der angegebene Commit. Anstelle von HEAD
kannst du auch einen beliebigen Git-SHA-1-Commit-Hash einsetzen.
'--hard
Das ist die direkte, GEFÄHRLICHSTE und am häufigsten verwendete Option. Mit der Option --hard
werden die Ref-Pointer des Commit-Verlaufs entsprechend dem angegebenen Commit aktualisiert. Der Staging-Index und das Arbeitsverzeichnis werden dann entsprechend auf den angegebenen Commit zurückgesetzt. Alle zuvor ausstehenden Änderungen am Staging-Index und am Arbeitsverzeichnis werden auf den Zustand des Commit-Baums zurückgesetzt. Das bedeutet, dass ausstehende Code-Änderungen im Staging-Index oder im Arbeitsverzeichnis verloren gehen.
Um das zu veranschaulichen, führen wir das zuvor erstellte Beispiel-Repository mit den drei Bäumen fort. Zunächst nehmen wie einige Änderungen an dem Repository vor. Führe folgende Befehle im Beispiel-Repository aus:
$ echo 'new file content' > new_file
$ git add new_file
$ echo 'changed content' >> reset_lifecycle_file
Mit diesen Befehlen haben wir eine neue Datei mit dem Namen new_file
erstellt und sie dem Repository hinzugefügt. Außerdem wird der Inhalt der reset_lifecycle_file
geändert. Nachdem wir diese Änderungen vorgenommen haben, sehen wir uns den Status des Repositorys mit git status
an.
$ git status
On branch main
Changes to be committed:
(use "git reset HEAD ..." to unstage)
new file: new_file
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: reset_lifecycle_file
Hier haben wir die Datei mit git add reset_lifecycle_file
dem Staging-Index hinzugefügt. Wenn wir jetzt git status
ausführen, wird reset_lifecycle_file
unter "Changes to be committed" in grün angezeigt. Beachte unbedingt, dass git status
keine getreue Darstellung des Staging-Index ist. Mit dem Befehl git status
werden Änderungen zwischen dem Commit-Verlauf und dem Staging-Index angezeigt. Betrachten wir den Staging-Index hier einmal genauer.
$ git ls-files -s
100644 8e66654a5477b1bf4765946147c49509a431f963 0 new_file
100644 d7d77c1b04b5edd5acfc85de0b592449e5303770 0 reset_lifecycle_file
Wir sehen hier, dass new_file
dem Index hinzugefügt wurde. Wir haben die reset_lifecycle_file
aktualisiert, doch der Staging-Index-SHA (d7d77c1b04b5edd5acfc85de0b592449e5303770
) bleibt unverändert. Das ist das erwartete Verhalten, da wir diese Änderungen nicht mit git add
zum Staging-Index hinzugefügt haben. Diese Änderungen befinden sich im Arbeitsverzeichnis.
Nun führen wir git reset --hard
aus, um den aktuellen Zustand des Repositorys zu erfahren.
$ git reset --hard
HEAD is now at dc67808 update content of reset_lifecycle_file
$ git status
On branch main
nothing to commit, working tree clean
$ git ls-files -s
100644 d7d77c1b04b5edd5acfc85de0b592449e5303770 0 reset_lifecycle_file
Hier haben wir mit der Option --hard
eine vollständige Zurücksetzung durchgeführt. Der Git-Output zeigt an, dass HEAD
auf den neuesten Commit, dc67808
, verweist. Als Nächstes überprüfen wir den Zustand des Repositorys mit git status
. Git meldet keine ausstehenden Änderungen. Wir kontrollieren auch den Zustand des Staging-Index und sehen, dass dieser auf einen Zeitpunkt zurückgesetzt wurde, an dem new_file
noch nicht hinzugefügt worden war. Unsere Änderung an reset_lifecycle_file
und das Hinzufügen von new_file
wurden gelöscht. Eines sollte dir unbedingt bewusst sein: Dieser Datenverlust kann nicht rückgängig gemacht werden.
'--mixed
So lautet der standardmäßige Betriebsmodus. Die Ref-Pointer werden aktualisiert. Der Staging-Index wird auf den Zustand des angegebenen Commits zurückgesetzt. Alle Änderungen, die im Staging-Index rückgängig gemacht wurden, werden in das Arbeitsverzeichnis verschoben. Machen wir weiter.
$ echo 'new file content' > new_file
$ git add new_file
$ echo 'append content' >> reset_lifecycle_file
$ git add reset_lifecycle_file
$ git status
On branch main
Changes to be committed:
(use "git reset HEAD ..." to unstage)
new file: new_file
modified: reset_lifecycle_file
$ git ls-files -s
100644 8e66654a5477b1bf4765946147c49509a431f963 0 new_file
100644 7ab362db063f9e9426901092c00a3394b4bec53d 0 reset_lifecycle_file
Im Beispiel oben haben wir einige Änderungen am Repository vorgenommen. Zur Erinnerung: Wir haben eine new_file
hinzugefügt und die Inhalte der reset_lifecycle_file
geändert. Diese Änderungen wurden dann mit git add
dem Staging-Index hinzugefügt. In diesem Zustand des Repositorys führen wir nun das Zurücksetzen durch.
$ git reset --mixed
$ git status
On branch main
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: reset_lifecycle_file
Untracked files:
(use "git add ..." to include in what will be committed)
new_file
no changes added to commit (use "git add" and/or "git commit -a")
$ git ls-files -s
100644 d7d77c1b04b5edd5acfc85de0b592449e5303770 0 reset_lifecycle_file
Hier haben wir einen "gemischten Reset" zum Zurücksetzen durchgeführt. Zur Wiederholung: --mixed
ist der Standardmodus und bewirkt dasselbe wie git reset
. Sehen wir uns den Output zu git status
und git ls-files
einmal genauer an: Der Staging-Index wurde auf einen Zustand zurückgesetzt, in dem reset_lifecycle_file
die einzige Datei im Index ist. Der Objekt-SHA für reset_lifecycle_file
wurde auf die vorherige Version zurückgesetzt.
Beachte hier, dass git status
anzeigt, dass es Änderungen an reset_lifecycle_file
und eine nicht verfolgte Datei gibt: new_file
. Das ist eindeutig auf --mixed
zurückzuführen. Der Staging-Index wurde zurückgesetzt und die ausstehenden Änderungen wurden in das Arbeitsverzeichnis verschoben. Vergleichen wir das, was beim Zurücksetzen mit --hard
passiert: Der Staging-Index wurde zurückgesetzt – und auch das Arbeitsverzeichnis. Die dazugehörigen Updates sind verloren gegangen.
'--soft
Mit dem Argument --soft
werden die Ref-Pointer aktualisiert und das Zurücksetzen wird an dieser Stelle angehalten. Staging-Index und Arbeitsverzeichnis bleiben unberührt. Dieses Verhalten kann man leider nicht so leicht demonstrieren. Wir verwenden weiterhin unser Demo-Repository und bereiten uns auf einen "Soft-Reset" (Teil-Reset) vor.
$ git add reset_lifecycle_file
$ git ls-files -s
100644 67cc52710639e5da6b515416fd779d0741e3762e 0 reset_lifecycle_file
$ git status
On branch main
Changes to be committed:
(use "git reset HEAD ..." to unstage)
modified: reset_lifecycle_file
Untracked files:
(use "git add ..." to include in what will be committed)
new_file
Hier haben wir die geänderte reset_lifecycle_file
wieder mit git add
in den Staging-Index befördert. Anhand des Outputs von git ls-files
können wir die Aktualisierung des Index überprüfen. Nach der Eingabe von git status
werden "Changes to be committed" in Grün angezeigt. Die new_file
aus den vorherigen Beispielen ist im Arbeitsverzeichnis als nicht verfolgte Datei im Umlauf. Da wir diese Datei für die folgenden Beispiele nicht mehr brauchen, führen wir schnell rm new_file
aus und die Datei wird gelöscht.
In diesem Zustand des Repositorys führen wir nun einen Soft-Reset durch.
$ git reset --soft
$ git status
On branch main
Changes to be committed:
(use "git reset HEAD ..." to unstage)
modified: reset_lifecycle_file
$ git ls-files -s
100644 67cc52710639e5da6b515416fd779d0741e3762e 0 reset_lifecycle_file
Hier haben wir einen "Soft-Reset" zum Zurücksetzen durchgeführt. Wenn wir den Repository-Status mit git status
und git ls-files
anzeigen lassen, erkennen wir, dass sich nichts geändert hat. Das ist das erwartete Verhalten. Mit einem Soft-Reset wird nur der Commit-Verlauf zurückgesetzt. Standardmäßig wird git reset
mit dem HEAD
als Ziel-Commit durchgeführt. Da unser Commit-Verlauf bereits am HEAD
sitzt und wir daher implizit auf den HEAD
zurücksetzen, hat sich nichts geändert.
Um --soft
besser zu verstehen und zu benutzen, brauchen wir einen Ziel-Commit, der nicht der HEAD
ist. Die reset_lifecycle_file
ist noch im Staging-Index zwischengelagert. Erstellen wir also einen neuen Commit.
$ git commit -m"prepend content to reset_lifecycle_file"
Zu diesem Zeitpunkt sollte es in unserem Repository drei Commits geben. Machen wir einen kleinen Zeitsprung zurück zum ersten Commit. Dazu brauchen wir die ID des ersten Commits. Diese finden wir in den Ausgabedaten von git log
.
$ git log
commit 62e793f6941c7e0d4ad9a1345a175fe8f45cb9df
Author: bitbucket
Date: Fri Dec 1 15:03:07 2017 -0800
prepend content to reset_lifecycle_file
commit dc67808a6da9f0dec51ed16d3d8823f28e1a72a
Author: bitbucket
Date: Fri Dec 1 10:21:57 2017 -0800
update content of reset_lifecycle_file
commit 780411da3b47117270c0e3a8d5dcfd11d28d04a4
Author: bitbucket
Date: Thu Nov 30 16:50:39 2017 -0800
initial commit
Beachte, dass Commit-Verlaufs-IDs in jedem System eindeutig sind. Die Commit-IDs auf deiner Maschine werden also andere als in diesem Beispiel sein. Wichtig für unser Beispiel hier ist die Commit-ID 780411da3b47117270c0e3a8d5dcfd11d28d04a4
. Diese ID gehört zum "ersten Commit". Wenn wir diese ID kennen, können wir sie für unseren Soft-Reset verwenden.
Bevor wir die Zeit zurückdrehen, sollten wir zunächst den aktuellen Zustand des Repositorys überprüfen.
$ git status && git ls-files -s
On branch main
nothing to commit, working tree clean
100644 67cc52710639e5da6b515416fd779d0741e3762e 0 reset_lifecycle_file
Hier haben wir die beiden Befehle git status
und git ls-files -s
zugleich ausgeführt. Daraufhin wird angezeigt, dass Änderungen im Repository ausstehen und die reset_lifecycle_file
im Staging-Index auf dem Stand von Version 67cc52710639e5da6b515416fd779d0741e3762e
ist. Behalten wir das im Hinterkopf. Nun führen wir einen Soft-Reset aus, um zum Zustand unseres ersten Commits zurückzukehren.
$git reset --soft 780411da3b47117270c0e3a8d5dcfd11d28d04a4
$ git status && git ls-files -s
On branch main
Changes to be committed:
(use "git reset HEAD ..." to unstage)
modified: reset_lifecycle_file
100644 67cc52710639e5da6b515416fd779d0741e3762e 0 reset_lifecycle_file
Mit dem Code oben wird ein "Soft Reset" ausgeführt und die beiden Befehle git status
und git ls-files
zugleich aufgerufen. Ausgegeben wird der Status des Repositorys. Wenn wir diese Ausgabe unter die Lupe nehmen, fällt etwas Interessantes auf. Zum einen gibt git status
die Änderungen an reset_lifecycle_file
an und hebt sie hervor, um anzuzeigen, dass die Änderungen für den nächsten Commit in den Staging-Bereich überführt wurden. Zum anderen sehen wir, dass sich durch die git ls-files
-Ausgabe der Staging-Index nicht geändert hat und unser vorheriger SHA 67cc52710639e5da6b515416fd779d0741e3762e beibehalten wurde.
Mit git log
erfahren mir Genaueres darüber, was bei dieser Zurücksetzung passiert ist:
$ git log commit 780411da3b47117270c0e3a8d5dcfd11d28d04a4 Author: bitbucket Date: Thu Nov 30 16:50:39 2017 -0800 initial commit
Das Protokoll zeigt nun einen einzigen Commit im Commit-Verlauf an. Das veranschaulicht den --soft
-Vorgang sehr gut. Bei der Ausführung von git reset
-Befehlen wird immer zuerst der Commit-Baum zurückgesetzt. Unsere vorherigen Beispiele mit --hard
und --mixed
wurden beide auf den HEAD
ausgeführt und haben die Zeit für den Commit-Baum nicht zurückgedreht. Mehr passiert bei einem Soft-Reset nicht.
Das mag verwirrend erscheinen, da mit git status
geänderte Dateien angezeigt werden. Mit --soft
bleibt der Staging-Index unberührt und die Updates an unserem Staging-Index blieben uns beim Zeitsprung durch den Commit-Verlauf erhalten. Das können wir uns mit git ls-files -s
bestätigen lassen. Wir sehen dann, dass der SHA für die reset_lifecycle_file
unverändert ist. Nicht vergessen: git status
offenbart nicht den Zustand der "drei Bäume", sondern die Unterschiede zwischen ihnen. In diesem Fall sehen wir, dass der Staging-Index vor den Änderungen im Commit-Verlauf liegt, und es sieht aus, als hätten wir diese Änderungen bereits gestaged.
Zurücksetzen vs. Rückgängigmachen
Während git revert
als die "sichere" Methode zum Rückgängigmachen von Änderungen gilt, kann git reset als die gefährliche Methode bezeichnet werden. Bei git reset
besteht das ernsthafte Risiko, dass etwas verloren geht. Mit git reset
werden Commits auf keinen Fall gelöscht, allerdings können sie "verwaisen", d. h., dass kein direkter Pfad von einem Ref mehr existiert, um einen Zugriff herzustellen. Solche verwaisten Commits können normalerweise mit git reflog ausfindig gemacht und wiederhergestellt werden. Nach einer internen Speicherbereinigung durch Git werden alle verwaisten Commits dauerhaft gelöscht. Standardmäßig führt Git die Speicherbereinigung alle 30 Tage durch. Der Commit-Verlauf ist einer der "drei Git-Bäume". Staging-Index und Arbeitsverzeichnis, die anderen beiden Bäume, sind weniger dauerhaft als Commits. Da du mit diesem Git-Befehl also riskierst, Code zu verlieren, solltest du ihn nur mit Bedacht einsetzen.
Eine Rückgängigmachung ist dabei zur sicheren Aufhebung von öffentlichen Commits gedacht, git reset
hingegen zur Aufhebung lokaler Änderungen im Staging-Index und im Arbeitsverzeichnis. Da diese beiden Befehle zwei ganz unterschiedliche Funktionen haben, sind sie auch unterschiedlich implementiert: Eine Zurücksetzung entfernt ein Changeset vollständig. Bei einer Rückgängigmachung jedoch wird das ursprüngliche Changeset beibehalten und ein neuer Commit durchgeführt, um die Änderungen aufzuheben.
Öffentlichen Verlauf nicht zurücksetzen
Du solltest git reset
niemals verwenden, wenn Snapshots nach dem in "
Das Entfernen eines Commit, den andere Teammitglieder entwickelt haben, stellt ein ernstes Problem für die Zusammenarbeit dar. Wenn sie versuchen, ihn mit deinem Repository zu synchronisieren, wird es aussehen, als ob ein großer Teil des Projektverlaufs plötzlich verschwunden wäre. Die nachstehende Sequenz zeigt, was beim Versuch passiert, einen öffentlichen Commit zurückzusetzen. Der origin/main
-Branch ist die Version deines lokalen main
-Branches im zentralen Repository.
Sobald du nach der Zurücksetzung neue Commits hinzufügst, nimmt Git an, dass dein lokaler Verlauf von origin/main
abweicht. Der zur Synchronisierung deiner Repositorys erforderliche Merge-Commit wird deine Teamkollegen sehr wahrscheinlich irritieren und frustrieren.
Also: Wende git reset <commit>
nur auf lokale Code-Experimente an, die nicht wie gewünscht funktioniert haben, niemals aber auf veröffentlichte Änderungen. Wenn du einen öffentlichen Commit korrigieren musst, verwende git revert
. Dieser Befehl ist speziell für diesen Zweck vorgesehen.
Beispiele
git reset <file>
Entfernt die angegebene Datei aus der Staging-Umgebung, ohne dabei Änderungen am Arbeitsverzeichnis vorzunehmen. Damit wird die Datei aus der Staging-Umgebung entfernt, ohne Änderungen zu überschreiben.
git reset
Mit diesem Befehl setzt du die Staging-Umgebung auf den neuesten Commit zurück, lässt das Arbeitsverzeichnis jedoch unverändert. Es werden sämtliche Dateien aus der Staging-Umgebung entfernt, jedoch keine Änderungen überschrieben. So kannst du den in der Staging-Umgebung gehosteten Snapshot von Grund auf neu erstellen.
git reset --hard
Mit diesem Befehl setzt du die Staging-Umgebung und das Arbeitsverzeichnis auf den neuesten Commit zurück. Da das Flag --hard
gesetzt ist, entfernt Git nicht nur die Änderungen aus der Staging-Umgebung, sondern überschreibt auch alle Änderungen im Arbeitsverzeichnis. Anders ausgedrückt: Alle noch nicht committeten Änderungen werden vollständig gelöscht. Du solltest diesen Befehl also nur verwenden, wenn du deine gesamte lokale Entwicklungsarbeit verwerfen möchtest.
git reset
Mit diesem Befehl verschiebst du die Spitze des aktuellen Branches zurück auf den Commit
und setzt die Staging-Umgebung entsprechend zurück, belässt das Arbeitsverzeichnis jedoch unverändert. Alle Änderungen, die seit dem <commit>
vorgenommen wurden, verbleiben im Arbeitsverzeichnis. So kannst du den Projektverlauf in Form von klarer strukturierten, granulareren Snapshots erneut committen.
git reset --hard
Mit diesem Befehl verschiebst du die Spitze des aktuellen Branches zurück auf den in <commit>
angegebenen Commit und setzt sowohl die Staging-Umgebung als auch das Arbeitsverzeichnis auf dessen Stand zurück. Dadurch werden nicht nur alle nicht committeten Änderungen vollständig gelöscht, sondern auch alle nachfolgenden Commits.
Datei aus der Staging-Umgebung entfernen
Der Befehl git reset
wird oft bei der Arbeit an Snapshots in der Staging-Umgebung verwendet. Im nächsten Beispiel nehmen wir an, dass du zwei Dateien hast: hello.py
und main.py
. Beide Dateien wurden dem Repository bereits hinzugefügt.
# Edit both hello.py and main.py
# Stage everything in the current directory
git add .
# Realize that the changes in hello.py and main.py
# should be committed in different snapshots
# Unstage main.py
git reset main.py
# Commit only hello.py
git commit -m "Make some changes to hello.py"
# Commit main.py in a separate snapshot
git add main.py
git commit -m "Edit main.py"
Mit git reset
kannst du Änderungen aus der Staging-Umgebung entfernen, die keinen Bezug zum nächsten Commit haben. So kannst du sicherstellen, dass deine Commits keinen unnötigen Code enthalten.
Lokale Commits entfernen
Im nächsten Beispiel zeigen wir einen erweiterten Verwendungszweck. Es veranschaulicht, was passiert, wenn du eine Zeit lang an einem neuen Experiment gearbeitet hast, es dann aber komplett verwerfen willst, nachdem du einige Snapshots committet hast.
# Create a new file called `foo.py` and add some code to it
# Commit it to the project history
git add foo.py
git commit -m "Start developing a crazy feature"
# Edit `foo.py` again and change some other tracked files, too
# Commit another snapshot
git commit -a -m "Continue my crazy feature"
# Decide to scrap the feature and remove the associated commits
git reset --hard HEAD~2
Der Befehl git reset HEAD~2
setzt den aktuellen Branch um 2 Commits zurück. Dadurch werden die beiden gerade erstellten Snapshots aus dem Projektverlauf entfernt. Denke daran: Diese Art Zurücksetzung solltest du ausschließlich auf noch nicht veröffentlichte Commits anwenden. Verwende diesen Befehl niemals, wenn du deine Commits bereits in ein gemeinsam genutztes Repository gepusht hast.
Zusammenfassung
Zusammenfassend lässt sich sagen: git reset
ist ein leistungsstarker Befehl, der lokale Änderungen auf den Stand eines bestimmten Git-Repositorys zurücksetzt. git reset
wirkt sich auf die "drei Bäume von Git" aus. Diese Bäume sind der Commit-Verlauf (HEAD
), der Staging-Index und das Arbeitsverzeichnis. Es gibt drei Befehlszeilenoptionen, die den drei Bäumen entsprechen. Die Optionen --soft, --mixed
und --hard
können auf git reset
angewendet werden.
In diesem Artikel haben wir anhand einiger anderer Git-Befehle gezeigt, wie das Zurücksetzen abläuft. Mehr über diese Befehle erfährst du auf den jeweiligen Seiten zu git status, git log, git add, git checkout, git reflog und git revert.
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.