Direkt zum Inhalt

Git Squash Commits: Ein Leitfaden mit Beispielen

Lerne, wie du mit interaktivem Rebase Commits auf einem Branch zerdrücken kannst, um eine saubere und organisierte Commit-Historie zu erhalten.
Aktualisierte 5. Nov. 2024  · 7 Min. Lesezeit

"Commit early, commit often" ist ein beliebtes Mantra in der Softwareentwicklung, wenn man Git verwendet. So wird sichergestellt, dass jede Änderung gut dokumentiert ist, die Zusammenarbeit verbessert und die Entwicklung des Projekts leichter nachvollziehbar wird. Das kann aber auch zu einem Übermaß an Commits führen.

An dieser Stelle kommt die Bedeutung von ins Spiel. Beim Squashing von Commits werden mehrere Commit-Einträge zu einem einzigen, zusammenhängenden Commit zusammengefasst.

Werde Dateningenieur

Werde ein Dateningenieur durch fortgeschrittenes Python-Lernen
Kostenloses Lernen Beginnen

Nehmen wir zum Beispiel an, dass wir an einem Feature arbeiten, das ein Anmeldeformular implementiert, und wir erstellen die folgenden vier Commits:

Beispiel für einen Git Commit-Verlauf

Sobald das Feature fertiggestellt ist, sind diese Commits für das Gesamtprojekt zu detailliert. Wir müssen in Zukunft nicht wissen, dass wir auf einen Fehler gestoßen sind, der während der Entwicklung behoben wurde. Um einen sauberen Verlauf im Hauptzweig zu gewährleisten, fassen wir diese Commits zu einem einzigen Commit zusammen:

Beispiel für das Zerdrücken von Git Commits

Wie man Commits in Git zerdrückt: Interaktiver Rebase

Die gängigste Methode, um Commits zu zerdrücken, ist ein interaktiver Rebase. Wir starten es mit dem Befehl:

git rebase -i HEAD~<number_of_commits>

Ersetze durch die Anzahl der Commits, die wir zerdrücken wollen.

In unserem Fall haben wir vier Commits, also lautet der Befehl:

git rebase -i HEAD~4

Wenn du diesen Befehl ausführst, wird ein interaktiver Befehlszeileneditor geöffnet:

CLI-Editor nach git rebase -i

Im oberen Bereich werden die Commits angezeigt, während der untere Bereich Kommentare dazu enthält, wie man Commits zerdrücken kann.

Wir haben vier Zusagen. Für jeden müssen wir entscheiden, welcher Befehl ausgeführt werden soll. Wir kümmern uns um die Befehle pick (p) und squash (s). Um diese vier Commits in einen einzigen Commit zusammenzufassen, können wir den ersten Commit auswählen und die restlichen drei Commits entfernen.

Wir wenden die Befehle an, indem wir den Text vor jedem Commit ändern, insbesondere ändern wir pick in s oder squash für den zweiten, dritten und vierten Commit. Um diese Änderungen vorzunehmen, müssen wir den "INSERT"-Modus im Befehlszeilen-Texteditor aufrufen, indem wir die Taste i auf der Tastatur drücken:

Einfügemodus im CLI-Editor aufrufen

Nachdem du i gedrückt hast, erscheint unten der Text -- INSERT --, der anzeigt, dass wir in den Einfügemodus gewechselt haben. Jetzt können wir den Cursor mit den Pfeiltasten bewegen, Zeichen löschen und wie in einem normalen Texteditor schreiben:

Interaktion mit dem CLI-Texteditor

Wenn wir mit den Änderungen zufrieden sind, müssen wir den Einfügemodus beenden, indem wir die Taste Esc auf der Tastatur drücken. Im nächsten Schritt speichern wir unsere Änderungen und verlassen den Editor. Dazu drücken wir zuerst die Taste :, um dem Editor zu signalisieren, dass wir einen Befehl ausführen wollen:

Gib einen Befehl in den CLI-Texteditor ein

Am unteren Rand des Editors sehen wir jetzt ein Semikolon :, das uns auffordert, einen Befehl einzufügen. Um die Änderungen zu speichern, verwenden wir den Befehl w, der für "schreiben" steht. Um den Editor zu schließen, benutze q, was für "quit" steht. Diese Befehle können kombiniert und zusammen getippt werden wq:

