Fortgeschrittene Git-Commit-Operationen

GitBeginner
Jetzt üben

Einführung

Willkommen zurück, Git-Abenteurer! Sie haben Ihre ersten Schritte in der Welt der Versionskontrolle gemacht, und jetzt ist es an der Zeit, Ihre Fähigkeiten auf die nächste Stufe zu heben. In diesem Labor werden wir einige der fortgeschritteneren Commit-Operationen von Git erkunden. Diese Techniken geben Ihnen noch mehr Kontrolle über die Historie Ihres Projekts und ermöglichen es Ihnen, Fehler zu korrigieren, Ihre Arbeit neu zu organisieren und effektiver zusammenzuarbeiten.

Betrachten Sie dieses Labor als ein Upgrade für Ihre Zeitmaschine. Sie können nicht nur durch die Zeit reisen, sondern lernen jetzt auch, wie Sie den Zeitstrahl selbst verändern können! Keine Sorge, wenn das entmutigend klingt – wir führen Sie durch jeden Schritt und erklären nicht nur, wie man diese Operationen durchführt, sondern auch, warum sie in realen Szenarien nützlich sind.

Am Ende dieses Labors werden Sie in der Lage sein, Commits zu ändern (amend), Änderungen rückgängig zu machen (revert), spezifische Commits auszuwählen (cherry-pick), interaktives Rebasing durchzuführen und Commits zusammenzuführen (squash). Dies sind leistungsstarke Werkzeuge, die professionelle Entwickler täglich einsetzen, um saubere und organisierte Projekthistorien zu pflegen. Tauchen wir ein und bringen wir Ihre Git-Kenntnisse auf das nächste Level!

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 bietet uns einen sauberen Raum zum Experimentieren, ohne Ihre bestehenden Projekte zu beeinflussen.

Öffnen Sie Ihr Terminal und geben Sie diese Befehle ein, wobei Sie nach jeder Zeile 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 (~) zeigt. Dieser Befehl navigiert Ihr Terminal in dieses Verzeichnis. Falls das Verzeichnis "project" nicht existiert, erhalten Sie möglicherweise eine Fehlermeldung. Erstellen Sie es in diesem Fall zuerst mit mkdir ~/project und versuchen Sie cd ~/project erneut.
  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. Dieser neue Ordner wird die Wurzel unseres Git-Repositorys für dieses Labor sein.
  3. cd git-advanced-lab: Dieser Befehl wechselt erneut Ihr aktuelles Verzeichnis, diesmal in das neu erstellte Verzeichnis "git-advanced-lab". Alle Befehle, die Sie nun ausführen, werden innerhalb dieses Verzeichnisses 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 ein und macht es zu einem Git-Repository. Sie werden sehen, dass ein versteckter Ordner namens .git erstellt wurde. Dieser Ordner ist das Herzstück Ihres Repositorys und speichert die gesamte Versionshistorie und Konfiguration.

Da wir nun ein Git-Repository initialisiert haben, erstellen wir eine einfache Datei und führen unseren ersten Commit durch:

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

Das passiert bei diesen Befehlen:

  1. echo "Hello, Advanced Git" > hello.txt: echo ist ein Befehl, der Text ausgibt. > ist ein Umleitungsoperator. Er nimmt die Ausgabe von echo und leitet sie in eine Datei namens "hello.txt" um. Wenn die Datei nicht existiert, wird sie erstellt; falls sie existiert, wird sie überschrieben.
  2. git add hello.txt: Bevor Git Änderungen verfolgen kann, müssen Sie Git explizit anweisen, damit zu beginnen. Dieser Befehl stellt die Datei in den Index (Staging Area). Das bedeutet, die Datei wird für den nächsten Commit vorbereitet.
  3. git commit -m "Initial commit": git commit nimmt alle Änderungen, die sich derzeit im Index befinden, und speichert sie als neuen Commit in der Historie des Repositorys. -m fügt eine Commit-Nachricht hinzu, die eine kurze Beschreibung der Änderungen sein sollte.

Hervorragend! Wir haben nun ein Repository mit einem Commit. Überprüfen wir den Status, um sicherzustellen, dass alles korrekt eingerichtet ist:

git status

Nach der Ausführung von git status sollten Sie eine Meldung sehen, die etwa so aussieht:

On branch master
nothing to commit, working tree clean

