Fortgeschrittene Git Commit-Operationen

GitGitBeginner
Jetzt üben

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

Willkommen zurück, Git-Abenteurer! Sie haben Ihre ersten Schritte in die Welt der Versionskontrolle unternommen, und jetzt ist es an der Zeit, Ihre Fähigkeiten zu verbessern. In diesem Lab werden wir einige der fortgeschritteneren Commit-Operationen von Git untersuchen. Diese Techniken geben Ihnen noch mehr Kontrolle über die Historie Ihres Projekts, sodass Sie Fehler bereinigen, Ihre Arbeit neu organisieren und effektiver zusammenarbeiten können.

Betrachten Sie dieses Lab als ein Upgrade Ihrer Zeitmaschine. Sie können nicht nur durch die Zeit reisen, sondern lernen jetzt auch, wie Sie die Zeitlinie selbst verändern können! Keine Sorge, wenn das abschreckend klingt - wir führen Sie durch jeden Schritt und erklären nicht nur, wie diese Operationen durchgeführt werden, sondern auch, warum sie in realen Szenarien nützlich sind.

Am Ende dieses Labs werden Sie in der Lage sein, Commits zu ändern (amend commits), Änderungen rückgängig zu machen (revert changes), bestimmte Commits auszuwählen (cherry-pick specific commits), interaktives Rebase durchzuführen (perform interactive rebasing) und Commits zusammenzufassen (squash commits). Dies sind leistungsstarke Werkzeuge, die professionelle Entwickler jeden Tag verwenden, um saubere, organisierte Projekthistorien zu pflegen. Lassen Sie uns eintauchen und Ihre Git-Kenntnisse auf die nächste Stufe heben!

Dies ist ein Guided Lab, das schrittweise Anweisungen bietet, um Ihnen beim Lernen und Üben zu helfen. Befolgen Sie die Anweisungen sorgfältig, um jeden Schritt abzuschließen und praktische Erfahrungen zu sammeln. Historische Daten zeigen, dass dies ein Labor der Stufe Anfänger mit einer Abschlussquote von 83% ist. Es hat eine positive Bewertungsrate von 95% von den Lernenden erhalten.

Einrichten Ihres Arbeitsbereichs

Bevor wir mit unseren fortgeschrittenen Operationen beginnen, richten wir einen neuen Arbeitsbereich ein. Wir erstellen ein neues Verzeichnis und initialisieren darin ein Git-Repository. Dies gibt uns einen sauberen Raum zum Experimentieren, ohne Ihre bestehenden Projekte zu beeinträchtigen.

Öffnen Sie Ihr Terminal und geben Sie diese Befehle ein, wobei Sie nach jeder Zeile die Eingabetaste (Enter) drücken:

cd ~/project
mkdir git-advanced-lab
cd git-advanced-lab
git init

Lassen Sie uns Schritt für Schritt aufschlüsseln, was diese Befehle bewirken:

  1. cd ~/project: cd steht für "change directory" (Verzeichnis wechseln). ~/project ist ein Pfad, der normalerweise auf einen "project"-Ordner in Ihrem Home-Verzeichnis (~) verweist. Dieser Befehl navigiert Ihr Terminal in dieses Verzeichnis. Wenn das Verzeichnis "project" in Ihrem Home-Verzeichnis nicht existiert, kann ein Fehler auftreten. Wenn dies der Fall ist, erstellen Sie es zuerst mit mkdir ~/project und versuchen Sie dann erneut cd ~/project.
  2. mkdir git-advanced-lab: mkdir steht für "make directory" (Verzeichnis erstellen). Dieser Befehl erstellt einen neuen Ordner namens "git-advanced-lab" in Ihrem aktuellen Verzeichnis (das nach dem vorherigen Befehl ~/project sein sollte). Dieses neue Verzeichnis wird das Stammverzeichnis unseres Git-Repositorys für dieses Lab sein.
  3. cd git-advanced-lab: Dieser Befehl ändert erneut Ihr aktuelles Verzeichnis und verschiebt Sie diesmal in das neu erstellte Verzeichnis "git-advanced-lab". Jetzt werden alle Befehle, die Sie ausführen, in diesem Verzeichnis ausgeführt.
  4. git init: Dies ist der entscheidende Git-Befehl zum Initialisieren eines Repositorys. git init richtet alle notwendigen Git-Strukturen im aktuellen Verzeichnis (git-advanced-lab) ein und verwandelt es in ein Git-Repository. Sie sehen einen versteckten Ordner namens .git, der in git-advanced-lab erstellt wurde. Dieser .git-Ordner ist das Herzstück Ihres Git-Repositorys und speichert die gesamte Versionshistorie und Konfiguration.

