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 Zustandsmanagementsysteme 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 den folgenden Befehlen 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" [master (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 im Beispiel-Repository nach dem Hinzufügen von reset_lifecycle_file einen einzigen Commit (d386d86).

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 master 
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 "modified" (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 Implementierungsdetails 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). Dies 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 Datei reset_lifecycle_file in den Staging-Index befördern.


$ git add reset_lifecycle_file 

$ git status 

On branch master 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 diesen Snapshot wird auch der Zustand des Staging-Index zum Commit-Zeitpunkt aufgenommen.

 $ git commit -am"update content of reset_lifecycle_file" [master dc67808] update content of reset_lifecycle_file 1 file changed, 1 insertion(+) $ git status On branch master nothing to commit, working tree clean

Hier haben wir einen neuen Commit mit der Nachricht "update content of reset_lifecycle_file" 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 dieses Changeset nun über die drei Bäume hinweg verfolgt haben, können wir jetzt mit git reset arbeiten.

Wie es funktioniert

Oberflächlich betrachtet 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 von Commits im master-Branch. Der HEAD-Ref und der master-Branch-Ref 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 der master-Ref weiterhin auf d. Der HEAD-Ref wurde entfernt und verweist jetzt auf Commit b. Das Repository befindet sich nun in einem Zustand mit "losgelöstem HEAD" (im Englischen "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 beeinflussen.

Die wichtigsten Optionen

Mit git reset werden standardmäßig implizit die Argumente --mixed und HEAD mitaufgerufen. Wenn du git reset ausführst, ist dies also gleichzusetzen mit 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

Dies 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 Änderungen im Staging-Index oder im Arbeitsverzeichnis verloren gehen.

Um dies zu veranschaulichen, nutzen wir weiterhin das zuvor erstellte Beispiel-Repository mit den drei Bäumen. Zunächst nehmen wir einige Änderungen am 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 wurde der Inhalt von 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 master 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

Wir sehen, dass nun Änderungen am Repository ausstehen. Im Staging-Index-Baum steht das Hinzufügen von new_file noch aus und im Arbeitsverzeichnis steht die Änderung von reset_lifecycle_file aus.

Bevor wir fortfahren, untersuchen wir den Zustand des Staging-Index:

$ 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 reset_lifecycle_file aktualisiert, doch der Staging-Index-SHA (d7d77c1b04b5edd5acfc85de0b592449e5303770) bleibt unverändert. Dies 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 master nothing to commit, working tree clean $ git ls-files -s 100644 d7d77c1b04b5edd5acfc85de0b592449e5303770 0 reset_lifecycle_file

Hier haben wir mit der Option --hard einen "Hard Reset" (vollständige Zurücksetzung) durchgeführt. Der Git-Output zeigt an, dass der 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 master 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 new_file hinzugefügt und die Inhalte von reset_lifecycle_file geändert. Diese Änderungen wurden dann mit git add auf den Staging-Index angewendet. In diesem Zustand des Repositorys führen wir nun das Zurücksetzen durch.

$ git reset --mixed $ git status On branch master 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 von 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 dies mit dem Vorgang beim Zurücksetzen mit --hard: Dabei wurden sowohl der Staging-Index als auch das Arbeitsverzeichnis zurückgesetzt. 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. Der Staging-Index und das Arbeitsverzeichnis bleiben unberührt. Dieses Verhalten lässt sich oft schwer demonstrieren. Wir verwenden weiterhin unser Demo-Repository und bereiten es auf einen "Soft Reset" vor.


$ git add reset_lifecycle_file 

$ git ls-files -s 

100644 67cc52710639e5da6b515416fd779d0741e3762e 0 reset_lifecycle_file 

$ git status 

On branch master 

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 mit git add die geänderte Version von reset_lifecycle_file wieder 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 Datei 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, um sie zu löschen.

In diesem Zustand des Repositorys führen wir nun einen Soft Reset durch.

 $ git reset --soft $ git status On branch master 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. Dies 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 ausgeführt. Da sich unser Commit-Verlauf bereits am HEAD befand und wir implizit auf den HEAD zurückgesetzt haben, hat sich nichts geändert.

Um --soft besser zu verstehen und einzusetzen, benötigen wir einen Ziel-Commit, der nicht der HEAD ist. Die Datei 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 master 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 am Repository ausstehen und dass 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 master Changes to be committed: (use "git reset HEAD ..." to unstage) modified: reset_lifecycle_file 100644 67cc52710639e5da6b515416fd779d0741e3762e 0 reset_lifecycle_file

Mit dem Code oben werden ein "Soft Reset" zum Zurücksetzen sowie die beiden Befehle git status und git ls-files zugleich durchgeführt. Ausgegeben wird der Zustand des Repositorys. Wenn wir diesen Output unter die Lupe nehmen, fällt uns 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 gestaged wurden. Zum anderen sehen wir, dass sich durch die git ls-files-Eingabe der Staging-Index nicht geändert hat und unser vorheriger SHA 67cc52710639e5da6b515416fd779d0741e3762e beibehalten wurde.

Mit git log erfahren wir 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. Dies 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 reset_lifecycle_file unverändert ist. Zur Erinnerung: 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". Der Staging-Index und das Arbeitsverzeichnis, die anderen beiden Bäume, sind weniger dauerhaft als Commits. Da du mit diesem Git-Befehl also riskierst, programmierten Code zu verlieren, solltest du ihn nur mit Bedacht einsetzen.

Eine Rückgängigmachung ist zur sicheren Aufhebung öffentlicher 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 "" angegebenen Commit bereits in ein öffentliches Repository gepusht wurden. Sobald ein Commit veröffentlicht wurde, musst du davon ausgehen, dass deine Kollegen mit ihm arbeiten.

Das Entfernen eines Commits, den andere Teammitglieder weiterentwickelt 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/master-Branch ist die Version deines lokalen master-Branchs im zentralen Repository.

Sobald du nach der Zurücksetzung neue Commits hinzufügst, nimmt Git an, dass dein lokaler Verlauf von origin/master abweicht. Der zur Synchronisierung deiner Repositorys erforderliche Merge-Commit wird deine Teamkollegen sehr wahrscheinlich verwirren und frustrieren.

Wende also git reset 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 

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 Branchs 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 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 Branchs zurück auf den in 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 ein erweitertes Anwendungsbeispiel. Es veranschaulicht, was passiert, wenn du eine Zeit lang an einem neuen Experiment gearbeitet hast, es dann aber komplett verwerfen möchtest, 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 freigegebenes Repository gepusht hast.

Zusammenfassung

Zusammenfassung: 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: git status, git log, git add, git checkout, git reflog und git revert.

Möchtest du dich mit "git reset" vertraut machen?

Sieh dir dieses interaktive Tutorial an.

Jetzt loslegen