git reset

Git reset und die drei Bäume
Der Befehl git reset ist ein Werkzeug zum Rückgängigmachen von Änderungen. Er hat drei Aufrufvarianten, die den drei internen Zustandsverwaltungssystemen von Git entsprechen, den sogenannten drei Bäumen von Git. Diese Systeme umfassen HEAD (die Commit-Historie), den Staging-Index und das Arbeitsverzeichnis. Wir werden uns jedes dieser Systeme ansehen.
Das Arbeitsverzeichnis
Der erste Baum ist das Arbeitsverzeichnis. Es repräsentiert die Dateien im Dateisystem Ihres Computers, die dem Code-Editor zum Anwenden von Änderungen zur Verfügung stehen. Das Arbeitsverzeichnis wird als ein bestimmter Commit des ausgecheckten Projekts betrachtet. Wenn das Projekt ausgecheckt ist, bedeutet das, dass seine Dateien als dekomprimierte Versionen aus dem Git-Repository extrahiert wurden.
git reset command
echo 'hello git reset' > edited_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: edited_fileStaging-Index
Der nächste Baum ist der Staging-Index, der die im Arbeitsverzeichnis vorgemerkten Änderungen nachverfolgt. Im Allgemeinen werden die Leistungsdetails des Staging-Bereichs von Git vor den Benutzern verborgen. Manchmal werden beim Sprechen über den Staging-Bereich unterschiedliche Begriffe verwendet, wie Cache, Verzeichnis-Cache, vorgemerkte Dateien, Staging-Bereich usw.
Hier benötigen wir git ls-files. command, das als Debug-Werkzeug gilt und den Zustand des Staging-Index überprüft.
git ls-files -s
git ls-files -s
#543644 a32de29bb3c1d643328b29ae775ad8c2e48c3256 0 edited_fileCommit-Historie
Der letzte Baum ist die Commit-Historie. Der Befehl git commit schreibt Änderungen in einen dauerhaften Snapshot, der im Staging-Index abgelegt wird.
git reset, commit history
git commit -am "edit content of test_file"
#[master ab23324] edit the content of edited_file
#1 file changed, 1 insertion(+)
git status
#On branch master
#nothing to commit, working tree cleanIm obigen Beispiel sehen Sie den neuen Commit mit der Nachricht "edit content of test_file". Die Änderungen werden an die Commit-Historie angehängt. In diesem Stadium zeigt git status, dass keine bevorstehenden Änderungen an einem der Bäume vorhanden sind. Wenn Sie git log aufrufen, sehen Sie die Commit-Historie. Sobald die Änderungen über die drei Bäume vorgenommen wurden, kann git reset verwendet werden.
Wie es funktioniert
Auf den ersten Blick hat der Befehl git reset einige Ähnlichkeiten mit git checkout, da beide auf HEAD arbeiten. Der Befehl git checkout arbeitet ausschließlich mit dem HEAD-Referenzzeiger, während der Befehl git reset den HEAD-Referenzzeiger und den aktuellen Branch-Referenzzeiger verschiebt. Sie werden sein Verhalten mit der folgenden Abbildung besser verstehen:

Diese Abbildung zeigt die Abfolge der Commits auf dem master-Branch. Wie Sie sehen, zeigen die HEAD-Referenz und die master-Branch-Referenz derzeit auf Commit d. Wir werden sehen, wie sich das Bild bei git checkout b und git reset b verändert.
git checkout b
Beim Ausführen des Befehls git checkout zeigt die master-Referenz weiterhin auf Commit d. Was die HEAD-Referenz betrifft, so wurde sie verschoben und der Zeiger auf Commit b geändert. Dadurch befindet sich das Repository nun in einem Zustand des „detached HEAD“.

git reset b
Der Befehl git reset verschiebt sowohl die HEAD- als auch die Branch-Referenzen auf den definierten Commit. Außerdem ändert er den Zustand der drei Bäume. Es gibt drei Befehlszeilenargumente --soft, --mixed und --hard, die die Änderung des Staging-Index und des Arbeitsverzeichnis-Baums festlegen.

Hauptoptionen
Standardmäßig hat der Befehl git reset die festen Argumente --mixed und HEAD. Das Aufrufen von git reset ist also dasselbe wie git reset --mixed HEAD. Hier ist HEAD der angegebene Commit. Sie können stattdessen jeden beliebigen Git-SHA-1-Commit-Hash verwenden.