Nachdem wir nun ein Git-Repository initialisiert haben, erstellen wir eine einfache Datei, mit der wir arbeiten können, und erstellen unseren ersten Commit (initial commit):

echo "Hello, Advanced Git" > hello.txt
git add hello.txt
git commit -m "Initial commit"

Folgendes geschieht in diesen Befehlen:

  1. echo "Hello, Advanced Git" > hello.txt: echo ist ein Befehl, der Text anzeigt. "Hello, Advanced Git" ist der Text, den wir anzeigen möchten. > ist ein Umleitungsoperator (redirection operator). Er nimmt die Ausgabe des echo-Befehls und leitet sie in eine Datei namens "hello.txt" um. Wenn "hello.txt" nicht existiert, wird sie erstellt. Wenn sie existiert, wird sie mit dem neuen Inhalt überschrieben. Dieser Befehl erstellt also eine neue Datei namens "hello.txt" mit dem Inhalt "Hello, Advanced Git".
  2. git add hello.txt: Bevor Git Änderungen in einer Datei verfolgen kann, müssen Sie Git explizit mitteilen, dass es mit der Verfolgung beginnen soll. git add hello.txt staged die Datei "hello.txt". Staging bedeutet, die Datei vorzubereiten, um in den nächsten Commit aufgenommen zu werden. Stellen Sie sich den Staging-Bereich als Vorbereitungszone für Ihren Commit vor.
  3. git commit -m "Initial commit": git commit nimmt alle Änderungen, die sich derzeit im Staging-Bereich befinden (in unserem Fall das Hinzufügen von "hello.txt"), und speichert sie als neuen Commit in der Historie des Repositorys. -m "Initial commit" fügt eine Commit-Nachricht hinzu (commit message). "Initial commit" ist die Nachricht selbst, die eine kurze Beschreibung der Änderungen sein sollte, die Sie committen. Es ist eine gute Praxis, klare und prägnante Commit-Nachrichten zu schreiben.

Großartig! Wir haben jetzt ein Repository mit einem Commit. Lassen Sie uns unseren Status überprüfen, um zu bestätigen, dass alles korrekt eingerichtet ist:

git status

Nach dem Ausführen von git status sollten Sie eine Meldung in Ihrem Terminal sehen, die in etwa so aussieht:

On branch master
nothing to commit, working tree clean

Diese Meldung zeigt an, dass Ihr Arbeitsverzeichnis (working tree) sauber ist. "Working tree" bezieht sich auf das Verzeichnis, in dem sich Ihre Projektdateien befinden. "Clean" bedeutet, dass es keine Änderungen in Ihrem Arbeitsverzeichnis gibt, die noch nicht committet wurden. Dies bestätigt, dass wir bereit sind, unsere fortgeschrittenen Operationen mit einem sauberen Blatt zu beginnen!

Ändern Ihres letzten Commits (Amending Your Last Commit)

Stellen Sie sich vor, Sie haben gerade einen Commit erstellt, stellen aber dann fest, dass Sie vergessen haben, eine Änderung in einer Datei aufzunehmen, oder Sie haben einen kleinen Tippfehler in Ihrer Commit-Nachricht gemacht. Anstatt einen ganz neuen Commit für eine so kleine Korrektur zu erstellen, ermöglicht Git es Ihnen, den letzten Commit mit der Option --amend zu korrigieren. Das ist, als würde man leicht in der Zeit zurückgehen, um Ihre letzte Aktion anzupassen.

Lass uns das ausprobieren. Ändern wir zunächst unsere Datei hello.txt, indem wir eine weitere Zeile hinzufügen:

echo "This is an important file." >> hello.txt

