Zwei Wege, Änderungen zu integrieren
Wenn Sie mit Git-Branches arbeiten, kommt der Punkt, an dem Sie Änderungen von einer Branch in eine andere übernehmen müssen. Git bietet Ihnen dafür zwei Hauptstrategien: merge und rebase. Beide erreichen das gleiche Endergebnis -- Ihre Branch enthält alle neuesten Änderungen -- aber sie tun dies auf grundlegend unterschiedliche Weise, und die richtige Wahl ist wichtig für die Historie Ihres Projekts und den Workflow Ihres Teams.
Wie Merge funktioniert
Ein merge nimmt zwei Branches und kombiniert sie, indem ein neuer merge commit erstellt wird. Dieser merge commit hat zwei Eltern: die Spitze Ihrer aktuellen Branch und die Spitze der Branch, die Sie integrieren.
git checkout feature/login
git merge main
Danach enthält Ihre Feature-Branch alle Änderungen von main, verbunden durch einen merge commit. Die Historie beider Branches bleibt exakt erhalten, wie sie passiert ist. Wenn Sie den Commit-Graphen betrachten, sehen Sie die zwei Entwicklungslinien am Merge-Punkt zusammenlaufen.
Die wesentlichen Eigenschaften von merge:
- Nicht-destruktiv. Keine bestehenden Commits werden verändert. Die Historie zeigt genau, was passiert ist.
- Erstellt einen merge commit. Dieser zusätzliche Commit verbindet die beiden Historien miteinander.
- Bewahrt den Kontext. Sie können immer sehen, wann eine Branch erstellt und wann sie zurückgeführt wurde.
Fast-forward merge
Wenn Ihre Branch nicht von der Zielbranch abgewichen ist (es gibt keine neuen Commits auf dem Ziel seit Sie die Branch erstellt haben), führt Git standardmäßig einen fast-forward merge durch. Es verschiebt einfach den Branch-Zeiger nach vorne. Es wird kein merge commit erstellt, da keiner nötig ist. Sie können auch in diesem Fall einen merge commit erzwingen mit:
git merge --no-ff feature/login
Dies ist nützlich, wenn Sie die Tatsache bewahren möchten, dass Arbeit auf einer separaten Branch stattfand, auch wenn der merge trivial war.
Wie Rebase funktioniert
Rebase verfolgt einen anderen Ansatz. Anstatt einen merge commit zu erstellen, spielt es Ihre Commits auf einer anderen Branch neu ab. Das Ergebnis ist eine lineare Historie, die aussieht, als hätten Sie Ihre Arbeit vom neuesten Punkt der Zielbranch aus begonnen.
git checkout feature/login
git rebase main
Was hinter den Kulissen passiert:
- Git findet den gemeinsamen Vorfahren der beiden Branches.
- Es entfernt vorübergehend die Commits Ihrer Branch.
- Es verschiebt Ihren Branch-Zeiger an die Spitze von main.
- Es spielt Ihre Commits einzeln darauf ab.
Die resultierende Historie ist perfekt linear. Kein merge commit, keine sich verzweigenden und zusammenlaufenden Linien im Graphen. Es sieht aus, als hätten Sie Ihren gesamten Code nach den neuesten Änderungen auf main geschrieben, auch wenn das nicht der Realität entspricht.
Die wesentlichen Eigenschaften von rebase:
- Erstellt eine lineare Historie. Der Commit-Graph ist sauber und leicht zu lesen.
- Schreibt Commit-Hashes um. Die neu abgespielten Commits erhalten neue SHA-Hashes, obwohl ihr Inhalt gleich ist.
- Kein merge commit. Die Branch wird sauber ohne zusätzliche Commits integriert.
Wann Merge verwenden
Merge ist in mehreren Situationen die sicherere und unkompliziertere Wahl:
- Gemeinsam genutzte Branches. Wenn mehrere Personen an derselben Branch arbeiten, bewahrt merge die Historie aller, ohne Commits umzuschreiben, von denen andere abhängen.
- Historienbewahrung ist wichtig. In Projekten, in denen Sie genau nachverfolgen müssen, wann und wie Änderungen integriert wurden, bietet merge eine vollständige, unveränderte Aufzeichnung.
- Integration langlebiger Branches. Beim Merge einer Release-Branch zurück in main oder beim Zusammenführen zweier Branches, die stark divergiert sind, markiert ein merge commit deutlich den Integrationspunkt.
- Sie wollen Einfachheit. Merge ist konzeptuell einfacher. Es gibt weniger Dinge, die schiefgehen können, besonders für Entwickler, die neu bei Git sind.
Wann Rebase verwenden
Rebase glänzt, wenn Sie eine saubere, lineare Projekthistorie wollen:
- Feature-Branch aktualisieren. Bevor Sie Ihre Funktion in main mergen, gibt Ihnen ein rebase auf den neuesten main einen sauberen Satz von Commits, die sich direkt ohne merge commit anwenden lassen.
- Saubere Historie beibehalten. Eine lineare Historie ist einfacher mit
git logzu durchsuchen, einfacher bei der Fehlersuche mit bisect, und einfacher bei Pull Requests zu überprüfen. - Lokales Aufräumen. Wenn Sie beim Arbeiten kleine inkrementelle Commits gemacht haben, können Sie rebase nutzen, um aufzuräumen, bevor Sie Ihre Branch teilen.
Die goldene Regel des Rebase
Führen Sie niemals ein rebase von Commits durch, die auf eine gemeinsam genutzte Branch gepusht wurden.
Dies ist die wichtigste Regel. Da rebase Commit-Hashes umschreibt, wird jeder, der seine Arbeit auf den ursprünglichen Commits aufgebaut hat, Probleme bekommen. Deren Historie stimmt nicht mehr mit der rebasten Historie überein, was zu duplizierten Commits, Konflikten und Verwirrung führt.
Die Regel ist einfach: Wenn andere Personen Ihre Commits möglicherweise gepullt haben, machen Sie kein rebase. Verwenden Sie rebase nur auf Ihren eigenen lokalen Branches oder auf Branches, bei denen Sie der einzige Contributor sind.
Wenn Sie versehentlich ein rebase einer gemeinsam genutzten Branch durchführen, müssen die anderen Entwickler im Team mit der divergierten Historie umgehen. Das bedeutet typischerweise einen force-push (der das Remote überschreibt) und dass alle anderen ihre lokalen Kopien zurücksetzen müssen. Das ist störend und vermeidbar.
Interaktiver Rebase: Historie mit Präzision umschreiben
Interaktiver rebase (git rebase -i) geht über das einfache Verschieben von Commits hinaus. Er ermöglicht es, die Commit-Historie selbst zu bearbeiten:
git rebase -i HEAD~5
Dies öffnet eine Liste der letzten 5 Commits, in der Sie:
- pick -- den Commit unverändert behalten
- reword -- die Commit-Nachricht ändern
- edit -- pausieren, um den Commit zu bearbeiten
- squash -- mit dem vorherigen Commit zusammenführen
- fixup -- wie squash, aber die Commit-Nachricht verwerfen
- drop -- den Commit komplett entfernen
- reorder -- die Reihenfolge durch Umordnen der Zeilen ändern
Interaktiver rebase ist leistungsstark zum Aufräumen einer unordentlichen Branch vor dem merge. Sie können "Tippfehler korrigiert"-Commits in ihren Eltern-Commit squashen, unklare Nachrichten umformulieren und Commits so umordnen, dass sie eine logische Geschichte erzählen.
Ein praktischer Workflow
Viele Teams verwenden einen kombinierten Ansatz, der das Beste beider Strategien nutzt:
- Feature-Branch von main erstellen.
- Während der Arbeit die Feature-Branch regelmäßig auf main rebasen, um aktuell zu bleiben.
- Vor dem Öffnen einer Pull Request den interaktiven rebase nutzen, um die Commits aufzuräumen.
- Die Feature-Branch in main mergen (oft mit einem merge commit, um den Integrationspunkt zu markieren).
Dies gibt Ihnen eine saubere, lineare Historie auf der Feature-Branch und einen klaren Merge-Punkt auf main. Es ist ein pragmatischer Mittelweg, der für die meisten Teams gut funktioniert.
Rebase und Merge in GitSquid
GitSquid unterstützt sowohl merge als auch rebase direkt über die Benutzeroberfläche. Sie können Branches über das Kontextmenü an jedem Branch-Label im Commit-Graphen mergen. Für rebase bietet GitSquid einen visuellen interaktiven Rebase-Editor, mit dem Sie Commits per Drag-and-Drop und Klicken umordnen, squashen, fixupen, reworden und droppen können, anstatt eine Textdatei in Ihrem Terminal zu bearbeiten. Das macht den interaktiven rebase zugänglich, auch wenn Sie die Kommandozeilenversion einschüchternd finden.
Zusammenfassung
| Merge | Rebase | |
|---|---|---|
| Historie | Nicht-linear, bewahrt Branches | Linear, sauber |
| Commit-Hashes | Unverändert | Umgeschrieben |
| Merge commit | Ja (außer fast-forward) | Nein |
| Sicher für gemeinsame Branches | Ja | Nein |
| Am besten für | Integration gemeinsamer Arbeit | Aufräumen von Feature-Branches |
Weder merge noch rebase ist universell besser. Es sind Werkzeuge für verschiedene Situationen. Verstehen Sie, was jedes mit Ihrer Historie macht, befolgen Sie die goldene Regel des rebase, und wählen Sie das passende für den jeweiligen Kontext. Ihr zukünftiges Ich beim Lesen von git log wird es Ihnen danken.