Diese Meldung zeigt an, dass Ihr Arbeitsverzeichnis "sauber" ist. Das bedeutet, es gibt keine Änderungen in Ihrem Verzeichnis, die noch nicht committet wurden. Wir sind bereit für die fortgeschrittenen Operationen!

Den letzten Commit ändern

Stellen Sie sich vor, Sie haben gerade einen Commit erstellt, merken dann aber, dass Sie eine Änderung in einer Datei vergessen haben oder einen Tippfehler in der Commit-Nachricht gemacht haben. Anstatt einen komplett neuen Commit für eine so kleine Korrektur zu erstellen, erlaubt Git Ihnen, den letzten Commit mit der Option --amend zu korrigieren. Das ist so, als würde man in der Zeit ein kleines Stück zurückgehen, um die letzte Aktion anzupassen.

Probieren wir es aus. Zuerst modifizieren wir 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 an. Beachten Sie den Unterschied beim Operator >>:

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

Nach diesem Befehl enthält hello.txt zwei Zeilen. Nehmen wir nun an, wir hätten diesen Hinweis bereits im ersten Commit haben wollen. Wir können den vorherigen Commit ändern, um diese Änderung einzuschließen und die Nachricht zu aktualisieren.

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

Hier ist die Aufschlüsselung:

  1. git add hello.txt: Da wir die Datei geändert haben, müssen wir sie erneut in den Index aufnehmen.
  2. git commit --amend -m "Initial commit with important note": Hier passiert die Magie.
    • git commit --amend: Das Flag --amend sagt Git, dass wir den letzten Commit modifizieren wollen, anstatt einen neuen zu erstellen.
    • -m "...": Dies gibt eine neue Commit-Nachricht an. Wenn Sie -m weglassen, öffnet Git Ihren Standard-Texteditor, damit Sie die ursprüngliche 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 weg, und der neue enthält sowohl die Änderungen als auch die aktualisierte Nachricht.

Überprüfen wir dies mit dem Log:

git log --oneline

Sie sollten nur einen Commit in der Ausgabe sehen, aber die Nachricht sollte nun "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 Hinweise zum Ändern (Amending):

  • Nur unveröffentlichte Commits ändern: Sie sollten nur Commits ändern, die Sie noch nicht in ein gemeinsames Remote-Repository (wie GitHub oder GitLab) hochgeladen (gepusht) haben. Das Ändern eines bereits gepushten Commits schreibt die Historie um, auf die sich andere verlassen könnten, was zu erheblichen Problemen in der Zusammenarbeit führen kann.
  • Lokale Bereinigung: Das Ändern ist primär nützlich, um Ihre lokale Historie aufzuräumen, bevor Sie Ihre Arbeit teilen.

Einen Commit rückgängig machen

Manchmal stellen Sie nach einem Commit fest, dass dieser einen Fehler eingeführt hat oder Sie die Änderungen einfach nicht mehr wollen. Sie könnten daran denken, git reset zu verwenden, um in der Zeit zurückzugehen. git reset kann jedoch destruktiv sein, besonders wenn Sie Änderungen bereits geteilt haben. Ein sichererer Weg für die Zusammenarbeit ist git revert.

git revert erstellt einen neuen Commit, der die Änderungen eines spezifischen Commits rückgängig macht. Es löscht den ursprünglichen Commit nicht aus der Historie; stattdessen fügt es einen neuen hinzu, der die Effekte des unerwünschten Commits umkehrt. Dies bewahrt die Historie und ist viel sicherer für geteilte Repositorys.

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

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

Nun entscheiden wir, dass diese Zeile ein Fehler war. Wir machen den letzten Commit rückgängig mit:

git revert HEAD

Aufschlüsselung des Befehls:

  • git revert: Der Befehl zum Umkehren von Änderungen.
  • HEAD: In Git ist HEAD ein Zeiger auf den aktuellen Commit Ihres aktuellen Branches (meist der neueste). git revert HEAD bedeutet also "mache den neuesten Commit rückgängig".

Wenn Sie diesen Befehl ausführen, passiert Folgendes:

  1. Analyse: Git schaut sich die Änderungen des HEAD-Commits an.
  2. Gegenoperation: Git berechnet, wie diese Änderungen rückgängig gemacht werden können (in unserem Fall: Entfernen der Zeile).
  3. Neuer Commit: Git erstellt automatisch einen neuen Commit mit diesen Umkehr-Änderungen.
  4. Editor (Optional): Git öffnet normalerweise Ihren Texteditor mit einer vorgefertigten Nachricht wie "Revert 'Add line to be reverted'". Sie können diese bestätigen oder anpassen.