Dieser Befehl hängt eine neue Zeile an unsere Datei "hello.txt" an. Lassen Sie uns den Operator >> verstehen:

  • > würde die gesamte Datei mit dem neuen Inhalt überschreiben.
  • >> hängt den neuen Inhalt an das Ende der bestehenden Datei an und behält den ursprünglichen Inhalt bei.

Nach dem Ausführen dieses Befehls enthält "hello.txt" also zwei Zeilen:

Hello, Advanced Git
This is an important file.

Nehmen wir nun an, wir haben erkannt, dass wir diese "important file"-Notiz in unseren ersten Commit selbst hätten aufnehmen sollen. Wir können unseren vorherigen Commit ändern (amend), um diese Änderung einzubeziehen und die Commit-Nachricht zu aktualisieren, um die Ergänzung widerzuspiegeln.

git add hello.txt
git commit --amend -m "Initial commit with important note"

Hier ist eine Aufschlüsselung dieser Befehle:

  1. git add hello.txt: Wir haben hello.txt geändert, daher müssen wir die Änderungen mit git add erneut stagen. Dies teilt Git mit, dass wir den aktuellen Zustand von "hello.txt" in unseren Commit aufnehmen möchten.
  2. git commit --amend -m "Initial commit with important note": Hier geschieht die Magie.
    • git commit --amend: Das Flag --amend teilt Git mit, dass wir anstatt einen neuen Commit zu erstellen, den letzten Commit ändern möchten.
    • -m "Initial commit with important note": Dies liefert eine neue Commit-Nachricht. Wenn Sie die Option -m weglassen, öffnet Git Ihren Standardtexteditor, sodass Sie die ursprüngliche Commit-Nachricht bearbeiten können.

Was ist gerade passiert?

Anstatt einen neuen Commit in Ihrer Git-Historie zu erstellen, hat Git den vorherigen "Initial commit" effektiv durch eine neue, verbesserte Version ersetzt. Der alte Commit ist verschwunden, und der neue Commit enthält die Änderungen, die wir gestaged haben, und die aktualisierte Commit-Nachricht.

Lassen Sie uns dies überprüfen, indem wir unser Commit-Log überprüfen:

git log --oneline

Sie sollten nur einen Commit in der Ausgabe sehen, aber die Commit-Nachricht sollte jetzt "Initial commit with important note" lauten.

<commit_hash> Initial commit with important note

Drücken Sie q, um die Log-Ansicht zu verlassen, falls sie sich nicht automatisch schließt.

Wichtige Überlegungen beim Ändern (Amending):

  • Nur nicht-gepushte Commits ändern (Only Amend Unpushed Commits): Sie sollten nur Commits ändern, die Sie noch nicht in ein gemeinsames Remote-Repository (wie GitHub, GitLab usw.) gepusht haben. Das Ändern eines Commits, der bereits gepusht wurde, überschreibt die Historie, auf die sich andere möglicherweise verlassen, was zu erheblichen Problemen und Verwirrung in kollaborativen Umgebungen führen kann.
  • Lokale Historienbereinigung (Local History Cleanup): Das Ändern (Amending) ist in erster Linie nützlich, um Ihre lokale Commit-Historie zu bereinigen, bevor Sie Ihre Arbeit teilen. Es ist eine großartige Möglichkeit, Ihre Commits zu verfeinern, während Sie beispielsweise noch an einem Feature-Branch arbeiten.

Einen Commit rückgängig machen (Reverting a Commit)

Manchmal erstellen Sie einen Commit und stellen später fest, dass er einen Fehler eingeführt hat, oder Sie möchten diese Änderungen einfach rückgängig machen. Sie könnten daran denken, git reset zu verwenden, um in der Zeit zurückzugehen und den Commit zu entfernen. git reset kann jedoch destruktiv sein, insbesondere wenn Sie Ihre Änderungen bereits gepusht haben oder wenn andere am selben Branch arbeiten. Eine sicherere und kollaborativere Möglichkeit, Änderungen rückgängig zu machen, ist die Verwendung von git revert.

git revert erstellt einen neuen Commit, der die Änderungen rückgängig macht, die durch einen bestimmten Commit eingeführt wurden. Er löscht den ursprünglichen Commit nicht aus der Historie; stattdessen fügt er einen neuen Commit hinzu, der die Auswirkungen des unerwünschten Commits effektiv umkehrt. Dies bewahrt die Historie und ist viel sicherer für gemeinsam genutzte Repositories.

