Interaktives Rebasing (Interactive Rebasing)
Interaktives Rebasing ist eine der mächtigsten und potenziell komplexesten Funktionen von Git. Es ermöglicht Ihnen, Ihre Commit-Historie auf sehr flexible Weise umzuschreiben. Sie können Commits neu anordnen, Commits kombinieren (squashen), Commit-Nachrichten bearbeiten oder sogar Commits vollständig entfernen. Es ist, als hätten Sie einen feinkörnigen Editor für Ihre Commit-Historie.
Warnung: Interaktives Rebasing, insbesondere auf gemeinsam genutzten Branches, kann riskant sein, da es die Historie umschreibt. Rebasen Sie niemals Commits, die bereits in ein gemeinsam genutztes Repository gepusht wurden, es sei denn, Sie verstehen die Auswirkungen vollständig und haben sich mit Ihrem Team abgestimmt. Rebasing ist im Allgemeinen sicherer und besser geeignet, um Ihre lokalen Commits aufzuräumen, bevor Sie sie mit anderen teilen.
Erstellen wir eine Reihe von Commits, mit denen wir arbeiten können, damit wir interaktives Rebasing in Aktion sehen können:
echo "First change" >> hello.txt
git commit -am "First change"
echo "Second change" >> hello.txt
git commit -am "Second change"
echo "Third change" >> hello.txt
git commit -am "Third change"
Diese Befehle erstellen in schneller Folge drei neue Commits, die jeweils eine Zeile zu unserer Datei "hello.txt" hinzufügen. Die Kurzform -am
in git commit -am
kombiniert das Stagen aller geänderten und gelöschten Dateien (-a
) mit dem Committen (-m
). Es ist eine praktische Abkürzung, wenn Sie bereits Dateien verfolgt haben und nur Änderungen an diesen committen möchten.
Nehmen wir nun an, wir möchten diese drei Commits aufräumen. Vielleicht waren "First change" und "Second change" eigentlich Teil derselben logischen Änderung, und wir möchten sie zu einem einzigen Commit zusammenfassen. Und vielleicht möchten wir die Commit-Nachricht für "Third change" verbessern. Interaktives Rebasing ist dafür perfekt.
Starten Sie eine interaktive Rebase-Sitzung mit:
git rebase -i HEAD~3
Lassen Sie uns diesen Befehl verstehen:
git rebase -i
: git rebase
ist der Befehl für das Rebasing. Das Flag -i
steht für "interactive" (interaktiv), was Git mitteilt, dass wir ein interaktives Rebase durchführen möchten.
HEAD~3
: Dies gibt den Bereich der Commits an, die wir rebasen möchten. HEAD
bezieht sich auf den aktuellen Commit. ~3
bedeutet "gehe drei Commits von HEAD zurück". HEAD~3
wählt also die letzten drei Commits aus. Sie könnten auch einen Commit-Hash anstelle von HEAD~3
angeben, um das Rebase von einem bestimmten Punkt in der Historie aus zu starten.
Wenn Sie diesen Befehl ausführen, öffnet Git Ihren Standardtexteditor mit einer Liste der letzten drei Commits. Die Editoroberfläche ist der Ort, an dem Sie Git anweisen, wie Ihre Historie geändert werden soll. Sie sehen etwas wie dieses (die Commit-Hashes werden unterschiedlich sein):
1 pick 63c95db First change
2 pick 68e7909 Second change
3 pick 5371424 Third change
4
5 ## Rebase 3bf348d..5371424 onto 3bf348d (3 commands)
6 #
7 ## Commands:
8 ## p, pick <commit> = use commit
9 ## r, reword <commit> = use commit, but edit the commit message
10 ## e, edit <commit> = use commit, but stop for amending
11 ## s, squash <commit> = use commit, but meld into previous commit
12 ## f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
13 ## commit's log message, unless -C is used, in which case
14 ## keep only this commit's message; -c is same as -C but
15 ## opens the editor
16 ## x, exec <command> = run command (the rest of the line) using shell
17 ## b, break = stop here (continue rebase later with 'git rebase --continue')
18 ## d, drop <commit> = remove commit
19 ## l, label <label> = label current HEAD with a name
20 ## t, reset <label> = reset HEAD to a label
21 ## m, merge [-C <commit> | -c <commit>] <label> [## <oneline>]
22 ## . create a merge commit using the original merge commit's
23 ## . message (or the oneline, if no original merge commit was
24 ## . specified); use -c <commit> to reword the commit message
25 #
26 ## These lines can be re-ordered; they are executed from top to bottom.
27 #
28 ## If you remove a line here THAT COMMIT WILL BE LOST.
29 #
30 ## However, if you remove everything, the rebase will be aborted.
Verständnis des interaktiven Rebase-Editors:
- Commit-Liste: Der obere Teil der Datei listet die Commits auf, die Teil der Rebase-Operation sind. Standardmäßig werden sie in chronologischer Reihenfolge aufgelistet (älteste oben, neueste unten).
- Befehle: Unterhalb der Commit-Liste stellt Git eine Liste verfügbarer Befehle bereit, mit denen Sie jeden Commit ändern können. Die gebräuchlichsten sind:
pick
(oder p
): Verwenden Sie den Commit so, wie er ist. Dies ist die Standardeinstellung.
reword
(oder r
): Verwenden Sie den Commit, erlauben Sie aber, die Commit-Nachricht zu ändern.
edit
(oder e
): Verwenden Sie den Commit, stoppen Sie aber den Rebase-Prozess bei diesem Commit, damit Sie weitere Änderungen vornehmen können (z. B. Dateien ändern, weitere Änderungen hinzufügen).
squash
(oder s
): Kombinieren Sie diesen Commit mit dem vorherigen Commit in der Liste. Die Commit-Nachricht dieses Commits wird an die Nachricht des vorherigen Commits angehängt (Sie können die kombinierte Nachricht später bearbeiten).
fixup
(oder f
): Ähnlich wie squash
, verwirft aber die Commit-Nachricht dieses Commits und verwendet nur die Nachricht des vorherigen Commits.
drop
(oder d
): Entfernen Sie diesen Commit vollständig aus der Historie.
Unser interaktiver Rebase-Plan:
Wir wollen:
- "Second change" in "First change" squashen (zusammenführen).
- "Third change" umformulieren (reword), um eine bessere Nachricht zu erhalten.
Um dies zu erreichen, ändern Sie die Editor-Datei so, dass sie wie folgt aussieht:
pick abc1234 First change
squash def5678 Second change
reword ghi9101 Third change
Ändern Sie die Commit-Hashes in der Editor-Datei nicht.
Anweisungen zum Bearbeiten in Vim (falls dies Ihr Editor ist):
- Insert-Modus aktivieren: Drücken Sie die Taste
i
. Dadurch können Sie die Datei eingeben und bearbeiten.
- Änderungen vornehmen: Verwenden Sie Ihre Pfeiltasten, um zu den Zeilen zu navigieren und "pick" in "squash" und "reword" zu ändern, wie oben gezeigt.
- Insert-Modus verlassen: Drücken Sie die Taste
Esc
.
- Speichern und Beenden: Geben Sie
:wq
ein und drücken Sie Enter
.
Nachdem Sie die Editor-Datei gespeichert und geschlossen haben, beginnt Git mit der Durchführung der Rebase-Operationen basierend auf Ihren Anweisungen.
Zuerst Commits squashen:
Git wird zuerst den Befehl "squash" verarbeiten. Es wird die Änderungen von "Second change" in "First change" kombinieren. Dann wird Ihr Texteditor erneut geöffnet, diesmal, damit Sie die kombinierte Commit-Nachricht für den gequetschten (squashed) Commit bearbeiten können. Sie sehen etwas wie dieses im Editor:
## This is a combination of 2 commits.
## This is the 1st commit message:
First change
## This is the 2nd commit message:
Second change
## Please enter the commit message for your changes. Lines starting
## with '#' will be ignored, and an empty message aborts the commit.
#
## Date: Tue Oct 24 10:30:00 2023 +0000
#
## interactive rebase in progress; onto <base_commit_hash>
#
## Last commands done (2 commands done):
## pick abc1234 First change
## squash def5678 Second change
## Next command to do (1 remaining command):
## reword ghi9101 Third change
## You are currently rebasing branch 'master' on '<base_commit_hash>'.
Dieser Editor zeigt Ihnen die ursprünglichen Commit-Nachrichten der Commits, die gequetscht (squashed) werden. Sie können diesen Text nun bearbeiten, um eine einzige, kohärente Commit-Nachricht für den kombinierten Commit zu erstellen. Sie könnten ihn beispielsweise ändern in:
Combined first and second changes: Initial setup of hello.txt
Nachdem Sie die Nachricht bearbeitet haben, speichern und schließen Sie den Editor.
Als Nächstes eine Commit-Nachricht umformulieren (Rewording):
Git wird dann zum Befehl "reword" für "Third change" übergehen. Es wird den Editor noch einmal öffnen, diesmal, damit Sie die Commit-Nachricht für "Third change" bearbeiten können. Sie sehen die ursprüngliche Nachricht:
Third change
## Please enter the commit message for your changes. Lines starting
## with '#' will be ignored, and an empty message aborts the commit.
#
## Date: Tue Oct 24 10:35:00 2023 +0000
#
## interactive rebase in progress; onto <base_commit_hash>
#
## Last commands done (3 commands done):
## pick abc1234 First change
## squash def5678 Second change
## reword ghi9101 Third change
## Next command to do (0 remaining commands):
## (finish)
## You are currently rebasing branch 'master' on '<base_commit_hash>'.
Ändern Sie die Nachricht in etwas Beschreibenderes, wie:
Improved third change: Added a more descriptive line to hello.txt
Speichern und schließen Sie den Editor.
Rebase-Abschluss:
Nachdem alle Befehle in Ihren interaktiven Rebase-Anweisungen verarbeitet wurden, beendet Git den Rebase-Prozess. Sie sehen normalerweise eine Meldung wie "Successfully rebased and updated refs/heads/master." (Erfolgreich rebased und refs/heads/master aktualisiert).
Überprüfen der rebased Historie:
Überprüfen Sie nun erneut Ihr Commit-Log:
git log --oneline
Sie sollten eine Historie sehen, die in etwa so aussieht (Commit-Hashes werden unterschiedlich sein):
<commit_hash_third_revised> Improved third change: Added a more descriptive line to hello.txt
<commit_hash_combined> Combined first and second changes: Initial setup of hello.txt
<commit_hash_cherry_pick> Add new feature
<commit_hash_revert> Revert "Add line to be reverted"
<commit_hash_original_reverted> Add line to be reverted
<commit_hash_initial_amended> Initial commit with important note
Beachten Sie, dass:
- Sie jetzt weniger Commits haben als zuvor (wir haben zwei Commits zu einem zusammengefasst).
- Die Commit-Nachricht für das, was "First change" und "Second change" war, jetzt kombiniert und aussagekräftiger ist.
- Die Commit-Nachricht für das, was "Third change" war, umformuliert wurde.
- Die Commit-Hashes der rebased Commits haben sich geändert, weil wir die Historie umgeschrieben haben.
Wichtige Erinnerungen zum interaktiven Rebasing:
- Lokales Historien-Tool: Interaktives Rebase ist in erster Linie ein Werkzeug zum Aufräumen Ihrer lokalen Commit-Historie.
- Vermeiden Sie das Rebasing gemeinsam genutzter Branches: Rebasen Sie keine Branches, die in ein gemeinsam genutztes Repository gepusht wurden, es sei denn, Sie sind sich absolut sicher, was Sie tun, und haben sich mit Ihrem Team abgestimmt. Das Umschreiben der gemeinsam genutzten Historie kann ernsthafte Probleme für Mitarbeiter verursachen.
git rebase --abort
: Wenn Sie während eines interaktiven Rebase einen Fehler machen oder wenn Sie verwirrt sind oder auf Konflikte stoßen, die Sie nicht lösen können, können Sie jederzeit git rebase --abort
verwenden, um die Rebase-Operation abzubrechen und Ihren Branch in den Zustand zurückzuversetzen, in dem er sich vor dem Start des Rebase befand. Dies ist eine sichere Möglichkeit, ein Rebase rückgängig zu machen, wenn etwas schief geht.