Umgang mit dem Texteditor:

Falls sich ein Editor wie Vim öffnet:

  • In Vim: Drücken Sie Esc, tippen Sie :wq und drücken Sie Enter.
  • In Nano: Drücken Sie Strg + X, dann Y und Enter.

Prüfen wir nun die Historie:

git log --oneline

Sie sollten nun drei Commits sehen:

  1. Den Revert-Commit (ganz oben).
  2. Den Commit, den wir rückgängig gemacht haben.
  3. Den ursprünglichen "Initial commit".

Warum ist git revert besser als git reset in Teams?

  • Nicht-destruktive Historie: Es wird nichts gelöscht. Jeder kann sehen, dass ein Fehler gemacht und korrigiert wurde.
  • Kein Force-Push nötig: git reset erfordert oft ein riskantes git push --force. git revert vermeidet dies.
  • Nachvollziehbarkeit: Es bietet einen klaren Prüfpfad (Audit Trail).

Cherry-picking von Commits

Cherry-picking in Git ist wie das Pflücken einer Kirsche vom Baum – Sie wählen einen spezifischen Commit von einem Branch aus und wenden ihn auf Ihren aktuellen Branch an. Dies ist nützlich, wenn Sie nur eine bestimmte Funktion oder einen Fix aus einem anderen Branch übernehmen möchten, ohne den ganzen Branch zu mergen.

Simulieren wir ein Szenario, in dem wir ein Feature in einem separaten Branch entwickelt haben:

git checkout -b feature-branch
echo "This is a new feature" >> feature.txt
git add feature.txt
git commit -m "Add new feature"
  1. git checkout -b feature-branch: Erstellt einen neuen Branch namens "feature-branch" und wechselt sofort dorthin.
  2. Wir erstellen eine Datei und committen sie auf diesem neuen Branch.

Nun möchten wir dieses Feature in unseren Hauptbranch (master) integrieren. Zuerst wechseln wir zurück:

git checkout master

Jetzt wenden wir den Commit "Add new feature" vom feature-branch auf unseren master an:

git cherry-pick feature-branch

In diesem einfachen Fall steht feature-branch für den neuesten Commit auf diesem Branch. Wenn Sie einen spezifischen Commit aus einer Reihe von Commits wählen wollten, würden Sie stattdessen den Commit-Hash verwenden (den Sie mit git log finden).

Was Git beim Cherry-picking tut:

  1. Es identifiziert den Commit.
  2. Es wendet die Änderungen auf den aktuellen Branch an.
  3. Es erstellt einen neuen Commit auf dem master-Branch mit denselben Änderungen und derselben Nachricht.

Überprüfen Sie den Log auf master:

git log --oneline

Sie sehen einen neuen Commit "Add new feature". Beachten Sie, dass dieser eine andere Hash-ID hat als das Original auf dem Feature-Branch, da es sich um eine Kopie der Änderungen handelt, die in die aktuelle Historie eingefügt wurde.

Wichtige Hinweise zum Cherry-picking:

  • Neuer Commit: Es wird immer ein neuer Commit erstellt, das Original wird nicht verschoben.
  • Konflikte: Wie beim Mergen können Konflikte auftreten, wenn die Codebasis sich zu stark unterscheidet.
  • Sparsam einsetzen: Zu viel Cherry-picking kann die Historie unübersichtlich machen. Es ist ideal für isolierte Fixes.

Interaktives Rebasing

Interaktives Rebasing ist eines der mächtigsten Werkzeuge von Git. Es erlaubt Ihnen, Ihre Commit-Historie flexibel umzuschreiben: Commits umordnen, zusammenführen (squash), Nachrichten bearbeiten oder Commits ganz löschen.

Warnung: Da Rebasing die Historie umschreibt, ist es riskant auf geteilten Branches. Rebasen Sie niemals Commits, die bereits in ein öffentliches Repository gepusht wurden.

Erstellen wir eine Reihe von Commits zum Üben:

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"

Das Flag -am bei git commit ist eine Abkürzung für "alle geänderten Dateien indexieren und committen".

Nehmen wir an, wir wollen diese drei Commits aufräumen. Wir möchten die ersten beiden kombinieren und die Nachricht des dritten verbessern. Starten wir die interaktive Sitzung:

