Git Rebase oder Merge? Wähle die richtige Strategie
1. Überblick
git merge und git rebase werden beide verwendet, um Änderungen zwischen zwei Branches zu integrieren, aber sie erzeugen sehr unterschiedliche Commit-Historien. Die falsche Strategie zu wählen führt zu durcheinander Log, macht Review schwierig, oder noch schlimmer — es zerstört die Historie deiner Kollegen. Dieser Artikel hilft dir, die Mechanik jedes Befehls durch konkrete Beispiele zu verstehen, herauszufinden, wann du merge oder rebase nutzt, und häufige Fehler beim Rebase in Teams zu vermeiden.
2. Beispiel-Kontext
Angenommen, du arbeitest am Branch feature/payment, der von main abzweigt. Während du codierst, haben Kollegen bereits mehrere Commits in main gemergt.
git log --oneline --graph --all
* c3f9a1d (main) PaymentValidator hinzugefügt
* 7b2e4aa (main) Bug in OrderService behoben
| * 9d1a0ef (feature/payment) PaymentService.processPayment() hinzugefügt
| * 4f3c2bb (feature/payment) PaymentService initialisiert
|/
* a1b2c3d Gemeinsamer Base-Commit
Beide Branches haben sich von a1b2c3d abgezweigt. Jetzt möchtest du feature/payment auf den aktuellen Stand von main bringen, bevor du einen Pull Request öffnest. Es gibt zwei Möglichkeiten: merge oder rebase.
3. Git Merge: Behalte die Historie, erstelle einen Merge-Commit
3.1. Verwendung
git checkout feature/payment
git merge main
Dieser Befehl erstellt einen neuen Merge-Commit, der zwei Parent-Commits hat: den letzten Commit von feature/payment und den letzten Commit von main.
* e5f6a7b (HEAD -> feature/payment) Merge branch 'main' into feature/payment
|\
| * c3f9a1d (main) PaymentValidator hinzugefügt
| * 7b2e4aa (main) Bug in OrderService behoben
* | 9d1a0ef PaymentService.processPayment() hinzugefügt
* | 4f3c2bb PaymentService initialisiert
|/
* a1b2c3d Gemeinsamer Base-Commit
3.2. Merkmale
- Die ursprüngliche Historie beider Branches wird zu 100% bewahrt, kein Commit wird modifiziert.
- Es wird ein zusätzlicher Merge-Commit erstellt, der den Zeitpunkt aufzeichnet, an dem die beiden Branches zusammentreffen.
- Völlig sicher mit bereits gepushten Branches — niemand wird negativ beeinflusst.
Hinweis:
git mergeüberschreibt nicht die SHA irgendeines bereits existierenden Commits. Deshalb ist es immer sicher mit Shared Branches.
4. Git Rebase: Schreibe die Historie um, erstelle einen linearen Branch
4.1. Verwendung
git checkout feature/payment
git rebase main
Rebase nimmt jeden Commit auf feature/payment von der Abzweigungsstelle (a1b2c3d) und “spielt” (replay) sie auf dem Top von main ab. Das Ergebnis sind völlig neue Commits mit unterschiedlichen SHAs.
* 2a9e1cc (HEAD -> feature/payment) PaymentService.processPayment() hinzugefügt
* d8b4f0a PaymentService initialisiert
* c3f9a1d (main) PaymentValidator hinzugefügt
* 7b2e4aa (main) Bug in OrderService behoben
* a1b2c3d Gemeinsamer Base-Commit
Die Historie ist jetzt eine gerade Linie (linear history), ohne einen einzigen Merge-Commit.
4.2. Merkmale
9d1a0efund4f3c2bbexistieren nicht mehr — sie werden durch2a9e1ccundd8b4f0amit neuen SHAs ersetzt.- Das Log ist sauber, lesbar und gut für
git bisect. - Gefährlich, wenn der Branch von anderen gepullt wurde, denn sie haben die alten Commits noch lokal, während die neuen auf dem Remote sind.
5. Konflikte beheben: Der größte Unterschied bei der Durchführung
Mit merge müssen Konflikte (falls vorhanden) nur einmal beim Merge-Commit gelöst werden:
git merge main
# CONFLICT (content): Merge conflict in PaymentService.java
# ... Datei editieren, lösen ...
git add PaymentService.java
git commit
Mit rebase, wenn mehrere Commits vorhanden sind, können Konflikte bei jedem Commit während der Replay auftreten:
git rebase main
# CONFLICT in Commit 4f3c2bb
# Datei editieren, gelöst:
git add PaymentService.java
git rebase --continue
# wenn der nächste Commit auch Konflikt hat, wiederhole es
Wenn du stoppen und zum Zustand vor dem Rebase zurückkehren möchtest:
git rebase --abort
Hinweis: Einen Branch mit 10 Commits zu rebasen kann bedeuten, dass du den Konflikt 10 Mal auflösen musst — ein Grund, warum
squashvor dem Rebase (siehe Abschnitt 7) sehr nützlich ist.
6. Die Goldene Regel: Rebase nie einen Shared Branch
Dies ist die wichtigste Regel beim Gebrauch von Rebase.
# Branch main wurde bereits von 5 anderen Personen gepullt
git checkout main
git rebase feature/old-stuff # ❌ ÄUSSERST GEFÄHRLICH
git push --force origin main
Da Rebase neue SHAs für alle Commits erzeugt, werden alle, die main gepullt haben, nach push --force eine divergierte Historie gegenüber dem Remote haben. Ihr nächster Pull wird Fehler verursachen oder unzählige doppelte Commits erstellen.
error: failed to push some refs to 'origin'
hint: Updates were rejected because the tip of your current branch is behind
Wenn du nach einem Rebase gezwungen bist zu force-pushen, nutze --force-with-lease statt --force, um nicht die Commits anderer zu überschreiben:
git push --force-with-lease origin feature/payment
--force-with-lease prüft, ob der Remote keine zusätzlichen Commits von anderen erhalten hat seit deinem letzten Fetch. Wenn ja, schlägt der Befehl fehl, statt blind zu überschreiben.
Die Goldene Regel: Rebase nur persönliche Branches, die nicht geteilt werden oder nur von dir bearbeitet werden. Rebase niemals main, develop oder irgendwelche Shared Branches.
7. Interactive Rebase: Bereinige die Historie vor dem Merge
Eine sehr praktische Anwendung von Rebase ist das Aufräumen von Fragment-Commits auf einem persönlichen Branch vor dem Erstellen eines Pull Requests.
git checkout feature/payment
git rebase -i HEAD~3
Ein Editor öffnet sich:
pick 4f3c2bb PaymentService initialisiert
pick 9d1a0ef PaymentService.processPayment() hinzugefügt
pick 1c8f3aa Tippfehler behoben
Ändere die letzten zwei Zeilen zu squash, um sie in den ersten Commit zu mergen:
pick 4f3c2bb PaymentService initialisiert
squash 9d1a0ef PaymentService.processPayment() hinzugefügt
squash 1c8f3aa Tippfehler behoben
Speichern, Git wird dich nach einer neuen Commit-Nachricht fragen. Ergebnis: 3 Fragment-Commits werden zu 1 sauberen, aussagekräftigen Commit — sehr sinnvoll vor dem Öffnen einer PR, damit der Reviewer nicht “Tippfehler behoben”, “wip”, “Ah, Import vergessen” Commits lesen muss.
8. Praktische Strategie: Kombiniere beide
Die meisten effizienten Teams wählen nicht absolut zwischen den beiden, sondern kombinieren sie je nach Kontext:
| Situation | Nutze |
|---|---|
Aktualisiere persönlichen Branch mit neuem main |
rebase |
| Räume Fragment-Commits vor der PR-Eröffnung auf | rebase -i (squash) |
Merge des überprüften Feature Branches in main |
merge (normalerweise über PR, kann squash-merge sein) |
| Branch wird von vielen Personen bearbeitet/gepullt | merge, niemals rebase |
| Benötige eine Audit-Spur des Integrationszeitpunkts | merge |
Typisches Vorgehen in der Praxis:
# 1. Vor der PR-Eröffnung: Rebase auf main um aktualisiert zu sein, verhindere späte Konflikte
git checkout feature/payment
git rebase main
# 2. Räume Commits bei Bedarf auf
git rebase -i main
# 3. Pushe persönlichen Branch (sicher weil nur du ihn verwendest)
git push --force-with-lease origin feature/payment
# 4. Merge in main über PR (Merge-Commit oder squash-merge, nicht rebase main)
Dies hält das Log des feature/payment sauber, linear und einfach zu reviewen in der PR, während main sicher bleibt, da es nur Merge-Commits (oder Squash-Commits) aus genehmigten PRs erhält.
9. Squash Merge: Ein Mittelweg
Viele Plattformen (GitHub, GitLab) haben einen “Squash and merge”-Button — er merged alle Commits der PR zu einem einzigen Commit und merged ihn in main.
git checkout main
git merge --squash feature/payment
git commit -m "Zahlungsverarbeitung-Funktion hinzugefügt (#42)"
Ergebnis: main hat genau einen neuen Commit, keinen Merge-Commit, die Historie des feature/payment (auf dem Remote) bleibt erhalten, wenn du den Branch nicht löschst. Dies ist die beliebteste Wahl für Teams mit PR-Workflow, weil es das Log auf main sauber hält und gleichzeitig die ursprüngliche Historie nicht antastet.
10. Häufige Fehler
- Rebase dann normalerweise pushen (nicht
--force): Git meldet einen Fehler, weil die Historie divergiert. Nutze nichtgit pullum das “zu beheben” — es wird einen unerwünschten Merge-Commit erstellen und deine lineare Historie ruinieren. - Vergessen, dass der Rebase-Branch Shared ist: Prüfe immer
git log origin/<branch>, ob andere bereits auf den alten Commits aufbauen. - Nach Konfliktauflösung
--continuevergessen: Der Rebase-Zustand bleibt “hängend”,git statuswird dich deutlich sagen, dass du mitten im Rebase bist. - Continuous
mergeauf persönlichem Branch verwenden füllt das Log mit kleinen Merge-Commits und wird unlesbar — nutze statt mehrfachem Merge vommainin deinen Branch rebase. - Zu viel Squashing: Mehrere nicht verwandte Änderungen in einen Commit mergen macht
git bisectund einzelnegit revertnutzlos.
11. Fazit
merge ist sicher, bewahrt die echte Historie und passt zu Shared Branches. rebase erzeugt saubere, lineare Historien, überschreibt aber SHAs, daher nur auf persönlichen, nicht geteilten Branches. In der Praxis musst du nicht absolut für eine Seite wählen: Rebase um deinen persönlichen Branch zu aktualisieren und zu bereinigen, merge (oder squash-merge) um über PR in main zu integrieren. Merke dir die Goldene Regel — rebase niemals einen Branch, auf dem andere aufbauen — und du wirst die meisten Git-Probleme im Team vermeiden.