„Früher committen, öfter committen“ ist ein beliebtes Mantra in der Softwareentwicklung bei der Verwendung von Git. Dies stellt sicher, dass jede Änderung gut dokumentiert ist, die Zusammenarbeit verbessert wird und die Entwicklung des Projekts einfacher zu verfolgen ist. Dies kann jedoch auch zu einer Überzahl von Commits führen.
Jetzt kommt die Bedeutung des Commits zusammenfassen ins Spiel. Das Zusammenfassen von Commits ist der Prozess, bei dem mehrere Commit-Einträge zu einem kohärenten Commit kombiniert werden.
Angenommen, wir arbeiten an einer Funktion zur Implementierung eines Anmeldeformulars und erstellen die folgenden vier Commits:
Sobald die Funktion abgeschlossen ist, sind diese Commits für das Gesamtvorhaben zu detailliert. Wir müssen in der Zukunft nicht wissen, dass wir auf einen Bug gestoßen sind, der während der Entwicklung behoben wurde. Um eine saubere Historie im Hauptzweig zu gewährleisten, vereinen wir diese Commits in einen einzigen Commit:
Wie man Commits in Git zusammenfasst: Interaktives Rebase
Die gebräuchlichste Methode, um Commits zusammenzufassen, ist die Verwendung eines interaktiven Rebase. Wir starten es mit dem Befehl:
git rebase -i HEAD~<number_of_commits>
Ersetzen Sie <number_of_commits>
durch die Anzahl der Commits, die wir zusammenfassen möchten.
In unserem Fall haben wir vier Commits, also lautet der Befehl:
git rebase -i HEAD~4
Das Ausführen dieses Befehls öffnet einen interaktiven Befehlszeilen-Editor:
Der obere Abschnitt zeigt die Commits an, während der untere Kommentare zu deren Squashing enthält.
Wir sehen vier Commits. Für jeden müssen wir entscheiden, welchen Befehl auszuführen. uns geht es um die Befehle pick
(p
) und squash
(s
). Um diese vier Commits in einen einzelnen Commit zu squaschen, können wir den ersten auswählen und die verbleibenden drei squaschen.
Wir setzen die Befehle um, indem wir den Text vor jedem Commit ändern, insbesondere pick
in s
oder squash
für die zweiten, dritten und vierten Commits ändern. Um diese Änderungen vorzunehmen, müssen wir in den „EINFÜGE“-Modus des Kommandozeilen-Texteditors wechseln, indem wir die i
-Taste auf der Tastatur drücken:
Nach dem Drücken von i
erscheint der Text -- INSERT --
unten, was anzeigt, dass wir den Einfügemodus betreten haben. Jetzt können wir den Cursor mit den Pfeiltasten bewegen, Zeichen löschen und wie in einem Standard-Texteditor tippen:
Sobald wir mit den Änderungen zufrieden sind, müssen wir den Einfügemodus durch Drücken der Esc
-Taste auf der Tastatur verlassen. Der nächste Schritt ist es, unsere Änderungen zu speichern und den Editor zu beenden. Dazu drücken wir zunächst die :
-Taste, um dem Editor zu signalisieren, dass wir beabsichtigen, einen Befehl auszuführen:
Unten im Editor sehen wir jetzt ein Semikolon :
, das uns auffordert, einen Befehl einzugeben. Um die Änderungen zu speichern, verwenden wir den Befehl w
, was für „write“ (schreiben) steht. Um den Editor zu schließen, verwenden wir q
, was für „quit“ (beenden) steht. Diese Befehle können kombiniert und gemeinsam eingegeben werden wq
:
Um den Befehl auszuführen, drücken wir die Enter
-Taste. Diese Aktion schließt den aktuellen Editor und öffnet einen neuen, allowing uns, die Commit-Nachricht für den neu zusammengefassten Commit einzugeben. Der Editor zeigt eine Standardnachricht an, die aus den Nachrichten der vier Commits besteht, die wir zusammenfassen:
Ich empfehle, die Nachricht so zu ändern, dass sie die durch diese kombinierten Commits vorgenommenen Änderungen genau widerspiegelt – schließlich dient das Squashing dazu, eine saubere und leicht lesbare Historie zu erhalten.
Um mit dem Editor zu interagieren und die Nachricht zu bearbeiten, drücken wir erneut i
, um den Bearbeitungsmodus zu öffnen und die Nachricht nach unserem Gusto zu bearbeiten.
In diesem Fall ersetzen wir die Commit-Nachricht mit „Implement login form.“ Um den Bearbeitungsmodus zu beenden, drücken wir Esc
. Dann speichern wir die Änderungen, indem wir :
drücken, den Befehl wq
eingeben und Enter
drücken.
Wie man die Commit-Historie ansieht
Im Allgemeinen kann es schwierig sein, die gesamte Commit-Historie wiederzuerinnern. Um die Commit-Historie anzuzeigen, können wir den Befehl git log
verwenden. In dem genannten Beispiel würde die Ausführung des git log
-Befehls vor der Durchführung des Squash die folgende Anzeige erzeugen:
Um durch die Liste der Commits zu navigieren, verwenden Sie die Pfeiltasten nach oben und unten. Um zu beenden, drücken Sie q
.
Wir können git log
verwenden, um den Erfolg des Squash zu bestätigen. Die Ausführung danach zeigt einen einzelnen Commit mit der neuen Nachricht an:
Gesquashten Commit pushen
Der obige Befehl wirkt auf das lokale Repository. Um das entfernte Repository zu aktualisieren, müssen wir unsere Änderungen pushen. Da wir jedoch die Commit-Historie geändert haben, müssen wir mit der Option --force
einen强制 Push durchführen:
git push --force origin feature/login-form
Das强制 Pushen überschreibt die Commit-Historie auf dem entfernten Branch und könnte andere, die an diesem Branch arbeiten, stören. Es ist eine gute Praxis, sich mit dem Team abzustimmen, bevor man das macht.
Eine sicherere Methode, um einen强制推送 durchzuführen, die das Risiko einer Störung der Zusammenarbeit reduziert, besteht darin, stattdessen die Option --force-with-lease
zu verwenden:
git push --force-with-lease origin feature/login-form
Diese Option stellt sicher, dass wir nur einen强制推送 durchführen, wenn der entfernte Zweig seit unserem letzten Abrufen oder Pull nicht aktualisiert wurde.
特定提交的合并
Stellen wir uns vor, wir haben fünf Commits:
Angenommen, wir möchten die Commits 1, 2 und 5 beibehalten und die Commits 3 und 4 zusammenfassen.
Bei der Verwendung einer interaktiven Rebase werden die für das Zusammenfassen markierten Commits mit dem unmittelbar vorangehenden Commit kombiniert. In diesem Fall bedeutet es, dass wir Commit4
zusammenfassen möchten, sodass es in Commit3
einfließt.
Um dies zu tun, müssen wir eine interaktive Rebase durchführen, die diese zwei Commits umfasst. In diesem Fall genügen drei Commits, daher verwenden wir den Befehl:
git rebase -i HEAD~3
Dann setzen wir Commit4
auf s
, damit es mit Commit3
zusammengefasst wird:
Nach der Ausführung dieses Befehls und der Auflistung der Commits stellen wir fest, dass die Commits 3 und 4 zusammengefasst wurden, während der Rest unverändert bleibt.
Zusammenfassen ab einem bestimmten Commit
In dem Befehl git rebase -i HEAD~3
ist der Teil HEAD
eine Abkürzung für den jüngsten Commit. Die Syntax ~3
wird verwendet, um einen Vorfahren eines Commits anzugeben. Zum Beispiel bezieht sich HEAD~1
auf den Elternteil des HEAD
-Commits.
Bei einer interaktiven Neubasisierung werden die betrachteten Commits alle Vorgängercommits bis zum im Befehl angegebenen Commit eingeschlossen. Beachten Sie, dass der angegebene Commit nicht enthalten ist:
Statt HEAD zu verwenden, können wir direkt eine Commit-Hash angeben. Zum Beispiel hat Commit2
den Hash dbf3cc118d6d7c08ef9c4a326b26dbb1e3fe9ddf
, daher lautet der Befehl:
git rebase -i dbf3cc118d6d7c08ef9c4a326b26dbb1e3fe9ddf
startet eine Neubasisierung, die alle nach Commit2
vorgenommenen Commits berücksichtigt. Wenn wir also eine Neubasisierung an einem bestimmten Commit starten und diesen Commit einschließen möchten, können wir den Befehl verwenden:
git rebase -i <commit-hash>~1
Konflikte lösen beim Zusammenfassen von Commits
Wenn wir Commits zusammenfassen, kombinieren wir mehrere Commit-Änderungen in einem einzelnen Commit, was zu Konflikten führen kann, wenn sich Änderungen überlappen oder stark abweichen. Hier sind einige häufige Szenarien, in denen Konflikte auftreten können:
- Überlappende Änderungen: Wenn zwei oder mehr zu sammengefasste Commits dieselben Zeilen einer Datei oder eng verwandte Zeilen geändert haben, kann Git diese Änderungen möglicherweise nicht automatisch bereinigen.
- Unterschiedlicher Änderungszustand: Wenn ein Commit einen bestimmten Codeabschnitt hinzufügt und ein anderer Commit diesen gleichen Codeabschnitt ändert oder löscht, kann das Zusammenfassen dieser Commits zu Konflikten führen, die gelöst werden müssen.
- Umbenennen und Ändern: Wenn ein Commit eine Datei umbenennt und nachfolgende Commits Änderungen am alten Namen vornehmen, kann das Zusammenfassen dieser Commits Git verwirren und zu einem Konflikt führen.
- Änderungen an Binärdateien: Binärdateien lassen sich schlecht mit textbasierten Diff-Tools zusammenführen. Wenn mehrere Commits dieselbe Binärdatei ändern und wir versuchen, sie zu squaschen, kann ein Konflikt auftreten, weil Git diese Änderungen nicht automatisch angleichen kann.
- Komplexer Verlauf: Wenn die Commits einen komplexen Verlauf mit mehreren Merge-Vorgängen, Ästen oder Rebases dazwischen haben, kann das Squashen zu Konflikten führen aufgrund der nicht-linearen Natur der Änderungen.
Beim Squashen versucht Git, jede Änderung nacheinander anzuwenden. Treten während des Prozesses Konflikte auf, pausiert es und lässt uns diese lösen.
Konflikte werden mit Konfliktmarkern <<<<<<
und >>>>>>
markiert. Um die Konflikte zu lösen, müssen wir die Dateien öffnen und jeden manuell durch Auswahl des zu behaltenden Codessegments lösen.
Nach der Konfliktlösung müssen wir die gelösten Dateien mit dem Befehl git add
in den Index aufnehmen. Dann können wir die Rebase mit folgendem Befehl fortsetzen:
git rebase --continue
Für mehr Informationen über Git-Konflikte, schau dir dieses Tutorial zu wie man Merge-Konflikte in Git löstan.
Alternativen zum Squashing mit Rebase
Der Befehl git merge --squash
ist eine alternative Methode zu git rebase -i
, um mehrere Commits in einen einzelnen Commit zusammenzufassen. Dieser Befehl ist besonders nützlich, wenn wir Änderungen von einem Zweig in den Hauptzweig integrieren möchten, während alle einzelnen Commits zusammengefasst werden. Hier ist eine Übersicht, wie man mit git merge
squasht:
- Wir navigieren zu dem Zielzweig, in den wir die Änderungen integrieren möchten.
- Wir führen den Befehl
git merge --squash <branch-name>
aus und ersetzen<branch-name>
mit dem Namen des Zweiges. - Wir committen die Änderungen mit
git commit
, um einen einzelnen Commit zu erstellen, der alle Änderungen vom Feature-Zweig darstellt.
Zum Beispiel, sagen wir, wir möchten die Änderungen der Zweigstelle feature/login-form
als einen einzigen Commit in main
integrieren:
git checkout main git merge --squash feature-branch git commit -m "Implement login form"
Diese sind die Einschränkungen dieses Ansatzes im Vergleich zu git rebase -i
:
- Feinheit der Kontrolle: Weniger Kontrolle über einzelne Commits. Mit Rebase können wir auswählen, welche Commits wir zusammenführen möchten, während Merge alle Änderungen in einen Commit kombinieren muss.
- Intermediate history: Bei der Verwendung von Merge geht die individuelle Commit-Historie aus dem Feature-Branch in Hauptzweig verloren. Dies kann es schwieriger machen, die inkrementellen Änderungen, die während der Entwicklung der Funktion vorgenommen wurden, zu verfolgen.
- Pre-commit review: Da es alle Änderungen als einen einzigen Änderungsatz bereitstellt, können wir nicht jeden Commit einzeln vor dem Squashing überprüfen oder testen, im Gegensatz zu einem interaktiven Rebase, bei dem jeder Commit nacheinander überprüft und getestet werden kann.
Conclusion
Die Eingliederung häufiger und kleiner Commits in den Entwicklungsworkflow fördert die Zusammenarbeit und klare Dokumentation, kann aber auch die Geschichte des Projekts durcheinanderbringen. Das Zusammenfassen von Commits schlägt eine Balance, indem es die wichtigen Meilensteine bewahrt und das Rauschen kleiner iterativer Änderungen eliminiert.
Um mehr über Git zu erfahren, empfehle ich folgende Ressourcen:
Source:
https://www.datacamp.com/tutorial/git-squash-commits