Erstellen wir einen neuen Commit, den wir dann rückgängig machen werden:

echo "This line will be reverted" >> hello.txt
git add hello.txt
git commit -m "Add line to be reverted"

Diese Befehle ähneln dem, was wir zuvor getan haben:

  1. echo "This line will be reverted" >> hello.txt: Fügt unserer Datei "hello.txt" eine neue Zeile hinzu.
  2. git add hello.txt: Staged die geänderte Datei.
  3. git commit -m "Add line to be reverted": Erstellt einen neuen Commit mit der Nachricht "Add line to be reverted", der das Hinzufügen dieser neuen Zeile zu "hello.txt" beinhaltet.

Nehmen wir nun an, wir entscheiden, dass das Hinzufügen von "This line will be reverted" ein Fehler war und wir es entfernen möchten. Wir können den letzten Commit mit folgendem Befehl rückgängig machen:

git revert HEAD

Lassen Sie uns diesen Befehl aufschlüsseln:

  • git revert: Dies ist der Befehl zum Rückgängigmachen von Änderungen.
  • HEAD: In Git ist HEAD ein Zeiger auf den aktuellen Commit Ihres aktuellen Branchs. In den meisten Fällen bezieht sich HEAD auf den letzten Commit. git revert HEAD bedeutet also "den letzten Commit rückgängig machen". Sie können auch einen bestimmten Commit-Hash angeben, um einen älteren Commit rückgängig zu machen.

