DanLevy.net

Deathmatch: Git Rebase vs. Merge

Eine zeitlose Frage...

Hero image for Deathmatch: Git Rebase vs. Merge

Deathmatch: Git Rebase vs. (Squash) Merge!

Sollte ich Rebase verwenden? Oder Squash Merge?

Warum löst dieses Thema religiöse Leidenschaft aus?

Einige Ingenieure nutzen ihr Wissen über Git (und das Terminal) als Signal für ihr eigenes Können. Und jede Praxis, die mit unserer Identität oder unserem Ego verknüpft ist, kann praktisch unmöglich unvoreingenommen analysiert werden – geschweige denn geändert werden.

Weitere Faktoren sind Vertrautheit und Survivorship Bias, die unsere eigene Einschätzung und unsere Annahmen zusätzlich vernebeln können.

Schlüsselfrage: Was ist der Zweck eines Git-Commits?

  1. Commitst du früh und oft? Mit einer „Checkpoint”- oder Backup-Denkweise?
    • Wo alles aufgezeichnet wird, sogar Fehlstarts und Experimente? (z. B. git commit -am "Updated deps" && git push, regelmäßig wiederholt)
    • Sind Commit-Messages für dich vielleicht weniger wichtig als der Code selbst?
  2. Oder sind deine Commits sorgfältig kuratierte, skulptierte Kunstwerke?
    • Vielleicht ist jeder Commit eine in sich geschlossene, atomare Arbeitseinheit? (z. B. git add package.json && git commit -m "Updated deps")
    • Oder du kannst einfach keine „unordentlichen” Commit-Logs ertragen?
    • Beinhalten deine PR-Reviews oft ein Commit-für-Commit-Review?

| 💡 Welche anderen mentalen Modelle definieren, wie du Commits siehst? Lass es mich wissen @justsml!

Denkst du über Git auf eine Weise nach, die den größten Wert für dich, dein Team und deine Organisation schafft?

Angesichts so unterschiedlicher Denkweisen rund um Commit-Strategien ist es kein Wunder, dass so viel Verwirrung um den „richtigen” Git-Einsatz herrscht.

Szenario: Einen überarbeiteten Release-Tag erstellen

Vergleichen wir den Prozess, einen Release-Tag zu erstellen, wobei einige jüngste Commits auf main ausgeschlossen werden.

Git Tag Release von main mit 2 Feature-Branches

Der Rebase-Weg

Mentales Modell: „Ich möchte eine alternative Version einer bestehenden Historie erstellen. (z. B. ich habe vor 16 Merges einen Fehler gemacht und brauche vielleicht feingranulare Kontrolle, um ihn zu korrigieren. Außerdem könnte ich in einem scheinbar endlosen Zyklus von Konflikten und --continue stecken bleiben.)”

  1. Neuesten Stand holen: git checkout main && git pull
  2. Neuen Branch erstellen: git checkout -b release/hot-newness-and-stuff
  3. Interaktiven Rebase starten und die Git-Ref angeben, zu der du zurückreisen möchtest. git rebase -i HEAD~6 (Hinweis: HEAD~6 ist eine „Git-Ref”-Abkürzung für „6 Commits zurück”)
  4. Gewünschte Commit(s) löschen, indem du ihr Prefix auf drop änderst. Speichern und Editor schließen.
  5. Merge-Konflikte beheben, git add . && git rebase --continue (NICHT git commit).
  6. Vorherigen Schritt wiederholen, bis alles abgeschlossen ist.
  7. Tag erstellen/pushen mit dem aktuellen Prozess. Beispiel git tag -a v1.2.3 -m 'Release v1.2.3' && git push --tags

Vorteile

Nachteile

Der (Squash) Merge-Weg

Mentales Modell: „Ich möchte ein individuelles Release, beginnend an einem bestimmten Punkt, und einschließlich aller gewünschten Branch(es).”

  1. Neuesten Stand holen: git checkout main && git pull
  2. Neuen Branch erstellen: git checkout -b release/hot-newness-and-stuff
  3. Gewünschte Branches und/oder Commits mergen: git merge --no-ff feature/hot-newness bug/fix-123 (verwende nach Möglichkeit das --no-ff-Flag.)
  4. Merge-Konflikt beheben (falls einer auftritt).
  5. Tag erstellen/pushen mit dem aktuellen Prozess. Beispiel git tag -a v1.2.3 -m 'Release v1.2.3' && git push --tags

Vorteile

Nachteile

Fazit

Letztendlich sollte ein einfacherer Prozess mit weniger Risiko gewinnen.

Auch wenn Rebaser durchaus Wege haben, ihre Probleme zu lösen (oder zu vermeiden), bleibt die Tatsache: Du brauchst irgendwann einen schwarzen Gurt in Git-Fu. (z. B. Sogar ein simpler git push kann zusätzliche Komplexität mit sich bringen: War es git push --force oder git push --force-with-lease? Warum sich damit überhaupt herumschlagen?)

Es gibt noch einen weiteren Grund, warum Rebasing, um eine überarbeitete Historie zu erstellen, immer im Nachteil gegenüber git merge ... sein wird. Ein git merge lässt die git-CLI fortgeschrittene Algorithmen anwenden, um Konflikte zu vermeiden, indem der HEAD jedes Branches analysiert wird.

Das kann klüger sein, weil jeder Merge sich nur auf den neuesten Stand jedes gewünschten Branches konzentriert. Wohingegen Rebasing die Commit-Historie in der angegebenen Reihenfolge neu abspielen (oder löschen) muss. Das schränkt Gits Fähigkeit zur Optimierung ein, da es immer nur 2 Commits auf einmal vergleicht.

Letztendlich bedeutet Rebasing, dass du gelegentlich irrelevante alte Commits und Konflikte nacherleben wirst – selbst wenn du weißt, dass sie seitdem entfernt oder gelöst wurden.

Zusammenfassung