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

  • 9d1a0ef und 4f3c2bb existieren nicht mehr — sie werden durch 2a9e1cc und d8b4f0a mit 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 squash vor 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 nicht git pull um 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 --continue vergessen: Der Rebase-Zustand bleibt “hängend”, git status wird dich deutlich sagen, dass du mitten im Rebase bist.
  • Continuous merge auf persönlichem Branch verwenden füllt das Log mit kleinen Merge-Commits und wird unlesbar — nutze statt mehrfachem Merge vom main in deinen Branch rebase.
  • Zu viel Squashing: Mehrere nicht verwandte Änderungen in einen Commit mergen macht git bisect und einzelne git revert nutzlos.

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.

gitrebasemergeversion-controlworkflow