Wenn Sie git revert HEAD ausführen, führt Git Folgendes aus:

  1. Änderungen analysieren (Analyze the changes): Es betrachtet die Änderungen, die durch den HEAD-Commit eingeführt wurden (in unserem Fall "Add line to be reverted").
  2. Umgekehrte Änderungen erstellen (Create reverse changes): Es findet heraus, wie diese Änderungen rückgängig gemacht werden können (in unserem Fall das Entfernen der Zeile "This line will be reverted" aus "hello.txt").
  3. Neuen Commit erstellen (Create a new commit): Es erstellt automatisch einen neuen Commit, der diese umgekehrten Änderungen anwendet.
  4. Texteditor öffnen (optional) (Open Text Editor (Optional)): Git öffnet normalerweise Ihren Standardtexteditor (wie Vim, Nano, VS Code's Editor usw.) mit einer vorgefertigten Commit-Nachricht für den Revert-Commit. Die Standardnachricht lautet normalerweise so etwas wie "Revert 'Add line to be reverted'". Sie können die Standardnachricht akzeptieren oder sie ändern, um mehr Kontext bereitzustellen.

Umgang mit dem Texteditor während git revert:

Wenn sich Ihr Standardeditor öffnet und Sie mit ihm nicht vertraut sind (insbesondere wenn es Vim ist), machen Sie sich keine Sorgen! Der Vorgang ist normalerweise einfach:

  • So akzeptieren Sie die Standardnachricht und schließen den Revert ab:
    • Wenn es Vim ist: Drücken Sie die Esc-Taste (um in den Befehlsmodus zu gelangen), geben Sie dann :wq (write and quit) ein und drücken Sie die Enter-Taste.
    • Wenn es Nano ist: Drücken Sie Ctrl + X (zum Beenden), dann Y (zum Speichern der Änderungen) und Enter (zum Bestätigen des Dateinamens).
    • Für andere Editoren: Suchen Sie nach Optionen zum "Speichern" und "Schließen" oder "Beenden".

Nachdem Sie den Editor gespeichert und geschlossen haben (falls er sich geöffnet hat), schließt Git den Revert-Commit ab.

Lassen Sie uns nun unsere Commit-Historie überprüfen:

git log --oneline

Sie sollten drei Commits in Ihrem Log sehen, in umgekehrt chronologischer Reihenfolge (neuester zuerst):

  1. Ein Commit, der mit "Revert" beginnt (der Revert-Commit, den wir gerade erstellt haben).
  2. "Add line to be reverted" (der Commit, den wir rückgängig gemacht haben).
  3. "Initial commit with important note" (unser ursprünglicher geänderter Commit).
<commit_hash_revert> Revert "Add line to be reverted"
<commit_hash_original> Add line to be reverted
<commit_hash_initial> Initial commit with important note

Wenn Sie sich jetzt den Inhalt von hello.txt ansehen, sollte die Zeile "This line will be reverted" verschwunden sein, wodurch die Änderungen aus dem Commit "Add line to be reverted" effektiv rückgängig gemacht wurden.

Warum wird git revert in gemeinsam genutzten Repositories git reset vorgezogen?

  • Nicht-destruktive Historie (Non-Destructive History): git revert schreibt die Historie nicht um. Es fügt einen neuen Commit hinzu, um Änderungen rückgängig zu machen, wodurch es sicher für die Zusammenarbeit ist. Jeder kann den ursprünglichen Fehler und die Korrektur in der Historie sehen.
  • Vermeidet Force-Pushing (Avoids Force Pushing): git reset erfordert oft Force-Pushing (git push --force) in ein Remote-Repository, wenn Sie die Commits bereits gepusht haben. Force-Pushing kann sehr störend sein und die Arbeit anderer überschreiben. git revert vermeidet dieses Problem.
  • Klarer Audit-Trail (Clear Audit Trail): git revert bietet eine klare Aufzeichnung darüber, wann und warum Änderungen rückgängig gemacht wurden. Dies ist wertvoll, um die Entwicklung des Projekts zu verstehen und Probleme zu beheben.

Commits "cherry-picken" (Cherry-picking Commits)

"Cherry-picking" in Git ist wie das Pflücken einer Kirsche von einem Baum – Sie wählen einen bestimmten Commit von einem Branch aus und wenden ihn auf Ihren aktuellen Branch an. Dies ist nützlich, wenn Sie ein bestimmtes Feature oder einen Fix von einem anderen Branch übernehmen möchten.

Simulieren wir ein Szenario, in dem wir ein Feature haben, das in einem separaten Branch entwickelt wurde, und wir möchten nur dieses Feature in unseren Hauptbranch übernehmen.

Zuerst müssen wir einen neuen Branch erstellen und einen Commit darauf erstellen:

git checkout -b feature-branch
echo "This is a new feature" >> feature.txt
git add feature.txt
git commit -m "Add new feature"

Lassen Sie uns diese Befehle aufschlüsseln:

  1. git checkout -b feature-branch:
    • git checkout: Dieser Befehl wird verwendet, um Branches zu wechseln oder neue Branches zu erstellen.
    • -b feature-branch: Das Flag -b weist git checkout an, einen neuen Branch namens "feature-branch" zu erstellen und sofort zu diesem zu wechseln. Dieser Befehl erstellt also einen neuen Branch und macht ihn zu Ihrem aktiven Branch.
  2. echo "This is a new feature" >> feature.txt: Erstellt eine neue Datei namens "feature.txt" mit dem Inhalt "This is a new feature" in unserem "feature-branch".
  3. git add feature.txt: Staged die neu erstellte Datei "feature.txt".
  4. git commit -m "Add new feature": Erstellt einen Commit auf dem "feature-branch" mit der Nachricht "Add new feature", einschließlich des Hinzufügens von "feature.txt".

Nehmen wir nun an, wir sind vorerst mit diesem Feature fertig und möchten es in unseren Hauptbranch (master) integrieren. Zuerst müssen wir zurück zum master-Branch wechseln:

git checkout master

Dieser Befehl wechselt einfach Ihren aktiven Branch zurück zu "master".

Jetzt möchten wir den Commit "Add new feature" vom "feature-branch" auf unseren master-Branch anwenden. Wir verwenden git cherry-pick dafür:

git cherry-pick feature-branch

Warten Sie, warum feature-branch und nicht ein Commit-Hash? In diesem einfachen Fall wird feature-branch als "commit-ish" zum letzten Commit auf feature-branch aufgelöst. Da wir nur einen Commit auf feature-branch erstellt haben, "cherry-pickt" dies effektiv diesen Commit. Wenn Sie mehrere Commits auf feature-branch hätten und einen bestimmten auswählen wollten, würden Sie den Commit-Hash dieses bestimmten Commits verwenden. Sie könnten beispielsweise den Commit-Hash mit git log feature-branch --oneline finden und dann git cherry-pick <commit_hash> verwenden.

Wenn Sie git cherry-pick feature-branch ausführen, führt Git Folgendes aus:

  1. Commit finden (Find the commit): Es identifiziert den letzten Commit auf "feature-branch" (oder den Commit, den Sie per Hash angegeben haben).
  2. Änderungen anwenden (Apply changes): Es versucht, die Änderungen, die durch diesen Commit eingeführt wurden, auf Ihren aktuellen Branch (master) anzuwenden.
  3. Neuen Commit erstellen (Create a new commit): Wenn dies erfolgreich ist (keine Konflikte), erstellt Git einen neuen Commit auf Ihrem master-Branch, der die gleichen Änderungen wie der "cherry-gepickte" Commit und normalerweise die gleiche Commit-Nachricht hat.

Lassen Sie uns unser Commit-Log auf dem master-Branch überprüfen:

git log --oneline

Sie sollten einen neuen Commit auf Ihrem master-Branch mit der Nachricht "Add new feature" sehen. Wichtig ist, dass dies ein neuer Commit mit einem anderen Commit-Hash als der Commit auf feature-branch ist, obwohl die Änderungen gleich sind.

Wenn Sie auch die Dateien in Ihrem master-Branch auflisten (z. B. mit dem Befehl ls), sollten Sie dort auch die neue Datei "feature.txt" sehen.

Wichtige Überlegungen für Cherry-picking:

  • Neuer Commit (New Commit): Cherry-pick erstellt immer einen neuen Commit. Es verschiebt oder kopiert den ursprünglichen Commit nicht. Dies ist wichtig, wenn Sie über die Commit-Historie nachdenken.
  • Potenzielle Konflikte (Potential Conflicts): Cherry-picking kann manchmal zu Konflikten führen, insbesondere wenn die Codebasen der beiden Branches stark voneinander abweichen. Wenn Konflikte auftreten, pausiert Git den Cherry-Pick-Prozess und fordert Sie auf, diese zu beheben, ähnlich wie bei Merge-Konflikten.
  • Sparsam verwenden (Use Sparingly): Obwohl leistungsstark, sollte Cherry-picking mit Bedacht eingesetzt werden. Übermäßiger Gebrauch von Cherry-pick kann Ihre Historie schwerer nachvollziehbar machen, insbesondere wenn Sie später den gesamten Feature-Branch mergen. Es ist am effektivsten, um bestimmte, isolierte Änderungen oder Fixes von einem Branch auf einen anderen anzuwenden, wenn ein vollständiger Merge nicht gewünscht oder angemessen ist.

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:

  1. "Second change" in "First change" squashen (zusammenführen).
  2. "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):

  1. Insert-Modus aktivieren: Drücken Sie die Taste i. Dadurch können Sie die Datei eingeben und bearbeiten.
  2. Änderungen vornehmen: Verwenden Sie Ihre Pfeiltasten, um zu den Zeilen zu navigieren und "pick" in "squash" und "reword" zu ändern, wie oben gezeigt.
  3. Insert-Modus verlassen: Drücken Sie die Taste Esc.
  4. 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.

Rebasing von der Wurzel (Rebasing from the Root)

In Schritt 5 haben wir git rebase -i HEAD~3 verwendet, um die letzten drei Commits zu rebasen. Aber interaktives Rebase kann noch leistungsstärker sein. git rebase -i --root ermöglicht es Ihnen, alle Commits in der Historie Ihres Repositorys interaktiv zu rebasen, beginnend mit dem allerersten (Wurzel-)Commit. Dies gibt Ihnen die ultimative Kontrolle, um Ihre gesamte Projekthistorie umzugestalten.

Äußerste Vorsicht (Extreme Caution): Root-Rebasing ist eine extrem leistungsstarke Operation, und mit großer Macht kommt große Verantwortung (und das Potenzial für erhebliche Probleme bei Missbrauch). Root-Rebasing auf gemeinsam genutzten Repositories ist fast immer eine schlechte Idee und sollte vermieden werden, es sei denn, es gibt einen außergewöhnlichen und gut verstandenen Grund dafür, und nur nach sorgfältiger Abstimmung mit Ihrem gesamten Team. Es schreibt die gesamte Historie komplett um und ändert die Commit-Hashes für jeden Commit von der Wurzel an. Dies wird zu erheblichen Störungen führen, wenn andere am selben Repository zusammenarbeiten.

Für dieses Lab arbeiten wir in einem lokalen, isolierten Repository, daher ist es sicher, mit Root-Rebase zu experimentieren, um zu lernen, wie es funktioniert. Denken Sie nur an die Warnungen für reale kollaborative Projekte!

Probieren wir ein Root-Rebase aus:

git rebase -i --root

Dieser Befehl ist dem vorherigen Rebase sehr ähnlich, aber anstelle von HEAD~3 verwenden wir --root. --root weist Git an, alle Commits vom Anfang der Repository-Historie für das interaktive Rebase zu berücksichtigen.

Wenn Sie dies ausführen, wird Ihr Standardtexteditor erneut geöffnet, aber diesmal werden alle Commits in Ihrem Repository aufgelistet, beginnend mit dem allerersten Commit ("Initial commit with important note" oder was auch immer Sie ihn geändert haben). Sie sehen jetzt eine längere Liste als im vorherigen Schritt.

Möglicherweise sehen Sie etwas wie dieses (Ihre Commit-Hashes und -Nachrichten können je nachdem, wie Sie die vorherigen Schritte ausgeführt haben, variieren):

pick <hash_initial> Initial commit with important note
pick <hash_reverted> Add line to be reverted
pick <hash_revert_commit> Revert "Add line to be reverted"
pick <hash_new_feature> Add new feature
pick <hash_combined> Combined first and second changes: Initial setup of hello.txt
pick <hash_third_revised> Improved third change: Added a more descriptive line to hello.txt

Unser Root-Rebase-Plan (Our Root Rebase Plan):

Für diese Übung wollen wir ein paar Dinge mit diesem Root-Rebase tun:

  1. Ändern Sie die Nachricht des allerersten Commits (Change the message of the very first commit). Nehmen wir an, wir möchten die Nachricht unseres ersten Commits noch weiter verfeinern.
  2. Squashen Sie den "Revert"-Commit in den Commit, der die Zeile eingeführt hat, die wir rückgängig gemacht haben (Squash the "Revert" commit into the commit that introduced the line we reverted). Dies ist ein etwas konstruiertes Beispiel, aber es demonstriert, wie Sie die Historie manipulieren können. Wir könnten argumentieren, dass der "Revert"-Commit eigentlich nur Teil der Bereinigung des ursprünglichen Fehlers ist, also könnten wir ihn in den Commit squashen, der den Fehler eingeführt hat. (In einem realen Szenario möchten Sie möglicherweise nicht immer Reverts squashen, da sie eine klare Aufzeichnung des Rückgängigmachens von Änderungen liefern).

Um diesen Plan umzusetzen, ändern Sie die Editordatei so, dass sie wie folgt aussieht:

reword <hash_initial> Initial commit with important note
squash <hash_reverted> Add line to be reverted
pick <hash_revert_commit> Revert "Add line to be reverted"
pick <hash_new_feature> Add new feature
pick <hash_combined> Combined first and second changes: Initial setup of hello.txt
pick <hash_third_revised> Improved third change: Added a more descriptive line to hello.txt

Wir haben "pick" in "reword" für den ersten Commit und "pick" in "squash" für den zweiten Commit ("Add line to be reverted") geändert. Lassen Sie den "Revert"-Commit vorerst als "pick".

Speichern und schließen Sie den Editor.

Zuerst den ersten Commit umformulieren (First, Rewording the Initial Commit):

Git wird zuerst pausieren, damit Sie die Nachricht des ersten Commits umformulieren können. Der Editor wird mit der aktuellen Nachricht "Initial commit with important note" (oder was auch immer Sie ihn in Schritt 2 geändert haben) geöffnet. Ändern Sie sie in etwas wie: "Initial setup and file creation". Speichern und schließen Sie den Editor.

Als Nächstes "Add line to be reverted" in den ersten Commit squashen (Next, Squashing "Add line to be reverted" into the Initial Commit):

Git wird dann den Befehl "squash" verarbeiten. Es wird die Änderungen von "Add line to be reverted" mit dem vorherigen Commit (der jetzt "Initial setup and file creation" ist) kombinieren. Der Editor wird erneut geöffnet, damit Sie die kombinierte Commit-Nachricht bearbeiten können. Sie sehen die ursprünglichen Nachrichten beider Commits. Sie können eine neue Nachricht erstellen, die die kombinierten Änderungen widerspiegelt. Sie könnten die Nachricht beispielsweise ändern in:

Initial setup and file creation with subsequent line removal

Speichern und schließen Sie den Editor.

Rebase-Abschluss (Rebase Completion):

Git wird die Verarbeitung der restlichen Befehle fortsetzen ("pick" für die verbleibenden Commits) und das Root-Rebase abschließen.

Überprüfen der Root-rebased Historie (Verifying the Root-Rebased History):

Überprüfen Sie Ihr Commit-Log ein letztes Mal:

git log --oneline

Sie sollten Ihre gesamte Commit-Historie sehen, aber mit den Änderungen, die Sie im Root-Rebase angewiesen haben:

  • Der allererste Commit sollte die neue Nachricht "Initial setup and file creation with subsequent line removal" (oder ähnlich) haben.
  • Der Commit "Add line to be reverted" und der Commit "Revert" sind möglicherweise noch vorhanden, oder sie wurden möglicherweise kombiniert, je nachdem, wie genau Sie das Rebase angewiesen haben. (In den obigen Beispielanweisungen haben wir "Revert" als "pick" beibehalten, also sollte er noch vorhanden sein).
  • Commit-Hashes haben sich in der gesamten Historie vom Root-Commit an geändert.

Denken Sie nochmals an die äußerste Vorsicht beim Root-Rebase, insbesondere in gemeinsam genutzten Repositories! Dieser Schritt dient in erster Linie zu Bildungszwecken, um die Leistungsfähigkeit und Flexibilität des interaktiven Rebasing zu demonstrieren.

Zusammenfassung (Summary)

Herzlichen Glückwunsch, Git-Meister! Sie haben Ihre Fähigkeiten in der Versionskontrolle gerade noch weiter verbessert. Fassen wir die leistungsstarken Techniken zusammen, die Sie in diesem Lab gelernt haben:

  1. Commits ändern (Amending Commits): Sie können jetzt kleinere Fehler beheben oder kleine Änderungen an Ihrem letzten Commit hinzufügen, ohne einen neuen Commit zu erstellen, wodurch Ihre Historie für kleinere Anpassungen sauber bleibt.
  2. Commits rückgängig machen (Reverting Commits): Sie haben gelernt, wie Sie Änderungen sicher rückgängig machen können, indem Sie einen neuen Commit erstellen, der einen vorherigen Commit umkehrt. Dies ist die nicht-destruktive Methode, um Fehler in einer kollaborativen Umgebung zu beheben.
  3. Cherry-Picking (Cherry-picking): Sie können jetzt bestimmte Commits von einem Branch auf einen anderen anwenden, wodurch Sie eine feinkörnige Kontrolle darüber haben, welche Änderungen Sie übernehmen. Perfekt, um selektiv Funktionen oder Fehlerbehebungen einzubringen.
  4. Interaktives Rebasing (Interactive Rebasing): Sie haben die Kunst des Umschreibens der Historie gemeistert, wodurch Sie Ihre Commits bereinigen und organisieren können, bevor Sie sie mit anderen teilen. Dies beinhaltet das Neuanordnen, Squashen und Bearbeiten von Commit-Nachrichten für eine sauberere Historie.
  5. Root-Rebasing (Root Rebasing): Sie haben gelernt, wie Sie Ihre gesamte Projekthistorie umgestalten können, beginnend mit dem allerersten Commit, wodurch Sie die ultimative Kontrolle über die Erzählung Ihres Repositorys erhalten. Denken Sie an die Macht und das Risiko, die damit verbunden sind!

Diese fortgeschrittenen Git-Operationen sind unglaublich leistungsstarke Werkzeuge im Werkzeugkasten eines Entwicklers. Sie ermöglichen es Ihnen, eine saubere, organisierte Commit-Historie zu pflegen, was entscheidend ist, wenn Sie an großen Projekten arbeiten oder mit einem Team zusammenarbeiten. Eine gut gepflegte Commit-Historie dient nicht nur der Ästhetik; sie verbessert die Code-Überprüfung, das Debugging und das Verständnis der Projektentwicklung im Laufe der Zeit erheblich.