Merge vs. Rebase – Der fundamentale Unterschied
Der Unterschied zwischen git merge und git rebase ist eines der wichtigsten Konzepte, das du als fortgeschrittener Git-Nutzer wirklich verstanden haben solltest. Beide Befehle dienen demselben Zweck – Änderungen aus einem Branch in einen anderen zu integrieren – aber sie tun dies auf völlig unterschiedliche Weise, mit unterschiedlichen Auswirkungen auf deine Commit-Historie.
Die Ausgangssituation
Stellen wir uns folgendes Szenario vor: Du arbeitest an einem Feature-Branch, während dein Team weiterhin Commits auf main macht. Nach einiger Zeit sieht deine Repository-Struktur so aus:
gitGraph
commit id: "A"
commit id: "B"
branch feature
commit id: "X"
commit id: "Y"
checkout main
commit id: "C"
commit id: "D"
Du hast auf deinem feature-Branch die Commits X und Y erstellt, während auf main in der Zwischenzeit C und D hinzugekommen sind. Jetzt möchtest du die Änderungen von main in deinen Feature-Branch integrieren (oder umgekehrt deinen Feature-Branch in main einbringen). Hier kommen Merge und Rebase ins Spiel.
Was passiert bei einem Merge?
Ein Merge erstellt einen neuen Merge-Commit, der die beiden Entwicklungslinien zusammenführt. Dieser Merge-Commit hat zwei Eltern-Commits – er verbindet buchstäblich die beiden Branches miteinander.
Der Merge-Befehl
# Du bist auf feature und möchtest main integrieren
git checkout feature
git merge main
In PhpStorm: Git → Merge... → main auswählen
Das Ergebnis nach dem Merge
gitGraph
commit id: "A"
commit id: "B"
branch feature
commit id: "X"
commit id: "Y"
checkout main
commit id: "C"
commit id: "D"
checkout feature
merge main id: "M" tag: "Merge-Commit"
Der neue Commit M ist der Merge-Commit. Er enthält keine eigenen Code-Änderungen (außer eventuell Konfliktlösungen), sondern dient als „Knotenpunkt", der die beiden Historien verbindet.
Eigenschaften eines Merge
| Aspekt | Beschreibung |
|---|---|
| Commit-Historie | Bleibt vollständig erhalten, zeigt aber eine verzweigte Struktur |
| Originalcommits | X und Y behalten ihre ursprünglichen SHA-Hashes |
| Neuer Commit | Ein Merge-Commit mit zwei Eltern wird erstellt |
| Nicht-destruktiv | Die bestehende Historie wird niemals verändert |
Was passiert bei einem Rebase?
Ein Rebase nimmt deine Commits und „verpflanzt" sie auf eine neue Basis. Statt eines Merge-Commits werden deine Commits neu erstellt – sie werden auf den aktuellen Stand des Ziel-Branches aufgebaut, als hättest du erst jetzt mit der Arbeit begonnen.
Der Rebase-Befehl
# Du bist auf feature und möchtest auf main "umbasieren"
git checkout feature
git rebase main
In PhpStorm: Git → Rebase... → main auswählen
Was genau passiert beim Rebase?
Der Rebase-Prozess läuft in mehreren Schritten ab:
- Git identifiziert die Commits, die nur auf
featureexistieren (X und Y) - Git speichert diese Commits temporär (als Patches)
- Git setzt den
feature-Branch auf die Spitze vonmain(Commit D) - Git wendet die gespeicherten Commits nacheinander wieder an
Das Ergebnis sind neue Commits X' und Y', die denselben Inhalt haben wie die Originale, aber:
- einen anderen Eltern-Commit (sie bauen jetzt auf D auf, nicht auf B)
- einen anderen SHA-Hash (weil sich der Eltern-Commit geändert hat)
- einen anderen Zeitstempel (optional, je nach Konfiguration)
Das Ergebnis nach dem Rebase
gitGraph
commit id: "A"
commit id: "B"
commit id: "C"
commit id: "D"
commit id: "X'" tag: "neu erstellt"
commit id: "Y'" tag: "neu erstellt"
Beachte: Die ursprünglichen Commits X und Y existieren technisch noch im Repository (erreichbar über git reflog), aber kein Branch zeigt mehr auf sie. Sie werden bei der nächsten Garbage Collection entfernt.
Visueller Vergleich: Merge vs. Rebase
Um den Unterschied noch deutlicher zu machen, hier beide Ergebnisse im direkten Vergleich:
flowchart TB
subgraph vorher["Ausgangssituation"]
direction LR
A1["A"] --> B1["B"]
B1 --> C1["C"] --> D1["D"]
B1 --> X1["X"] --> Y1["Y"]
end
subgraph merge["Nach git merge main"]
direction LR
A2["A"] --> B2["B"]
B2 --> C2["C"] --> D2["D"]
B2 --> X2["X"] --> Y2["Y"]
D2 --> M["M"]
Y2 --> M
end
subgraph rebase["Nach git rebase main"]
direction LR
A3["A"] --> B3["B"] --> C3["C"] --> D3["D"]
D3 --> X3["X'"] --> Y3["Y'"]
end
vorher --> merge
vorher --> rebase
style M fill:#90EE90,color:#000000
style X3 fill:#FFD700,color:#000000
style Y3 fill:#FFD700,color:#000000
Die tiefgreifenden Unterschiede im Detail
1. Integrität der Historie vs. Sauberkeit
Merge bewahrt die exakte historische Wahrheit: Es zeigt, dass parallel entwickelt wurde und wann die Zusammenführung stattfand. Das kann wertvoll sein, wenn du später nachvollziehen möchtest, wie die Entwicklung tatsächlich ablief.
Rebase erzeugt eine saubere, lineare Historie, als hätte es nie parallele Entwicklung gegeben. Das macht die Historie leichter lesbar, „lügt" aber technisch gesehen über den tatsächlichen Entwicklungsverlauf.
2. Commit-Identität
Dieser Punkt ist entscheidend für das Verständnis:
# Vor dem Rebase
git log --oneline feature
# abc1234 Y - Feature fertiggestellt
# def5678 X - Feature begonnen
# Nach dem Rebase
git log --oneline feature
# 111aaaa Y' - Feature fertiggestellt ← NEUER HASH!
# 222bbbb X' - Feature begonnen ← NEUER HASH!
Die Commits nach dem Rebase sind komplett neue Git-Objekte. Sie haben:
- denselben Autor
- dieselbe Commit-Message
- dieselben Code-Änderungen (Diffs)
- aber einen anderen SHA-Hash (weil der Parent-Commit anders ist)
3. Konfliktbehandlung
Bei einem Merge musst du Konflikte einmal lösen – im Merge-Commit. Die Konfliktlösung wird Teil dieses einen Commits.
Bei einem Rebase musst du Konflikte für jeden Commit einzeln lösen, während Git die Commits nacheinander auf die neue Basis anwendet. Das kann aufwändiger sein, führt aber dazu, dass jeder Commit für sich genommen „funktioniert".
# Rebase mit Konflikten
git rebase main
# CONFLICT in datei.php
# Konflikt lösen, dann:
git add datei.php
git rebase --continue
# Möglicherweise nächster Konflikt im nächsten Commit...
4. Auswirkungen auf andere Entwickler
Hier liegt der kritischste Unterschied:
| Szenario | Merge | Rebase |
|---|---|---|
| Commits noch nicht gepusht | ✅ Sicher | ✅ Sicher |
| Commits bereits gepusht | ✅ Sicher | ⚠️ Gefährlich! |
| Andere arbeiten auf dem Branch | ✅ Sicher | ❌ Sehr problematisch! |
Wenn du Commits rebasst, die bereits gepusht wurden, änderst du deren SHA-Hashes. Wenn ein Kollege diese Commits bereits hat, entstehen Duplikate in der Historie, weil Git die alten und neuen Commits als unterschiedlich betrachtet.
Wann sollte ich Merge verwenden?
Merge ist die richtige Wahl, wenn:
-
Du öffentliche Historie integrierst – Wenn du Änderungen aus
mainin deinen Feature-Branch holst und dieser Branch bereits gepusht wurde, ist Merge sicherer. -
Mehrere Entwickler am gleichen Branch arbeiten – Ein Merge verändert keine bestehenden Commits, sodass niemand Probleme bekommt.
-
Du die komplette Entwicklungshistorie bewahren möchtest – Für Auditing oder Nachvollziehbarkeit kann die „echte" Historie wichtig sein.
-
Du größere Feature-Branches in
mainintegrierst – Viele Teams bevorzugen hier einen Merge-Commit als „Markierung", dass ein Feature abgeschlossen wurde. -
Du unsicher bist – Merge ist die sicherere Option. Im Zweifel: Merge verwenden.
Beispiel: Feature in main mergen
git checkout main
git merge feature --no-ff # --no-ff erzwingt einen Merge-Commit
Das --no-ff (no fast-forward) Flag ist wichtig, wenn du explizit einen Merge-Commit möchtest, auch wenn ein Fast-Forward möglich wäre.
Wann sollte ich Rebase verwenden?
Rebase ist die richtige Wahl, wenn:
-
Du deine lokale Arbeit aufräumen möchtest, bevor du sie teilst – Vor dem ersten Push ist Rebase völlig sicher und macht deine Commits präsentabler.
-
Du deinen Feature-Branch auf den neuesten Stand von
mainbringen möchtest (und der Branch noch nicht gepusht wurde oder nur du daran arbeitest). -
Du eine lineare, saubere Historie bevorzugst – Manche Teams haben die Konvention, dass Feature-Branches vor dem Merge rebased werden müssen.
-
Du mit Interactive Rebase Commits zusammenfassen oder aufteilen möchtest – Das ist nur mit Rebase möglich.
Beispiel: Feature-Branch aktualisieren
git checkout feature
git fetch origin
git rebase origin/main
# Jetzt ist dein Feature-Branch auf dem neuesten Stand
In PhpStorm kannst du das über Git → Rebase... machen oder über das Git-Log-Fenster (Rechtsklick auf origin/main → „Rebase Current onto Selected").
Der kombinierte Workflow: Das Beste aus beiden Welten
Viele professionelle Teams nutzen einen kombinierten Workflow, der die Vorteile beider Ansätze vereint:
flowchart TD
A["Feature-Branch erstellen"] --> B["Lokal entwickeln\nmit kleinen Commits"]
B --> C{"Bereit zum Teilen?"}
C -->|Nein| B
C -->|Ja| D["Interactive Rebase:\nCommits aufräumen"]
D --> E["Rebase auf aktuellen main"]
E --> F["Push Feature-Branch"]
F --> G["Pull Request erstellen"]
G --> H["Code Review"]
H --> I{"Änderungen nötig?"}
I -->|Ja| J["Änderungen committen"]
J --> H
I -->|Nein| K["Merge oder Squash-Merge\nin main"]
style D fill:#FFD700,color:#000000
style E fill:#FFD700,color:#000000
style K fill:#90EE90,color:#000000
Die Schritte im Detail
-
Während der Entwicklung: Mache so viele kleine Commits wie nötig, ohne dir über die „Schönheit" der Historie Gedanken zu machen.
-
Vor dem Push: Nutze Interactive Rebase (
git rebase -i), um deine Commits aufzuräumen – Work-in-Progress-Commits zusammenfassen, Commit-Messages verbessern. -
Vor dem Pull Request: Rebase auf den aktuellen
main, um sicherzustellen, dass dein Branch sauber auf dem neuesten Stand aufbaut. -
Beim Mergen: Der Pull Request wird entweder mit einem Merge-Commit oder einem Squash-Merge in
mainintegriert.
Die „goldene Regel" des Rebasings
🚨 Rebase niemals Commits, die bereits gepusht wurden und an denen andere arbeiten könnten!
Diese Regel ist so wichtig, dass sie einen eigenen Abschnitt verdient. Hier ist, was passiert, wenn du sie brichst:
Das Katastrophen-Szenario
sequenceDiagram
participant Du
participant GitHub
participant Kollege
Du->>GitHub: Push commits X, Y
GitHub->>Kollege: Pull commits X, Y
Note over Kollege: Kollege arbeitet<br/>basierend auf Y
Du->>Du: Rebase X, Y → X', Y'
Du->>GitHub: Force Push X', Y'
Note over GitHub: X, Y ersetzt durch X', Y'
Kollege->>GitHub: Push neuer Commit Z
Note over GitHub: KONFLIKT!<br/>Z basiert auf Y,<br/>aber Y existiert nicht mehr
GitHub-->>Kollege: Rejected!
Note over Kollege: Muss jetzt manuell<br/>reparieren 😱
Dein Kollege hat jetzt ein ernsthaftes Problem: Seine Arbeit basiert auf Commits, die in der offiziellen Historie nicht mehr existieren.
Was tun, wenn es doch passiert ist?
Falls du versehentlich gepushte Commits rebased hast:
-
Kommuniziere sofort mit deinem Team – Informiere alle, die betroffen sein könnten.
-
Koordiniere die Reparatur – Alle müssen ihre lokalen Branches aktualisieren:
git fetch origin git reset --hard origin/feature # ACHTUNG: Lokale Änderungen gehen verloren! -
Lerne daraus – Richte Protected Branches ein, die Force-Push verbieten.
Merge und Rebase in PhpStorm
Merge durchführen
- Stelle sicher, dass du auf dem Ziel-Branch bist (z.B.
feature) - Gehe zu Git → Merge...
- Wähle den Branch, den du einmergen möchtest (z.B.
main) - Klicke auf Merge
- Bei Konflikten öffnet sich der Merge-Dialog
Rebase durchführen
- Stelle sicher, dass du auf dem Branch bist, den du rebasen möchtest (z.B.
feature) - Gehe zu Git → Rebase...
- Wähle den Branch, auf den du rebasen möchtest (z.B.
main) - Klicke auf Rebase
- Bei Konflikten:
- Löse jeden Konflikt im Editor
- Klicke auf Continue Rebase in der Notification
Tipp: Rebase über das Git-Log-Fenster
Eine besonders intuitive Methode in PhpStorm:
- Öffne das Git-Tool-Window (Alt+9 / Cmd+9)
- Sieh dir das Commit-Log an
- Rechtsklicke auf den Commit, auf den du rebasen möchtest
- Wähle Rebase Current onto Selected
Praktisches Beispiel: Ein Tag im Leben eines Entwicklers
Lass mich einen typischen Workflow durchspielen:
Morgens: Feature-Branch erstellen
git checkout main
git pull
git checkout -b feature/user-profile
Während des Tages: Entwickeln mit vielen kleinen Commits
git commit -m "WIP: Grundstruktur angelegt"
git commit -m "WIP: Formular hinzugefügt"
git commit -m "Fix Typo"
git commit -m "WIP: Validierung"
git commit -m "Fertig mit Validierung"
Vor dem Feierabend: Aufräumen mit Interactive Rebase
git rebase -i HEAD~5
# Im Editor: squash die WIP-Commits zusammen
# Ergebnis: 2 saubere Commits statt 5 chaotische
Nächster Morgen: Auf aktuellen main rebasen
git fetch origin
git rebase origin/main
# Konflikte lösen, falls nötig
Push und Pull Request
git push -u origin feature/user-profile
# Pull Request auf GitHub erstellen
Nach dem Review: Merge in main
Auf GitHub: Squash and Merge oder Merge Commit – je nach Team-Konvention.
Zusammenfassung: Merge vs. Rebase auf einen Blick
| Kriterium | Merge | Rebase |
|---|---|---|
| Historie | Verzweigt, zeigt parallele Entwicklung | Linear, als wäre sequentiell entwickelt worden |
| Neue Commits | Ein Merge-Commit | Keine neuen Commits (außer neu erstellten) |
| Original-Commits | Bleiben unverändert | Werden neu erstellt (neue SHA-Hashes) |
| Sicherheit | Immer sicher | Nur sicher für nicht-gepushte Commits |
| Konfliktlösung | Einmal im Merge-Commit | Für jeden Commit einzeln |
| Lesbarkeit | Kann unübersichtlich werden | Sehr übersichtlich |
| Empfohlen für | Öffentliche Branches, Team-Arbeit | Lokale Arbeit, Branch-Aktualisierung |
Meine Empfehlung: Starte mit Merge als Standard – es ist sicherer und verzeihender. Nutze Rebase gezielt für lokale Aufräumarbeiten und Branch-Aktualisierungen, bevor du pushst. Mit der Zeit wirst du ein Gefühl dafür entwickeln, wann welcher Ansatz am besten passt. 🎯