git rebase -i HEAD~3
  • -i: Steht für "interactive".
  • HEAD~3: Bezieht sich auf die letzten drei Commits ab dem aktuellen Stand.

Es öffnet sich ein Editor mit einer Liste der Commits:

pick 63c95db First change
pick 68e7909 Second change
pick 5371424 Third change

Befehle im Editor:

  • pick (p): Commit so lassen, wie er ist.
  • reword (r): Nachricht ändern.
  • squash (s): Commit mit dem vorherigen (darüberliegenden) zusammenführen.
  • drop (d): Commit entfernen.

Unser Plan:

  1. "Second change" in "First change" einsaugen (squash).
  2. "Third change" umbenennen (reword).

Ändern Sie den Text im Editor wie folgt:

pick abc1234 First change
squash def5678 Second change
reword ghi9101 Third change

(Hinweis: Ändern Sie nicht die Hashes, nur die Wörter am Anfang der Zeile.)

Speichern und schließen Sie den Editor.

Schritt 1: Squashing
Git öffnet nun einen Editor, um die kombinierte Nachricht für die ersten beiden Commits festzulegen. Ändern Sie sie in:
Combined first and second changes: Initial setup of hello.txt

Schritt 2: Rewording
Danach öffnet Git einen weiteren Editor für den dritten Commit. Ändern Sie die Nachricht in:
Improved third change: Added a more descriptive line to hello.txt

Nach dem Speichern meldet Git: "Successfully rebased and updated...".

Überprüfen Sie das Ergebnis:

git log --oneline

Sie haben nun weniger Commits, sauberere Nachrichten, aber neue Commit-Hashes. Falls beim Rebasen etwas schiefgeht, können Sie den Vorgang jederzeit mit git rebase --abort abbrechen.

Rebasing von der Wurzel (Root)

In Schritt 5 haben wir die letzten drei Commits bearbeitet. Mit git rebase -i --root können Sie jedoch die gesamte Historie Ihres Repositorys ab dem allerersten Commit bearbeiten.

Extreme Vorsicht: Dies schreibt die komplette Geschichte neu. In echten Projekten ist dies fast immer ein Tabu, es sei denn, das Projekt ist noch rein lokal oder es gibt einen außergewöhnlichen Grund.

Da wir uns in einer sicheren Laborumgebung befinden, probieren wir es aus:

git rebase -i --root

Der Editor zeigt nun jeden einzelnen Commit an, den wir seit Beginn des Labors erstellt haben.

Unser Plan für den Root-Rebase:

  1. Die Nachricht des allerersten Commits verfeinern (reword).
  2. Den "Add line to be reverted"-Commit in den ersten Commit integrieren (squash), um den Fehler so zu behandeln, als wäre er nie passiert.

Ändern Sie die ersten Zeilen entsprechend auf reword und squash, speichern Sie und folgen Sie den Anweisungen in den sich öffnenden Editoren, um die Nachrichten anzupassen.

Nach Abschluss wird git log --oneline eine völlig neue, "perfekte" Historie zeigen, als hätten Sie von Anfang an alles richtig gemacht.

Zusammenfassung

Herzlichen Glückwunsch, Git-Meister! Sie haben Ihre Fähigkeiten in der Versionskontrolle massiv erweitert. Hier ist ein Rückblick auf das, was Sie gelernt haben:

  1. Commits ändern (Amend): Kleine Fehler im letzten Commit korrigieren, ohne die Historie aufzublähen.
  2. Änderungen rückgängig machen (Revert): Sicherer Weg, Fehler in einer Teamumgebung durch Gegentransaktionen zu korrigieren.
  3. Cherry-picking: Gezieltes Übernehmen einzelner Änderungen von einem Branch in einen anderen.
  4. Interaktives Rebasing: Die Kunst, die lokale Historie durch Zusammenführen und Umbenennen von Commits zu polieren.
  5. Root Rebasing: Die ultimative Kontrolle über die gesamte Erzählung Ihres Projekts.

Diese fortgeschrittenen Operationen helfen Ihnen, eine saubere und aussagekräftige Projekthistorie zu führen. Eine gut gepflegte Historie ist nicht nur Ästhetik; sie erleichtert Code-Reviews, die Fehlersuche und das Verständnis der Projektentwicklung über lange Zeiträume erheblich.