So speicherst und beendest du den CLI-Texteditor

Um den Befehl auszuführen, drücken wir die Taste Enter. Mit dieser Aktion wird der aktuelle Editor geschlossen und ein neuer Editor geöffnet, in den wir die Commit-Nachricht für den neu gequetschten Commit eingeben können. Der Editor zeigt eine Standardmeldung an, die die Meldungen der vier Commits enthält, die wir zerdrücken:

Bearbeiten der Squash-Übertragungsnachricht

Ich empfehle, die Nachricht so zu ändern, dass sie genau die Änderungen widerspiegelt, die durch diese kombinierten Commits implementiert wurden - schließlich ist es der Zweck des Squashings, eine saubere und leicht lesbare Geschichte zu erhalten. 

Um mit dem Editor zu interagieren und die Nachricht zu bearbeiten, drücken wir erneut i, um in den Bearbeitungsmodus zu gelangen und die Nachricht nach unseren Wünschen zu bearbeiten.

Beispiel für eine Squash-Übertragungsnachricht

In diesem Fall ersetzen wir die Commit-Meldung durch "Implementiere das Anmeldeformular". Um den Bearbeitungsmodus zu verlassen, drücken wir Esc. Dann speicherst du die Änderungen, indem du : drückst, den Befehl wq eingibst und Enter drückst.

Wie man den Commit-Verlauf anzeigt

Im Allgemeinen kann es schwierig sein, die gesamte Commit-Historie abzurufen. Um die Commit-Historie einzusehen, können wir den Befehl git log verwenden. In dem genannten Beispiel würde vor dem Squash die Ausführung des Befehls git log angezeigt:

Git log commit history before squash

Um in der Liste der Commits zu navigieren, benutze die Pfeiltasten nach oben und unten. Um zu beenden, drücke q.

Wir können git log nutzen, um den Erfolg des Squash zu bestätigen. Wenn du sie nach dem Squash ausführst, wird ein einzelner Commit mit der neuen Nachricht angezeigt:

Git log commit history nach Squash

Gequetschte Übertragung verschieben

Der obige Befehl wird auf das lokale Repository angewendet. Um das Remote-Repository zu aktualisieren, müssen wir unsere Änderungen pushen. Da wir jedoch den Commit-Verlauf geändert haben, müssen wir den Push mit der Option --force erzwingen:

git push --force origin feature/login-form

Ein erzwungener Push überschreibt die Commit-Historie auf dem entfernten Zweig und stört möglicherweise andere, die an diesem Zweig arbeiten. Es ist eine gute Praxis, mit dem Team zu kommunizieren, bevor du das tust.

Eine sicherere Methode, Push zu erzwingen, die das Risiko einer Störung von Mitwirkenden verringert, ist die Option --force-with-lease zu verwenden:

git push --force-with-lease origin feature/login-form

Diese Option stellt sicher, dass wir den Push nur dann erzwingen, wenn der entfernte Zweig seit unserem letzten Fetch oder Pull nicht aktualisiert wurde.

Bestimmte Commits unterdrücken

Stell dir vor, wir haben fünf Zusagen:

Beispiel für ein Git-Protokoll mit fünf Commits

Angenommen, wir wollen die Commits 1, 2 und 5 behalten und die Commits 3 und 4 löschen.

Jede Verpflichtung unterdrücken

Wenn du interaktives Rebase verwendest, werden Commits, die zum Squashing markiert sind, mit dem direkt vorangehenden Commit kombiniert. In diesem Fall bedeutet das, dass wir Commit4 so zerquetschen wollen, dass es in Commit3 aufgeht.

Dazu müssen wir einen interaktiven Rebase initiieren, der diese beiden Commits einschließt. In diesem Fall reichen drei Commits aus, also verwenden wir den Befehl:

git rebase -i HEAD~3

Dann setzen wir Commit4 auf s, so dass es mit Commit3 gequetscht wird:

Beispiel für Squash Two Commit

Nachdem wir diesen Befehl ausgeführt und die Commits aufgelistet haben, stellen wir fest, dass die Commits 3 und 4 zusammengelegt wurden, während der Rest unverändert bleibt.

