🔀Merge & Rebase

Zwei Wege, auseinandergelaufene Branches wieder zusammenzuführen – plus die Werkzeuge für den Ernstfall: Konflikte lösen, Verlorenes mit dem Reflog retten, Arbeit mit stash parken und Fehler mit bisect finden.

⚖️Merge vs. Rebase nebeneinander

Gleicher Startzustand, zwei Strategien. Das Ergebnis enthält dieselben Dateien – aber die Geschichte sieht anders aus.

🔀 git merge

b8aa7c5bd3d4f17c042133eb7b55291b0e608ab1 Initialer Commit (Alice)b8aa7c5Initialer Com…ffce1bec2c94fa8f3302e3c1178fb36c7a2548fc Suche (Alice)ffce1beSuche179c2a5dbf4f6af1cf98a62e066004a9342b648c Filter (Alice)179c2a5Filterfeaturec70fe17b23ff1bbdd63734bae3d3e56d6a2ee73f Footer (Alice)c70fe17Footer56616e0da46c2e77d54a3b5ade42ad7c416ba410 Logo (Alice)56616e0Logo8ef6c5854f6c466b425c6e21cc3d1d40efe034ee Merge branch 'feature' (Alice)8ef6c58Merge branch …HEAD → main

git merge feature (auf main): ein neuer Merge-Commit mit zwei Eltern verbindet beide Linien. Keine bestehende ID ändert sich.

$ git merge feature

🪜 git rebase

b8aa7c5bd3d4f17c042133eb7b55291b0e608ab1 Initialer Commit (Alice)b8aa7c5Initialer Com…ffce1bec2c94fa8f3302e3c1178fb36c7a2548fc Suche (Alice) nur noch über das Reflog erreichbarffce1beSuche179c2a5dbf4f6af1cf98a62e066004a9342b648c Filter (Alice) nur noch über das Reflog erreichbar179c2a5Filterc70fe17b23ff1bbdd63734bae3d3e56d6a2ee73f Footer (Alice)c70fe17Footer56616e0da46c2e77d54a3b5ade42ad7c416ba410 Logo (Alice)56616e0Logo0154454615bfa48f4b8a4454686409c58554e6ec Suche (Alice)0154454Sucheeccf5f9c6670c95094fa77b80d08ceceab8ed781 Filter (Alice)eccf5f9FilterHEAD → mainfeature

git merge feature (auf main) ist jetzt ein Fast-Forward: eine gerade Linie ohne Merge-Commit.

$ git rebase main · git merge feature
git mergegit rebase
Geschichtebleibt wie sie war, Verzweigungen sichtbarlinear, als wäre alles nacheinander entstanden
Commit-IDsunverändert – nur ein neuer Merge-Commitalle übertragenen Commits bekommen neue IDs
Konflikteeinmal, beim Mergeggf. pro übertragenem Commit
Sicher für geteilte Branches?janein – nur für eigene, noch nicht geteilte Commits
Typischer Einsatzfertigen Feature-Branch in main übernehmeneigenen Branch auf aktuellen main bringen, git pull --rebase
⚠️ Die goldene Regel des Rebasens
Rebase keine Commits, die schon andere haben (also gepusht und von anderen geholt wurden). Rebase ersetzt Commits durch neue – wer auf den alten weitergearbeitet hat, bekommt doppelte Commits und Chaos. Pro Git: „Do not rebase commits that exist outside your repository and that people may have based work on.“
💡 Fast-Forward
Ist der Ziel-Branch ein direkter Vorfahre, muss Git nichts zusammenführen: git merge schiebt den Zeiger einfach vor. --no-ff erzwingt trotzdem einen Merge-Commit, --ff-only verweigert alles andere.

💥Merge-Konflikte verstehen

Git vergleicht beim 3-Wege-Merge beide Seiten mit ihrem letzten gemeinsamen Vorfahren, der Merge-Base. Nur wo beide Seiten denselben Bereich verschieden geändert haben, entsteht ein Konflikt.
           ours (HEAD, main)
          ●───●  farbe=grün
         ╱
  ●───●  Merge-Base: farbe=blau         Regel pro Bereich:
         ╲                               • nur eine Seite geändert  → diese Änderung
          ●───●  farbe=rot               • beide gleich geändert   → übernehmen
           theirs (feature)              • beide verschieden       → KONFLIKT
