Verständnis und Anwendung von Git-Commits: Reset, Revert und Rebase
In diesem Artikel werden wir uns eingehend mit den verschiedenen Möglichkeiten beschäftigen, wie Sie Ihre Commits in Git manipulieren können. Als Entwickler stoßen Sie häufig auf Situationen, in denen Sie zu einem früheren Commit zurückkehren müssen. Dabei ist es entscheidend, die Unterschiede zwischen den Git-Befehlen reset
, revert
und rebase
zu verstehen. Lassen Sie uns diese Befehle und ihre Funktionen genauer betrachten.
Git Reset
Der Befehl git reset
ist ein mächtiges Werkzeug, um Änderungen rückgängig zu machen und zu früheren Zuständen zurückzukehren. Er funktioniert wie eine Art Rollback-Funktion, mit der Sie zwischen verschiedenen Commits hin- und herspringen können. Es gibt drei Hauptmodi für die Ausführung von git reset
: --soft
, --mixed
und --hard
. Standardmäßig wird der --mixed
-Modus verwendet. Bei der Verwendung von git reset
spielen drei interne Git-Komponenten eine Rolle: der HEAD
, der Staging-Bereich (Index) und das Arbeitsverzeichnis.
Das Arbeitsverzeichnis ist der Ort, an dem Sie an Ihren Dateien arbeiten und sie sich befinden. Sie können den Status mit git status
einsehen. Der Staging-Bereich (Index) ist der Bereich, in dem Git alle Änderungen verfolgt. Diese Änderungen werden im .git
-Verzeichnis gespeichert. Dateien werden mit git add "Dateiname"
zum Staging-Bereich hinzugefügt. Der aktuelle Zweig in Git wird als HEAD
bezeichnet, welcher immer auf den letzten Commit zeigt. Wenn Sie zu einem anderen Zweig wechseln, bewegt sich der HEAD
ebenfalls mit.
Wir betrachten nun die Funktionsweise von git reset
im --hard
-, --soft
– und --mixed
-Modus genauer. Der --hard
-Modus versetzt Ihren HEAD
auf den angegebenen Commit, wobei das Arbeitsverzeichnis mit den Dateien dieses Commits überschrieben und der Staging-Bereich zurückgesetzt wird. Der --soft
-Reset verändert nur den HEAD
-Zeiger, während Dateien im Arbeitsverzeichnis und Staging-Bereich erhalten bleiben. Im --mixed
-Modus (Standard) werden sowohl der HEAD
-Zeiger als auch der Staging-Bereich zurückgesetzt.
Git Hard Reset
Der git reset --hard
-Befehl wird verwendet, um den HEAD
-Zeiger auf einen bestimmten Commit zu verschieben. Dadurch werden alle Commits, die nach dem angegebenen Commit erfolgten, entfernt und der Commit-Verlauf wird geändert.
Betrachten wir das folgende Beispiel. Zuerst werden drei neue Dateien hinzugefügt und committet.
$ git status On branch master Your branch is ahead of 'origin/master' by 2 commits. (use "git push" to publish your local commits) nothing to commit, working tree clean
Nun erstellen wir 3 Dateien mit Inhalten:
$ vi file1.txt $ vi file2.txt $ vi file3.txt
Die neuen Dateien werden dem Repository hinzugefügt:
$ git add file*
Ein erneuter git status
-Befehl zeigt die neuen Dateien im Staging-Bereich:
$ git status On branch master Your branch is ahead of 'origin/master' by 2 commits. (use "git push" to publish your local commits) Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: file1.txt new file: file2.txt new file: file3.txt
Bevor wir den Commit ausführen, sehen wir uns das Commit-Log an:
$ git log --oneline 0db602e (HEAD -> master) one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Die Dateien werden nun committet:
$ git commit -m 'added 3 files' [master d69950b] added 3 files 3 files changed, 3 insertions(+) create mode 100644 file1.txt create mode 100644 file2.txt create mode 100644 file3.txt
Die neuen Dateien sind nun vorhanden:
$ git ls-files demo dummyfile newfile file1.txt file2.txt file3.txt
Nach dem Commit sind 4 Einträge im Log vorhanden, mit dem HEAD
auf dem letzten:
$ git log --oneline d69950b (HEAD -> master) added 3 files 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Wenn wir file1.txt
löschen und den Status abfragen, wird dies angezeigt:
$ git status On branch master Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits) Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) deleted: file1.txt no changes added to commit (use "git add" and/or "git commit -a")
Nun führen wir den Hard-Reset durch:
$ git reset --hard HEAD is now at d69950b added 3 files
Wenn wir den Status überprüfen, sehen wir, dass es nichts zu committen gibt und die gelöschte Datei wiederhergestellt wurde, da wir keinen Commit nach dem Löschen durchgeführt haben:
$ git status On branch master Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits) nothing to commit, working tree clean
Das Log zeigt an, dass der HEAD
auf den Commit „added 3 files“ zeigt:
$ git log commit d69950b7ea406a97499e07f9b28082db9db0b387 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 19:53:31 2020 +0530 added 3 files commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 test
Wir setzen nun den HEAD
um einen Commit zurück, indem wir HEAD^
verwenden:
$ git reset --hard HEAD^ HEAD is now at 0db602e one more commit
Wir sehen, dass der HEAD nun auf einen früheren Commit zeigt:
$ git log --oneline 0db602e (HEAD -> master) one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Das Log zeigt, dass der Commit d69950b
nicht mehr vorhanden ist:
$ git log commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 Test
Die drei neuen Dateien sind nicht mehr im Repository vorhanden, da sie nach dem Hard-Reset entfernt wurden:
$ git ls-files demo dummyfile newfile
Git Soft Reset
Betrachten wir nun ein Beispiel für git reset --soft
. Die 3 Dateien wurden erneut hinzugefügt und committet. Das Git-Log zeigt den neuesten Commit „soft reset“:
$ git log --oneline aa40085 (HEAD -> master) soft reset 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Details zum Commit können mit dem Befehl git log
angezeigt werden:
$ git log commit aa400858aab3927e79116941c715749780a59fc9 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 21:01:36 2020 +0530 soft reset commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 test
Wir möchten nun zum älteren Commit mit SHA 0db602e085a4d59cfa9393abac41ff5fd7afcb14
zurückkehren. Dafür verwenden wir git reset --soft
:
$ git reset --soft 0db602e085a4
Das Git-Log zeigt, dass der HEAD
auf den angegebenen Commit zurückgesetzt wurde:
$ git log commit 0db602e085a4d59cfa9393abac41ff5fd7afcb14 (HEAD -> master) Author: mrgeek <[email protected]> Date: Mon May 17 01:04:13 2020 +0530 one more commit commit 59c86c96a82589bad5ecba7668ad38aa684ab323 Author: mrgeek <[email protected]> Date: Mon May 17 00:54:53 2020 +0530 new commit commit e2f44fca2f8afad8e4d73df6b72111f2f2fd71ad (origin/master, origin/HEAD) Author: mrgeek <[email protected]> Date: Mon May 17 00:16:33 2020 +0530 test
Im Gegensatz zum Hard-Reset bleiben die Dateien des Commit aa400858aab3927e79116941c715749780a59fc9
, bei dem 3 Dateien hinzugefügt wurden, im Arbeitsverzeichnis erhalten. Daher ist ein Soft-Reset in Situationen, in denen keine Daten verloren gehen sollen, vorzuziehen:
$ git ls-files demo dummyfile file1.txt file2.txt file3.txt newfile
Git Revert
Der git revert
-Befehl dient dazu, Änderungen rückgängig zu machen, ähnlich wie reset
. Der Hauptunterschied besteht darin, dass revert
einen neuen Commit erstellt, um zu einem bestimmten Zustand zurückzukehren. git revert
löscht keine Daten während des Wiederherstellungsprozesses und fügt einen neuen Commit im Verlauf hinzu.
Nehmen wir an, wir fügen 3 Dateien hinzu und committen diese:
$ git commit -m 'add 3 files again' [master 812335d] add 3 files again 3 files changed, 3 insertions(+) create mode 100644 file1.txt create mode 100644 file2.txt create mode 100644 file3.txt
Das Log zeigt den neuen Commit an:
$ git log --oneline 812335d (HEAD -> master) add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Um zu einem früheren Commit, z.B. 59c86c9 new commit
, zurückzukehren, führen wir folgenden Befehl aus:
$ git revert 59c86c9
Dadurch wird ein Editor geöffnet. Dort können Sie Ihre Commit-Nachricht hinzufügen. Nach dem Speichern und Schließen wird der Revert ausgeführt.
Revert "new commit" This reverts commit 59c86c96a82589bad5ecba7668ad38aa684ab323. # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # # On branch master # Your branch is ahead of 'origin/master' by 4 commits. # (use "git push" to publish your local commits) # # Changes to be committed: # modified: dummyfile
Nachdem wir gespeichert und geschlossen haben, erscheint diese Ausgabe:
$ git revert 59c86c9 [master af72b7a] Revert "new commit" 1 file changed, 1 insertion(+), 1 deletion(-)
Das Log zeigt, dass revert
, im Gegensatz zu reset
, einen neuen Commit hinzugefügt hat:
$ git log --oneline af72b7a (HEAD -> master) Revert "new commit" 812335d add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Das Git-Log enthält den gesamten Commit-Verlauf. Wenn Sie Commits aus dem Verlauf entfernen möchten, ist revert
keine gute Wahl, aber wenn Sie die Commit-Änderungen im Verlauf beibehalten möchten, ist revert
anstelle von reset
der geeignete Befehl.
Git Rebase
git rebase
ermöglicht es, Commits eines Zweigs auf einen anderen zu verschieben oder zu kombinieren. In der Praxis werden Features üblicherweise nicht direkt auf dem master
-Branch entwickelt, sondern in einem separaten Feature-Branch. Wenn die Entwicklung abgeschlossen ist, werden diese Commits in den master
-Branch integriert.
rebase
kann manchmal verwirrend sein, da er der Zusammenführung sehr ähnlich ist. Sowohl Merge als auch Rebase haben das Ziel, Commits aus einem Feature-Branch in einen Master-Branch zu übernehmen. Angenommen, wir haben eine Situation wie in der folgenden Darstellung:
In einem Team mit mehreren Entwicklern, die an verschiedenen Feature-Branches arbeiten, kann dies schnell komplex werden. Anstatt einen Git-Merge durchzuführen, verwenden wir rebase
, um die Commits des Feature-Branches auf den Master-Branch zu verschieben. rebase
dupliziert die Commits des Feature-Branches auf dem Master-Branch.
Dadurch erhalten Sie eine klare, lineare Commit-Historie, die einfacher zu verfolgen ist. Auch bei einer großen Anzahl von Entwicklern bleibt der Commit-Verlauf übersichtlich.
Um dies praktisch zu zeigen, hier ein Beispiel. Der aktuelle master
-Branch hat 4 Commits:
$ git log --oneline 812335d (HEAD -> master) add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Wir erstellen einen neuen Zweig namens feature
, der auf dem 2. Commit (59c86c9
) basiert:
(master) $ git checkout -b feature 59c86c9 Switched to a new branch 'feature'
Das Log des feature
-Branches zeigt 2 Commits vom master
:
(feature) $ git log --oneline 59c86c9 (HEAD -> feature) new commit e2f44fc (origin/master, origin/HEAD) test
Wir erstellen feature1.txt
und fügen es zum feature
-Branch hinzu:
(feature) $ vi feature1.txt (feature) $ git add . The file will have its original line endings in your working directory (feature) $ git commit -m 'feature 1' [feature c639e1b] feature 1 1 file changed, 1 insertion(+) create mode 100644 feature1.txt
Wir erstellen ein weiteres Feature feature2.txt
und committen dies:
(feature) $ vi feature2.txt (feature) $ git add . The file will have its original line endings in your working directory (feature) $ git commit -m 'feature 2' [feature 0f4db49] feature 2 1 file changed, 1 insertion(+) create mode 100644 feature2.txt
Das Log des feature
-Branches zeigt die zwei neuen Commits:
(feature) $ git log --oneline 0f4db49 (HEAD -> feature) feature 2 c639e1b feature 1 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Um die zwei neuen Features zum master
-Branch hinzuzufügen, verwenden wir rebase
. Wir rebasen den feature
-Branch gegen den master
-Branch:
(feature) $ git rebase master Successfully rebased and updated refs/heads/feature.
Wir wechseln zum master
-Branch:
(feature) $ git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 3 commits. (use "git push" to publish your local commits)
Nun rebasen wir den master
-Branch gegen den feature
-Branch. Dadurch werden die Commits des feature
-Branches zum master
-Branch hinzugefügt:
(master) $ git rebase feature Successfully rebased and updated refs/heads/master.
Das Log des master
-Branches zeigt, dass die Commits des feature
-Branches erfolgreich hinzugefügt wurden:
(master) $ git log --oneline 766c996 (HEAD -> master, feature) feature 2 c036a11 feature 1 812335d add 3 files again 0db602e one more commit 59c86c9 new commit e2f44fc (origin/master, origin/HEAD) test
Dies war eine umfassende Einführung in die Verwendung von reset
, revert
und rebase
in Git.
Fazit
Dieser Artikel hat Ihnen hoffentlich einen detaillierten Überblick über die Git-Befehle reset
, revert
und rebase
gegeben. Mit diesem Wissen können Sie nun Ihre Commits nach Bedarf manipulieren. Experimentieren Sie mit diesen Befehlen, um ein besseres Verständnis für deren Funktionsweise zu erlangen.