Git-Log nach dem Zerquetschen

Squashing von einem bestimmten Commit

Im Befehl git rebase -i HEAD~3 ist der Teil HEAD eine Abkürzung für die letzte Übertragung. Die ~3 Syntax wird verwendet, um einen Vorgänger eines Commits anzugeben. Zum Beispiel bezieht sich HEAD~1 auf die übergeordnete Seite des HEAD Commits.

Illustration der ~ Notation

Bei einem interaktiven Rebase werden alle Vorgänger-Commits bis zu dem im Befehl angegebenen Commit berücksichtigt. Beachte, dass der angegebene Commit nicht enthalten ist:

Wie Commits in ein interaktives Rebase aufgenommen werden

Anstatt HEAD zu verwenden, können wir auch direkt einen Commit-Hash angeben. Zum Beispiel hat Commit2 einen Hash von dbf3cc118d6d7c08ef9c4a326b26dbb1e3fe9ddf, so dass der Befehl:

git rebase -i dbf3cc118d6d7c08ef9c4a326b26dbb1e3fe9ddf

würde einen Rebase starten, der alle Commits berücksichtigt, die nach Commit2 gemacht wurden. Wenn wir also einen Rebase bei einem bestimmten Commit starten und diesen Commit einbeziehen wollen, können wir den Befehl verwenden:

git rebase -i <commit-hash>~1

Auflösen von Konflikten beim Squashing von Commits

Wenn wir Commits zerdrücken, fassen wir mehrere Änderungen in einem einzigen Commit zusammen, was zu Konflikten führen kann, wenn sich die Änderungen überschneiden oder stark voneinander abweichen. Hier sind einige häufige Szenarien, in denen Konflikte entstehen können:

  1. Überschneidende Änderungen: Wenn zwei oder mehr Commits, die gequetscht werden sollen, dieselben Zeilen einer Datei oder eng miteinander verbundene Zeilen geändert haben, kann Git diese Änderungen möglicherweise nicht automatisch abgleichen. 
  2. Unterschiedliche Zustandsänderungen: Wenn ein Commit ein bestimmtes Stück Code hinzufügt und ein anderer Commit dasselbe Stück Code ändert oder löscht, kann das Zerdrücken dieser Commits zu Konflikten führen, die gelöst werden müssen.
  3. Umbenennen und Ändern von: Wenn ein Commit eine Datei umbenennt und nachfolgende Commits Änderungen an dem alten Namen vornehmen, kann das Zerdrücken dieser Commits Git verwirren und einen Konflikt verursachen.
  4. Änderungen an Binärdateien: Binärdateien lassen sich mit textbasierten Diff-Tools nicht gut zusammenführen. Wenn mehrere Commits dieselbe Binärdatei ändern und wir versuchen, sie zu zerdrücken, kann ein Konflikt entstehen, weil Git diese Änderungen nicht automatisch abgleichen kann.
  5. Komplexe Geschichte: Wenn die Commits eine komplexe Historie mit mehreren Merges, Branches oder Rebases haben, kann das Zerdrücken der Commits zu Konflikten führen, da die Änderungen nicht linear verlaufen.

Beim Squashing versucht Git, jede Änderung nacheinander anzuwenden. Wenn es während des Prozesses auf Konflikte stößt, hält es an und ermöglicht uns, diese zu lösen. 

Konflikte werden mit den Konfliktmarkern <<<<<< und >>>>>> gekennzeichnet. Um mit den Konflikten umzugehen, müssen wir die Dateien öffnen und sie manuell auflösen, indem wir auswählen, welchen Teil des Codes wir behalten wollen. 

Nachdem wir die Konflikte gelöst haben, müssen wir die gelösten Dateien mit dem Befehl git add bereitstellen. Dann können wir das Rebase mit dem folgenden Befehl fortsetzen:

git rebase --continue

Weitere Informationen zu Git-Konflikten findest du in diesem Tutorial über wie man Merge-Konflikte in Git auflöst.

Alternativen zum Squashing mit Rebase

