--- type: adr id: "0022" status: accepted date: 2026-08-19 supersedes: null superseded_by: null related: - "docs/issues/0099-threadnet-web-12-upstream-sicherheitsfixes-lassen-sich-nicht-m.md" --- # ADR-0022: Anschluss an Upstream durch einen einmaligen Merge, nicht durch einen geteilten Graft ## Kontext `ThreadNet-Web` enthält keine Upstream-Historie: Am 2026-05-10 kam ein kompletter Element-Web-Baum in einem Commit herein. Ohne gemeinsamen Vorfahren ist `git merge upstream/develop` unmöglich, und jedes Update bedeutet, zwölf eigene Patches von Hand auf einen neuen Baum aufzutragen. Das ist nicht nur mühsam, sondern gefährlich: Verschiebt Element eine Datei, verschwinden unsere Zeilen **ohne Konflikt** (#0099). Am 2026-08-19 wurde der tatsächliche Ursprung gemessen statt geraten: **`deadd548` vom 2026-05-08** (nicht der Tag `v1.12.17`, wie zuvor angenommen). Der Beleg ist die Baumdistanz — 43 abweichende Dateien, davon 31 reine Modus-Änderungen und der Rest unser eigenes Feature. Im Wegwerf-Klon getestet: Mit gesetztem Vorfahren läuft ein Merge von drei Monaten `develop` durch und erzeugt **32 konfliktbehaftete Dateien, davon nur vier Quellcode** — genau unsere Patches. ## Optionen **A: Geteilter Replace-Ref.** `git replace --graft` und `refs/replace/*` mitliefern. Die Historie bleibt formal unverändert, Git *interpretiert* sie nur anders. Nachteil: Jeder Klon braucht einen zusätzlichen Fetch, und wer ihn vergisst, sieht eine andere Historie als alle anderen — ein stiller Unterschied, der sich erst im Konfliktfall zeigt. **B: Einmaliger echter Merge**, danach normale Merges. ⚠️ **Praezisierung nach dem Test:** `--allow-unrelated-histories` allein liefert einen ZWEI-Wege-Vergleich und damit 1757 Konflikte — gemessen. Der Graft ist kein Gegenentwurf zu B, sondern sein Werkzeug: lokal setzen, den Merge damit rechnen lassen (32 Konflikte), committen. Der Merge-Commit traegt danach die echten Eltern, der Graft kann weg, und die Abstammung laeuft ueber den Merge-Commit selbst. Die Nahtstelle wird ein sichtbarer Merge-Commit. Kein Sonderwissen, kein Zusatzschritt, kein Klon kann sie versehentlich übersehen. **C: So weiterarbeiten wie bisher** — Patches von Hand auftragen. Verworfen: Das ist der Zustand, der die stille Klasse überhaupt erst erzeugt. ## Entscheidung **Option B** (sorb, 2026-08-19). Ausschlaggebend ist nicht der Aufwand — beide Wege sind ähnlich billig — sondern die Sichtbarkeit. A funktioniert nur, solange alle daran denken; B trägt sich selbst. Für ein Repo, dessen Kernproblem „Git meldet nichts" ist, wäre ein Mechanismus, der stillschweigend unterschiedlich wirkt, die falsche Wahl. `deadd548` bleibt trotzdem wichtig: Es ist der Stand, gegen den der Merge gefahren wird, und ohne diese Messung wäre der Merge auf eine erfundene Grundlage gelaufen. ## Konsequenzen - Der erste Merge ist ein einmaliger Kraftakt mit vier Quellcode-Konflikten; danach ist ein Upstream-Update ein gewöhnlicher Merge. - **Die stille Klasse verschwindet**: Verschiebt Upstream eine Datei, die wir angefasst haben, meldet Git künftig einen Konflikt, statt unsere Zeilen wortlos fallen zu lassen. - Die Nahtstelle bleibt als Merge-Commit dauerhaft sichtbar — gewollt, nicht geduldet. - **Abnahme ist kein Build, sondern ein Funktionstest**: Die ClamAV-Patches liegen in der Medien-Pipeline. Ohne den Test aus #0099 (verschlüsselte Datei senden, abgelehnte empfangen) ist der Merge nicht abgenommen. - Schritt 4 aus #0099 — ein Verfahren zum Auftragen der Patches — wird damit gegenstandslos.