--hard
Die am häufigsten verwendete Option ist --hard. Ihre Verwendung birgt jedoch einige Risiken. Mit --hard beginnen die Referenzzeiger der Commit-Historie auf den angegebenen Commit zu zeigen. Danach werden der Staging-Index und das Arbeitsverzeichnis so zurückgesetzt, dass sie dem angegebenen Commit entsprechen. Änderungen, die zuvor im Staging-Index und im Arbeitsverzeichnis ausstanden, werden zurückgesetzt, um dem Zustand des Commit-Baums zu entsprechen. Alle ausstehenden Änderungen im Staging-Index und Arbeitsverzeichnis gehen verloren. Das folgende Beispiel zeigt das oben Beschriebene. Führen Sie zunächst die folgenden Befehle aus:
git reset usage
echo 'test content' > test_file
git add test_file
echo 'modified content' >> edited_fileEine neue Datei namens test_file wurde erstellt und dem Repository hinzugefügt. Außerdem wird der Inhalt von edited_file geändert. Prüfen wir nun den Zustand des Repositorys mit diesen Änderungen mit dem Befehl git status.
git status
git status
#On branch master
#Changes to be committed:
#(use "git reset HEAD ..." to unstage)
#new file: test_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: edited_fileWie Sie sehen, gibt es nun einige ausstehende Änderungen. Die ausstehende Änderung für den Staging-Index-Baum ist das Hinzufügen von test_file, und die für das Arbeitsverzeichnis sind Änderungen an edited_file. Sehen wir uns nun den Zustand des Staging-Index an:
git ls-files -s
git ls-files -s
#123126 7a32454a5477b1bf4765946147c49509a431f963 0 test_file
#123126 6c423c1b04b5edd5acfc85de0b592449e5303773 0 edited_filetest_file wurde zum Index hinzugefügt. edited_file wurde aktualisiert, aber der SHA des Staging-Index (d7d77c1b04b5edd5acfc85de0b592449e5303770) bleibt derselbe. Diese Änderungen befinden sich im Arbeitsverzeichnis. Sie werden nicht in den Staging-Index übernommen, da wir den Befehl git add nicht verwendet haben. An diesem Punkt können wir git reset --hard ausführen und den neuen Zustand des Repos sehen:
git reset --hard
git reset --hard
#HEAD is now at ab23324 update content of edited_file
git status
#On branch master
#nothing to commit, working tree clean
git ls-files -s
#123126 6c423c1b04b5edd5acfc85de0b592449e5303773 0 edited_fileDie Option --hard führte einen „harten Reset“ aus. Git zeigt an, dass HEAD auf den jüngsten Commit ab23324 zeigt. Danach wird der Zustand des Repos mit git status überprüft. Git zeigt an, dass keine ausstehenden Änderungen vorhanden sind. Was den Zustand des Staging-Index betrifft, wurde er auf einen Punkt vor dem Hinzufügen von test_file zurückgesetzt. Die Änderungen an edited_file und das Hinzufügen von test_file wurden gelöscht. Dieser Verlust kann nicht rückgängig gemacht werden.
--mixed
Der Betriebsmodus ist standardmäßig --mixed. Er aktualisiert die Referenzzeiger. Der Staging-Index wird auf den angegebenen Commit zurückgesetzt. Rückgängig gemachte Änderungen aus dem Staging-Index werden im Arbeitsverzeichnis abgelegt.
the git reset command
echo 'new file content' > test_file
git add test_file
echo 'append content' >> edited_file
git add edited_file
git status
#On branch master
#Changes to be committed:
#(use "git reset HEAD ..." to unstage)
#new file: test_file
#modified: edited_file
git ls-files -s
#123126 6a32154a5477b1bf4765946147c49509a4323d32 0 test_file
#123126 3c3262db063f9e9426901092c00a3394b4bd3445 0 edited_fileIm obigen Beispiel wurde test_file hinzugefügt und der Inhalt von edited_file geändert. Anschließend werden diese Änderungen mit Hilfe von git add auf den Staging-Index angewendet. Bei diesem Zustand des Repositorys ist es nun an der Zeit, git reset aufzurufen.
git reset --mixed
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: edited_file
#Untracked files:
#(use "git add ..." to include in what will be committed)
#test_file
#no changes added to commit (use "git add" and/or "git commit -a")
git ls-files -s
#123126 6c423c1b04b5edd5acfc85de0b592449e5303773 0 edited_file--mixed ist der Standardmodus. Er hat denselben Effekt wie git reset. git status zeigt, dass es Änderungen an edited_file gibt und dass test_file eine nicht verfolgte Datei ist. Das ist genau das Verhalten von --mixed. Der Staging-Index wurde zurückgesetzt und die ausstehenden Änderungen wurden ins Arbeitsverzeichnis verschoben.
--soft
Das Argument --soft aktualisiert Referenzzeiger und stoppt den Reset. Es beeinflusst jedoch weder den Staging-Index noch das Arbeitsverzeichnis.
git reset --soft
git reset --soft
git status
#On branch master
#Changes to be committed:
#(use "git reset HEAD ..." to unstage)
#modified: edited_file
git ls-files -s
#123126 32a252710639e5da6b515416fd779d0741e4561a 0 edited_fileEin Soft-Reset setzt nur die Commit-Historie zurück. Standardmäßig wird er mit HEAD als Ziel-Commit aufgerufen. Erstellen wir nun einen neuen Commit, um ein --soft mit einem Ziel-Commit zu testen, das nicht HEAD ist:
git reset and commit
git commit -m "add changes to edited_file"Jetzt hat unser Repository drei Commits. Um den ersten zu finden, müssen wir seine ID ermitteln, was durch die Ausgabe von git log möglich ist.
git reset and git log
git log
#commit 62e793f6941c7e0d4ad9a1345a175fe8f45cb9df
#Author: w3docs
#Date: Fri Nov 1 14:02:07 2019 -0800
#add changes to edited_file
#commit ab23324a6da9f0dec51ed16d3d8823f28e1a72a
#Author: w3docs
#Date: Fri Nov 1 11:31:58 2019 -0800
#change content of edited_file
#commit 780411da3b47117270c0e3a8d5dcfd11d28d04a4
#Author: w3docs
#Date: Thu Sep 31 18:40:29 2019 -0800
#initial commitDies ist die ID des ersten Commits. Jetzt wird sie als Ziel für den Soft-Reset verwendet. Zuvor müssen wir den aktuellen Zustand des Repositorys prüfen:
git status && git ls-files -s
git status && git ls-files -s
#On branch master
#nothing to commit, working tree clean
#123126 32a252710639e5da6b515416fd779d0741e4561a 0 edited_fileJetzt können wir den ersten Commit per Soft-Reset zurücksetzen:
git reset --soft
git reset --soft 780411da3b47117270c0e3a8d5dcfd11d28d04a4
git status && git ls-files -s
#On branch master
#Changes to be committed:
#(use "git reset HEAD ..." to unstage)
#modified: edited_file
#123126 32a252710639e5da6b515416fd779d0741e4561a 0 edited_fileIm obigen Beispiel haben wir einen Soft-Reset durchgeführt und den kombinierten Befehl git status und git ls-files aufgerufen, der den Zustand des Repositorys ausgibt. Der Befehl git status zeigt, dass es einige Änderungen an edited_file gibt, und hebt sie als für den nächsten Commit vorgemerkte Änderungen hervor. Die Eingabe von git ls-files zeigt, dass der Staging-Index unverändert geblieben ist und den SHA 32a252710639e5da6b515416fd779d0741e4561a beibehält. Untersuchen wir den Zustand des Repositorys nach dem Soft-Reset zusätzlich mit Hilfe von git log:
git reset
git log
#commit 780411da3b47117270c0e3a8d5dcfd11d28d04a4
#Author: w3docs
#Date: Thu Sep 31 18:40:29 2019 -0800
#initial commitWie wir sehen können, zeigt die obige Ausgabe, dass sich nur ein einzelner Commit in der Commit-Historie befindet. Wie bei allen git reset-Aufrufen wird zunächst mit --soft der Commit-Baum zurückgesetzt.
Im Gegensatz zu --hard und --mixed, die beide gegen HEAD gearbeitet haben, hat ein Soft-Reset den Commit-Baum in die Vergangenheit zurückgesetzt.
Der Unterschied zwischen den Befehlen reset und revert
Git revert gilt als sicherere Methode zum Rückgängigmachen von Änderungen als git reset. Es besteht eine große Wahrscheinlichkeit, dass dabei Arbeit verloren gehen kann. git reset löscht keinen Commit, kann den Commit aber „verwaisen“ lassen. Das bedeutet, dass es keinen direkten Weg gibt, auf ihn zuzugreifen. Infolgedessen löscht Git alle verwaisten Commits, wenn der interne Garbage Collector ausgeführt wird. Standardmäßig führt Git den internen Garbage Collector alle 30 Tage aus. Verwaiste Commits werden normalerweise mithilfe des Befehls git reflog gefunden.
Ein weiterer Unterschied zwischen diesen beiden Befehlen besteht darin, dass git revert für das Rückgängigmachen öffentlicher Commits konfiguriert ist und git reset für das Rückgängigmachen lokaler Änderungen im Arbeitsverzeichnis und Staging-Index.
Die Unzulässigkeit des Zurücksetzens öffentlicher Historie
Verwenden Sie git reset <commit> nicht, wenn es nach <commit> Snapshots gibt, die in ein öffentliches Repository verschoben wurden. Wenn Sie einen Commit veröffentlichen, berücksichtigen Sie, dass auch andere Entwickler davon abhängen. Das Löschen von Commits, an denen auch andere Teammitglieder arbeiten, verursacht viele Probleme. Verwenden Sie git reset <commit> nur für lokale Änderungen. Um öffentliche Änderungen zu korrigieren, verwenden Sie den Befehl git revert.
Beispiele
Verwenden Sie Folgendes, um die angegebene Datei aus dem Staging-Bereich zu entfernen, ohne das Arbeitsverzeichnis zu ändern. Dadurch wird eine Datei aus dem Staging entfernt, ohne Änderungen zu überschreiben:
git reset file
git reset fileVerwenden Sie Folgendes, um den Staging-Bereich so zurückzusetzen, dass er dem letzten Commit entspricht, aber das Arbeitsverzeichnis unverändert bleibt. Dadurch werden alle Dateien aus dem Staging entfernt, ohne Änderungen zu überschreiben, und Sie erhalten die Möglichkeit, den vorgemerkten Snapshot von Grund auf neu aufzubauen:
git reset staging area
git resetVerwenden Sie Folgendes, um den Staging-Bereich und das Arbeitsverzeichnis so zurückzusetzen, dass sie dem letzten Commit entsprechen. Dadurch werden Änderungen aus dem Staging entfernt und auch alle Änderungen im Arbeitsverzeichnis überschrieben:
git reset --hard command
git reset --hardVerwenden Sie Folgendes, um die Spitze des Branches in der Zeit zurück auf commit zu verschieben, den Staging-Bereich anzupassen, aber das Arbeitsverzeichnis nicht anzutasten:
git reset commit
git reset commitVerwenden Sie Folgendes, um die Spitze des aktuellen Branches rückwärts auf commit zu verschieben und den Staging-Bereich sowie das Arbeitsverzeichnis entsprechend anzupassen:
git reset --hard commit
git reset --hard commitLokale Commits entfernen
Wie oben erwähnt, können Sie den Befehl git reset verwenden, um Commits im lokalen Repository zu löschen. Das folgende Beispiel demonstriert eine solche Verwendung von git reset. Der Befehl git reset --hard HEAD~2 verschiebt den aktuellen Branch um zwei weitere Commits zurück und entfernt die zwei zuletzt erstellten Snapshots aus der Projekt-Historie.
git add command
# Create a new file called `yourname.txt` and add some code to it
# Commit it to the project history
git add yourname.txt
git commit -m "Start to develop a project"
# Edit `yourname.txt` again and change some other tracked files, too
# Commit another snapshot
git commit -a -m "Continue developing"
# Scrap the project and remove the related commits
git reset --hard HEAD~2Dateien aus dem Staging entfernen
Der Befehl git reset wird normalerweise verwendet, um vorgemerkte Snapshots zu erstellen. Im folgenden Beispiel haben wir 2 Dateien namens task.txt und index.txt, die dem Repository hinzugefügt wurden. git reset ermöglicht es uns, Änderungen aus dem Staging zu entfernen, die nicht mit dem nächsten Commit verbunden sind.
git reset unstaging files
# Edit task.txt and index.txt
# Stage everything in the current directory
git add .
# Realize that the changes in task.txt and index.txt
# should be committed in different snapshots
# Unstage index.txt
git reset index.txt
# Commit only task.txt
git commit -m "Edit task.txt"
# Commit index.txt in a separate snapshot
git add index.txt
git commit -m "Edit index.txt"Practice
What are the features and options of the 'git reset' command in Git?