Der Befehl git merge --squash ist eine alternative Methode zu git rebase -i, um mehrere Commits zu einem einzigen Commit zusammenzufassen. Dieser Befehl ist besonders nützlich, wenn wir Änderungen aus einem Zweig in den Hauptzweig zusammenführen und dabei alle einzelnen Commits in einen einzigen zusammenfassen wollen. Hier ist ein Überblick darüber, wie du mit git merge squashen kannst:

  1. Wir navigieren zu dem Zielzweig, in den wir die Änderungen einfügen wollen.
  2. Wir führen den Befehl git merge --squash aus und ersetzen durch den Namen des Zweigs.
  3. Wir übertragen die Änderungen mit git commit, um einen einzigen Commit zu erstellen, der alle Änderungen des Feature-Branches enthält.

Nehmen wir zum Beispiel an, wir wollen die Änderungen des Zweigs feature/login-form in main als einen einzigen Commit einbinden:

git checkout main
git merge --squash feature-branch
git commit -m "Implement login form"

Dies sind die Einschränkungen dieses Ansatzes im Vergleich zu git rebase -i:

  • Granularität der Kontrolle: Weniger Kontrolle über einzelne Commits. Mit rebase können wir auswählen, welche Commits zusammengeführt werden sollen, während merge alle Änderungen in einem Commit zusammenfasst.
  • Zwischenzeitliche Geschichte: Wenn du die Zusammenführung verwendest, geht die individuelle Commit-Historie aus dem Feature-Zweig im Hauptzweig verloren. Das kann es schwieriger machen, die inkrementellen Änderungen zu verfolgen, die während der Entwicklung des Features vorgenommen wurden.
  • Überprüfung vor der Mittelbindung: Da alle Änderungen als ein einziger Änderungssatz aufgeführt werden, können wir nicht jeden Commit einzeln überprüfen oder testen, bevor wir ihn zerdrücken. Dies ist anders als bei einem interaktiven Rebase, bei dem jeder Commit nacheinander überprüft und getestet werden kann.

Fazit

Häufige und kleine Commits in den Entwicklungsworkflow einzubauen, fördert die Zusammenarbeit und eine klare Dokumentation, kann aber auch die Projekthistorie durcheinander bringen. Das Squashing von Commits schafft ein Gleichgewicht, indem es die wichtigen Meilensteine bewahrt und gleichzeitig den Lärm kleinerer iterativer Änderungen eliminiert.

Um mehr über Git zu erfahren, empfehle ich diese Ressourcen:


Photo of François Aubry
Author
François Aubry
LinkedIn
Das Unterrichten war schon immer meine Leidenschaft. Schon als Schülerin habe ich eifrig nach Möglichkeiten gesucht, anderen Schülern Nachhilfe zu geben und sie zu unterstützen. Diese Leidenschaft führte dazu, dass ich einen Doktortitel anstrebte, wobei ich auch als Lehrassistentin tätig war, um meine akademischen Bemühungen zu unterstützen. In diesen Jahren fand ich im traditionellen Klassenzimmer große Erfüllung, indem ich Verbindungen förderte und das Lernen erleichterte. Mit dem Aufkommen von Online-Lernplattformen erkannte ich jedoch das transformative Potenzial der digitalen Bildung. Ich war sogar aktiv an der Entwicklung einer solchen Plattform an unserer Hochschule beteiligt. Es ist mir ein großes Anliegen, traditionelle Unterrichtsprinzipien mit innovativen digitalen Methoden zu verbinden. Meine Leidenschaft ist es, Kurse zu erstellen, die nicht nur ansprechend und informativ, sondern auch für Lernende im digitalen Zeitalter zugänglich sind.
Themen

Lerne Data Engineering mit diesen Kursen!

Lernpfad

Associate Data Engineer

30 Stunden hr
Lerne die Grundlagen des Data Engineering: Datenbankdesign und Data Warehousing und arbeite mit Technologien wie PostgreSQL und Snowflake!
Siehe DetailsRight Arrow
Kurs Starten
Zertifizierung verfügbar

Kurs

Data Engineering verstehen

2 hr
246.8K
Entdecke, wie Data Engineers die Grundlagen für Data Science schaffen. Kein Programmieren erforderlich!
Mehr anzeigenRight Arrow