🧩 Merge-Base (gemeinsamer Vorfahre)
git merge-base main feature
🟠 ours = HEAD (main)
deine Seite
🔵 theirs = feature
die hereinkommende Seite
Ergebnis im Arbeitsverzeichnis
1 Konflikt
name=Shop
<<<<<<< HEAD
farbe=grün
=======
farbe=rot
>>>>>>> feature
waehrung=EUR
✅ 1 Zeile(n) unverändert
💥 Konflikt: Basis hatte 1, HEAD hat 1, feature hat 1 Zeile(n) – beide Seiten haben denselben Bereich unterschiedlich geändert.
✅ 1 Zeile(n) unverändert
✅ Konflikt lösen
1. Datei öffnen, Marker <<<<<<< ======= >>>>>>> entfernen und den gewünschten Inhalt herstellen. 2. git add datei 3. git commit (bzw. --continue).
💡 Eine Seite komplett nehmen
git checkout --ours datei bzw. --theirs. Achtung beim Rebase: dort ist „ours“ der Branch, auf den du rebast, und „theirs“ dein eigener Commit.
⚠️ Notausgang
git merge --abort, git rebase --abort, git cherry-pick --abort stellen den Zustand vor dem Befehl wieder her.

Selbst ausprobieren: Konflikt im Simulator

b8aa7c5bd3d4f17c042133eb7b55291b0e608ab1 Initialer Commit (Alice)b8aa7c5Initialer Com…cb5f08ccf330ec3fd86d95eca469df83fb0b3393 Konfiguration (Alice)cb5f08cKonfigurationbe12dcf226975c6730d2ef2caa30c7ffbd0d6254 rot (Alice)be12dcfrotfeature08980e32c914a9120b463169cb4cd72cf50554bd grün (Alice)08980e3grünHEAD → main
alice@laptop: ~/projekt
💡 main und feature haben config.txt verschieden geändert. Starte mit git merge feature.
main $

🛟git reflog – der Rettungsanker

Git notiert lokal jede Bewegung von HEAD. Auch nach reset --hard, einem missglückten Rebase oder einem gelöschten Branch sind die Commits noch da – nur kein Name zeigt mehr darauf.
b8aa7c5bd3d4f17c042133eb7b55291b0e608ab1 Initialer Commit (Alice)b8aa7c5Initialer Com…e887d2b1932e643f8565608e3747773db7023112 Wichtig 1 (Alice)e887d2bWichtig 10d44fb262cf7a3d80d9f193d8643dc677278fcce Wichtig 2 (Alice)0d44fb2Wichtig 2HEAD → main
alice@laptop: ~/projekt
💡 Führe erst git reset --hard HEAD~2 aus – die Commits verschwinden aus dem Log. Dann git reflog und zurückholen.
main $
💡 Was das Reflog kann
HEAD@{2} = wo HEAD vor zwei Bewegungen stand. Auch pro Branch: main@{1}, oder zeitlich: main@{yesterday}. Nicht erreichbare Einträge verfallen standardmäßig nach 30 Tagen, die übrigen nach 90.
⚠️ Was es nicht kann
Das Reflog ist rein lokal (wird nicht gepusht oder geklont) und kennt nur Commits. Nie committete Änderungen im Arbeitsverzeichnis kann es nicht zurückholen.

📦git stash – Arbeit parken

Du bist mitten in einer Änderung und musst schnell den Branch wechseln? stash legt die Änderungen an Index und Arbeitsverzeichnis beiseite und stellt den sauberen HEAD-Stand her.
b8aa7c5bd3d4f17c042133eb7b55291b0e608ab1 Initialer Commit (Alice)b8aa7c5Initialer Com…HEAD → main531efa429f0615e70c8fb6983919d05e501687ef Version 1.0.1 (Alice)531efa4Version 1.0.1hotfix
alice@laptop: ~/projekt
💡 README.md ist lokal geändert. Versuche git switch hotfix – dann mit git stash parken, wechseln, zurück, git stash pop.
main $

Intern ist ein Stash-Eintrag ein Commit (mit dem Index-Stand als weiterem Elternteil) unter refs/stash; ältere Einträge findet man über dessen Reflog: stash@{1}. Unversionierte Dateien werden nur mit git stash -u mitgenommen.

🔎git bisect – Binärsuche nach dem Fehler

Irgendwo zwischen einem guten und einem schlechten Commit wurde ein Fehler eingebaut. bisect halbiert den Bereich bei jedem Test – bei 1000 Commits reichen höchstens 10 Tests.
Der Fehler steckt (geheim) ab Commit:
Start
b8aa7
✅
#1
02997
·
#2
673ce
·
#3
37d89
·
#4
ab40e
·
#5
9b1f9
·
#6
4aca1
·
#7
96461
🔍
#8
76d14
·
#9
14cf3
·
#10
679f4
·
#11
f503c
·
#12
11598
·
#13
cd131
·
#14
58c14
·
#15
ca564
❌
Git checkt 964619b („Änderung 7“) aus. Noch 15 Kandidaten. Funktioniert die Software hier? (Die Wahrheit wäre: gut)
git bisect start
git bisect bad ca56484
git bisect good b8aa7c5

Quellen: Pro Git 3.2 „Einfaches Branching und Merging“, 3.6 „Rebasing“, 7.3 „Stashen und Bereinigen“, 7.10 „Debuggen mit Git“ (git-scm.com/book/de/v2); git-scm.com/docs/git-merge, git-rebase, git-reflog, git-stash, git-bisect.