Reißverschluss-Merge (zipper merge)
Wann dieses Verfahren
Zwei Zweige desselben Repos sind stark divergiert und beide tragen Wert —
typisch: main vs. master nach paralleler Pflege, ein alter PR gegen einen
weitergezogenen Zielbranch, zwei Agenten-/Team-Stände mit je eigenen Fixes.
Ein normaler Merge würde entweder eine Seite plattmachen („ours"/„theirs") oder
einen unentwirrbaren Konfliktbrei erzeugen. Das Reißverschluss-Verfahren löst das,
indem es die Zähne beider Seiten abschnittsweise ineinandergreifen lässt:
je Abschnitt gewinnt die nachweislich bessere Version — oder eine begründete Mischung.
Nicht nötig bei trivialen Konflikten (eine Seite offensichtlich veraltet,
wenige Hunks): dann normal mergen. Das Verfahren lohnt ab dem Punkt, an dem man
für mehrere Dateien/Abschnitte ernsthaft abwägen muss.
Phase 0 — Lagebild und Basiswahl
- Beide Seiten vollständig sichten (nicht nur die Konfliktmarker):
git log --oneline A..B und B..A, git diff A...B --stat, dann die
divergenten Dateien beider Fassungen lesen.
- Basis wählen — nach Realität, nicht nach Datum. Basis wird der Branch,
der sich mit der veröffentlichten Wirklichkeit deckt: Registry-Stand
(npm/PyPI/…), deployte Version, grüne CI, Release-Tags. Ein Branch mit
neuerem Datum, aber ohne Deckung zur Außenwelt, ist Material, nicht Basis.
- Konsistenz beider Seiten prüfen — oft sind BEIDE in sich inkonsistent
(z. B. Basis hat Version X ohne passenden Changelog, Gegenseite pflegt den
Changelog, hängt aber bei der Version zurück). Solche Befunde gehören in
die Tabelle, nicht unter den Teppich.
Phase 1 — Entscheidungstabelle (der Kern)
Für jeden divergenten Abschnitt (Datei, Block, Feld — so fein wie nötig) eine
Zeile:
| Abschnitt |
Nimm |
Warum |
package.json → version |
Basis |
deckt sich mit Registry |
overrides-Block |
Gegenseite |
enthält Security-Bumps, die der Basis fehlen |
↳ darin Eintrag X |
mischen |
Eintrag der Gegenseite übernehmen, aber auf die neuere Version der Basis heben |
Changelog [Unreleased] |
mischen |
beide Blöcke vereinigen, Duplikate raus |
Regeln:
- Fakten statt Vermutung. Jede „Warum"-Zelle stützt sich auf etwas
Prüfbares: Registry-Abfrage, Testlauf, Advisory-Datenbank, isolierte
Probeinstallation der strittigen Datei. Wer rät, merged Vermutungen.
- „Mischen" ist ein legitimes Verdikt — der Reißverschluss darf innerhalb
eines Abschnitts beide Zähne greifen lassen.
- Verwerfen ist ein legitimes Verdikt — was auf keiner Seite trägt
(toter Badge, halb eingeführtes Feature), fliegt raus. Der Zwischenzustand
(„Feature halb drin") ist die schlechteste Variante: ganz rein oder ganz raus.
- Die fertige Tabelle ist das Vertragsdokument der Umsetzung — bei
Sessionwechsel oder Übergabe reicht sie, um mechanisch fortzusetzen.
Phase 2 — Umsetzung: abschnittsweise committen, am Ende pushen
- Auf der Basis einen echten Merge beginnen:
git merge <gegenseite>
(bewusst KEIN -X ours/theirs — die Konflikte sind gewollt sichtbar).
- Konflikte Abschnitt für Abschnitt exakt nach Tabelle lösen.
- Pro logischem Abschnitt ein Commit (bzw. bei einem einzigen Merge-Commit:
pro Abschnitt ein dokumentierter Lösungsschritt in der Commit-Message).
Jede Commit-Message nennt das Tabellen-Verdikt („overrides: Gegenseite +
vitest auf Basis-Stand gehoben"). So bleibt jeder Reißverschluss-Zahn
einzeln nachvollziehbar und revertierbar.
- Tests nach der Umsetzung (Suite, Build, Installierbarkeit) — erst grün,
dann weiter.
- Erst am Ende pushen — der lokale Verlauf darf während des Verfahrens
umgebaut werden, der Remote sieht nur das fertige Ergebnis.
- Divergenz schließen ohne Löschen: den unterlegenen Branch per
Fast-Forward auf das Ergebnis setzen (
git checkout <gegenseite> && git merge --ff-only <basis>). Beide Namen zeigen danach auf denselben
Stand; keine Historie geht verloren.
Eskalationspfad — Rebuild statt Merge (letzte Stufe)
Wenn der Reißverschluss nicht mehr greift, wird nicht gemerged, sondern neu
gebaut: Man destilliert die Absicht/Funktionalität des Branches/PRs
(WAS wollte er erreichen — nicht WIE) und implementiert sie frisch auf dem
aktuellen Stand. Der alte Branch/PR wird mit Verweis auf den Neubau geschlossen.
Auslöser für die Eskalation (einer genügt):
- Die Gegenseite ist nachweislich defekt (z. B. nie installierbar gewesen,
Build bricht ab) — dann gab es nie einen funktionierenden Stand zu mergen.
- Konfliktmasse ≫ Substanz: Die Änderung ist klein, aber über hunderte
verschobene Zeilen verschmiert (Rename-/Format-Wellen dazwischen).
- Die Historie ist vergiftet (z. B. dieselbe Versionsnummer auf beiden Seiten
für unterschiedliche Inhalte vergeben — ein Merge würde die Lüge erben).
- Der PR ist so alt, dass sein Kontext (APIs, Struktur) nicht mehr existiert.
Vorgehen: Absicht in 2–5 Sätzen festhalten (aus PR-Beschreibung, Commits,
Diff-Substanz) → auf aktuellem Stand neu implementieren → Tests → im alten
PR/Branch dokumentieren, WAS übernommen wurde und WAS bewusst nicht, dann
schließen. Urheberschaft der Idee im Commit/PR-Text nennen.
Fallstricke (aus der Ursprungs-Session, teuer bezahlt)
- Doppelter
[Unreleased]-Changelog-Block ist der häufigste Hotspot bei
parallel gepflegten Branches — immer vereinigen, nie einen still verwerfen.
- Beide Seiten vergeben dieselbe Versionsnummer für verschiedene Inhalte:
vor dem Merge klären, welche Nummer die Registry kennt; die andere Seite
bekommt im vereinigten Changelog eine Korrektur.
- Ein Merge ist eine Audit-Gelegenheit: Beim abschnittsweisen Lesen fallen
Dinge auf, die keiner der Branches sah (offene Advisories, tote Links,
inkonsistente Pins). Funde mitnehmen, aber als eigene Commits — nicht mit
den Reißverschluss-Entscheidungen vermengen.
- Übernahme „weil neuer": Datum ist kein Argument. Jede Übernahme braucht
einen prüfbaren Grund.
- Force-Push-Reflex: Das Verfahren kommt ohne Force-Push aus (Merge +
Fast-Forward). Wenn ein Force nötig scheint, ist meist die Basiswahl falsch.
Red Flags
| Gedanke |
Realität |
| „Ich nehme einfach die neuere Seite" |
Datum ≠ Qualität. Tabelle bauen. |
| „Die Konflikte löse ich in einem Rutsch" |
Ein Sammel-Commit macht jede Einzelentscheidung unrevidierbar. |
| „Push ich schon mal zwischendurch" |
Halbfertige Reißverschlüsse auf dem Remote verwirren jeden Mitleser. Push erst am Ende. |
| „Mergen geht immer irgendwie" |
Bei defekter Gegenseite oder vergifteter Historie ist Rebuild ehrlicher und schneller. |
| „Die Tabelle ist Overkill" |
Ohne Tabelle ist nach der dritten Datei vergessen, warum Abschnitt 1 so entschieden wurde. |
Verwandte Skills
bugfix-protocol — wenn der Merge einen echten Defekt aufdeckt, dort weiter.
skill-extractor — Herkunft dieses Skills (Live-Session-Destillat).
human-loop-audit — teilt sich nur das Reißverschluss-Bild, hat aber mit Git-Merges
nichts zu tun (interaktives App-/GUI-Testverfahren mit dem Nutzer).
Changelog
1.0.0 (2026-08-07)
- Initiale Version. Extrahiert aus einer Live-Merge-Session vom 2026-08-06
(main/master-Vereinigung mehrerer Repos mit Entscheidungstabelle, Fund einer
nie installierbaren Gegenseite, Fast-Forward-Abschluss; Eskalationsfall
„Absicht übernehmen, neu bauen").
1---2name: reissverschluss-merge3description: Reißverschluss-Verfahren (englisch: zipper merge) für sehr konfliktreiche Merges: Wenn zwei divergente Branches oder ein PR und sein Zielbranch BEIDE wertvolle, unvereinbar erscheinende Änderungen tragen, wird nicht pauschal eine Seite gewählt, sondern Abschnitt für Abschnitt verglichen, per Entscheidungstabelle (Nimm/Warum) die jeweils bessere Version übernommen, abschnittsweise committet und erst am Ende gepusht. Letzte Eskalationsstufe: Rebuild statt Merge — nur die Absicht/Funktionalität des Branches übernehmen und auf dem aktuellen Stand neu bauen. Nutzen bei: "Merge-Konflikt", "divergente Branches", "main vs. master", "konfliktreicher PR", "zipper merge", "Reißverschluss", "beide Seiten haben Recht", "PR ist zu alt zum Mergen".4---56<img src="banner.png" width="100%" alt="reissverschluss-merge banner">78# Reißverschluss-Merge (zipper merge)910## Wann dieses Verfahren1112Zwei Zweige desselben Repos sind **stark divergiert** und **beide tragen Wert** —13typisch: `main` vs. `master` nach paralleler Pflege, ein alter PR gegen einen14weitergezogenen Zielbranch, zwei Agenten-/Team-Stände mit je eigenen Fixes.15Ein normaler Merge würde entweder eine Seite plattmachen („ours"/„theirs") oder16einen unentwirrbaren Konfliktbrei erzeugen. Das Reißverschluss-Verfahren löst das,17indem es die Zähne beider Seiten **abschnittsweise ineinandergreifen** lässt:18je Abschnitt gewinnt die nachweislich bessere Version — oder eine begründete Mischung.1920**Nicht nötig** bei trivialen Konflikten (eine Seite offensichtlich veraltet,21wenige Hunks): dann normal mergen. Das Verfahren lohnt ab dem Punkt, an dem man22für mehrere Dateien/Abschnitte ernsthaft abwägen muss.2324## Phase 0 — Lagebild und Basiswahl25261. **Beide Seiten vollständig sichten** (nicht nur die Konfliktmarker):27 `git log --oneline A..B` und `B..A`, `git diff A...B --stat`, dann die28 divergenten Dateien beider Fassungen lesen.292. **Basis wählen — nach Realität, nicht nach Datum.** Basis wird der Branch,30 der sich mit der **veröffentlichten Wirklichkeit** deckt: Registry-Stand31 (npm/PyPI/…), deployte Version, grüne CI, Release-Tags. Ein Branch mit32 neuerem Datum, aber ohne Deckung zur Außenwelt, ist Material, nicht Basis.333. **Konsistenz beider Seiten prüfen** — oft sind BEIDE in sich inkonsistent34 (z. B. Basis hat Version X ohne passenden Changelog, Gegenseite pflegt den35 Changelog, hängt aber bei der Version zurück). Solche Befunde gehören in36 die Tabelle, nicht unter den Teppich.3738## Phase 1 — Entscheidungstabelle (der Kern)3940Für jeden divergenten Abschnitt (Datei, Block, Feld — so fein wie nötig) eine41Zeile:4243| Abschnitt | Nimm | Warum |44|---|---|---|45| `package.json` → version | Basis | deckt sich mit Registry |46| `overrides`-Block | Gegenseite | enthält Security-Bumps, die der Basis fehlen |47| ↳ darin Eintrag `X` | **mischen** | Eintrag der Gegenseite übernehmen, aber auf die neuere Version der Basis heben |48| Changelog `[Unreleased]` | **mischen** | beide Blöcke vereinigen, Duplikate raus |4950Regeln:5152- **Fakten statt Vermutung.** Jede „Warum"-Zelle stützt sich auf etwas53 Prüfbares: Registry-Abfrage, Testlauf, Advisory-Datenbank, isolierte54 Probeinstallation der strittigen Datei. Wer rät, merged Vermutungen.55- **„Mischen" ist ein legitimes Verdikt** — der Reißverschluss darf innerhalb56 eines Abschnitts beide Zähne greifen lassen.57- **Verwerfen ist ein legitimes Verdikt** — was auf keiner Seite trägt58 (toter Badge, halb eingeführtes Feature), fliegt raus. Der Zwischenzustand59 („Feature halb drin") ist die schlechteste Variante: ganz rein oder ganz raus.60- Die fertige Tabelle ist das **Vertragsdokument** der Umsetzung — bei61 Sessionwechsel oder Übergabe reicht sie, um mechanisch fortzusetzen.6263## Phase 2 — Umsetzung: abschnittsweise committen, am Ende pushen64651. Auf der Basis einen echten Merge beginnen: `git merge <gegenseite>`66 (bewusst KEIN `-X ours/theirs` — die Konflikte sind gewollt sichtbar).672. Konflikte **Abschnitt für Abschnitt** exakt nach Tabelle lösen.683. **Pro logischem Abschnitt ein Commit** (bzw. bei einem einzigen Merge-Commit:69 pro Abschnitt ein dokumentierter Lösungsschritt in der Commit-Message).70 Jede Commit-Message nennt das Tabellen-Verdikt („overrides: Gegenseite +71 vitest auf Basis-Stand gehoben"). So bleibt jeder Reißverschluss-Zahn72 einzeln nachvollziehbar und revertierbar.734. **Tests nach der Umsetzung** (Suite, Build, Installierbarkeit) — erst grün,74 dann weiter.755. **Erst am Ende pushen** — der lokale Verlauf darf während des Verfahrens76 umgebaut werden, der Remote sieht nur das fertige Ergebnis.776. **Divergenz schließen ohne Löschen:** den unterlegenen Branch per78 Fast-Forward auf das Ergebnis setzen (`git checkout <gegenseite> &&79 git merge --ff-only <basis>`). Beide Namen zeigen danach auf denselben80 Stand; keine Historie geht verloren.8182## Eskalationspfad — Rebuild statt Merge (letzte Stufe)8384Wenn der Reißverschluss nicht mehr greift, wird **nicht gemerged, sondern neu85gebaut**: Man destilliert die **Absicht/Funktionalität** des Branches/PRs86(WAS wollte er erreichen — nicht WIE) und implementiert sie frisch auf dem87aktuellen Stand. Der alte Branch/PR wird mit Verweis auf den Neubau geschlossen.8889Auslöser für die Eskalation (einer genügt):9091- Die Gegenseite ist **nachweislich defekt** (z. B. nie installierbar gewesen,92 Build bricht ab) — dann gab es nie einen funktionierenden Stand zu mergen.93- Konfliktmasse ≫ Substanz: Die Änderung ist klein, aber über hunderte94 verschobene Zeilen verschmiert (Rename-/Format-Wellen dazwischen).95- Die Historie ist vergiftet (z. B. dieselbe Versionsnummer auf beiden Seiten96 für **unterschiedliche** Inhalte vergeben — ein Merge würde die Lüge erben).97- Der PR ist so alt, dass sein Kontext (APIs, Struktur) nicht mehr existiert.9899Vorgehen: Absicht in 2–5 Sätzen festhalten (aus PR-Beschreibung, Commits,100Diff-Substanz) → auf aktuellem Stand neu implementieren → Tests → im alten101PR/Branch dokumentieren, WAS übernommen wurde und WAS bewusst nicht, dann102schließen. Urheberschaft der Idee im Commit/PR-Text nennen.103104## Fallstricke (aus der Ursprungs-Session, teuer bezahlt)105106- **Doppelter `[Unreleased]`-Changelog-Block** ist der häufigste Hotspot bei107 parallel gepflegten Branches — immer vereinigen, nie einen still verwerfen.108- **Beide Seiten vergeben dieselbe Versionsnummer für verschiedene Inhalte:**109 vor dem Merge klären, welche Nummer die Registry kennt; die andere Seite110 bekommt im vereinigten Changelog eine Korrektur.111- **Ein Merge ist eine Audit-Gelegenheit:** Beim abschnittsweisen Lesen fallen112 Dinge auf, die keiner der Branches sah (offene Advisories, tote Links,113 inkonsistente Pins). Funde mitnehmen, aber als eigene Commits — nicht mit114 den Reißverschluss-Entscheidungen vermengen.115- **Übernahme „weil neuer":** Datum ist kein Argument. Jede Übernahme braucht116 einen prüfbaren Grund.117- **Force-Push-Reflex:** Das Verfahren kommt ohne Force-Push aus (Merge +118 Fast-Forward). Wenn ein Force nötig scheint, ist meist die Basiswahl falsch.119120## Red Flags121122| Gedanke | Realität |123|---|---|124| „Ich nehme einfach die neuere Seite" | Datum ≠ Qualität. Tabelle bauen. |125| „Die Konflikte löse ich in einem Rutsch" | Ein Sammel-Commit macht jede Einzelentscheidung unrevidierbar. |126| „Push ich schon mal zwischendurch" | Halbfertige Reißverschlüsse auf dem Remote verwirren jeden Mitleser. Push erst am Ende. |127| „Mergen geht immer irgendwie" | Bei defekter Gegenseite oder vergifteter Historie ist Rebuild ehrlicher und schneller. |128| „Die Tabelle ist Overkill" | Ohne Tabelle ist nach der dritten Datei vergessen, warum Abschnitt 1 so entschieden wurde. |129130## Verwandte Skills131132- `bugfix-protocol` — wenn der Merge einen echten Defekt aufdeckt, dort weiter.133- `skill-extractor` — Herkunft dieses Skills (Live-Session-Destillat).134- `human-loop-audit` — teilt sich nur das Reißverschluss-Bild, hat aber mit Git-Merges135 nichts zu tun (interaktives App-/GUI-Testverfahren mit dem Nutzer).136137## Changelog138139### 1.0.0 (2026-08-07)140- Initiale Version. Extrahiert aus einer Live-Merge-Session vom 2026-08-06141 (main/master-Vereinigung mehrerer Repos mit Entscheidungstabelle, Fund einer142 nie installierbaren Gegenseite, Fast-Forward-Abschluss; Eskalationsfall143 „Absicht übernehmen, neu bauen").