Was macht Gestapelte PRs in den Basis-Branch landen?
Verwenden, wenn mehrere PRs verkettet sind (PR N basiert auf Branch von PR N-1) und den echten Basis-Branch erreichen müssen. Das Kernprinzip: CI des Zwischenbranches ist nicht das Tor. Der Schutz des Basis-Branchs ist das Tor. Also retargete jeden Layer just-in-time auf den Basis-Branch und lasse die native CI ihn validieren. Verlasse dich nie darauf, Zwischenbranchnamen in CI-Triggern fest zu codieren.
Repo: Übergib -R owner/repo an gh, oder führe es aus dem Clone aus (gh repo view --json nameWithOwner -q .nameWithOwner
löst es auf). Unten steht R für dieses owner/repo.
Prinzipien
- Die Merge-Reihenfolge wird durch echte Abhängigkeiten bestimmt (Inhaltsabhängigkeiten + Dateien, die von mehr als einem Layer bearbeitet werden). Stelle diese Reihenfolge zuerst fest; ordne sie nicht aus Bequemlichkeit um.
- Merge immer nur einen Layer in den Basis-Branch, einen nach dem anderen. Merge niemals einen Kind-Layer in seinen alten Voraussetzungs-Branch.
- Bevor die CI eines Layers relevant wird, merge den aktuellen Basis-Branch in diesen Layer, sodass sein Diff nur noch seine eigene Änderung enthält und er alles bereits Gelandete sieht.
- Der Spitzen-Branch enthält kumulativ jeden Layer; ein CI-Lauf auf dem Spitzen-Branch (retargeted auf Basis) validiert den gesamten zusammengebauten Stack. Nutze ihn als optionalen Pre-Flight.
Schritt 0: Reihenfolge festlegen und Konflikte erkennen
Ermittle den echten Basis-Branch des Repos (Standard-Branch vom Git-Remote, z.B. develop oder main; die Agentenanweisungsdatei (AGENTS.md / CLAUDE.md) kann bekannte Basis-Branches nennen). Für jeden PR überprüfe Konflikte gegen den Basis-Branch, nicht gegen seinen veralteten gestapelten Basis-Branch (der CLEAN anzeigen und dich in die Irre führen würde):
git fetch origin <base> <head> --quiet
git merge-tree --write-tree origin/<base> origin/<head> | grep -iE 'CONFLICT|changed in both' || echo "clean vs base"
Faustregel für die Reihenfolge: Voraussetzungsinhalt vor dem Gate, das ihn benötigt (z.B. lint-cleanup vor lint-enable); wenn zwei Layer dieselbe Datei bearbeiten, merge der frühere zuerst.
Schritt 1..N: pro Layer, in der richtigen Reihenfolge
PR=<number>
gh pr edit $PR -R $R --base <base> # vom gestapelten Branch weg retargeten
# Ein separater Worktree für den Head-Branch hält deinen Haupt-Checkout sauber:
WT=../<repo>-<head> # ein beliebiger Pfad außerhalb deines Haupt-Checkouts
git worktree add "$WT" <head> 2>/dev/null || true
git -C "$WT" fetch origin --quiet
git -C "$WT" merge origin/<base> # Basis synchronisieren; Konflikte lösen (siehe unten)
git -C "$WT" push
gh pr ready $PR -R $R # wenn es der aktive Layer ist
gh pr checks $PR -R $R --watch # native CI, base=<base>
# nur nach Review-Freigabe:
gh pr merge $PR -R $R --squash --delete-branch
# dann zum nächsten Layer übergehen (dessen Retarget+Base-Sync wird diesen absorbieren)
Native GitHub-Stacks (merge-async)
Ein PR, der mit der nativen Stack-Funktion von GitHub erstellt wurde, verweigert die klassischen Schritte oben:
gh pr edit --base:GraphQL: Cannot change the base branch because the pull request is part of a stack. (updatePullRequest)gh pr merge --auto:GraphQL: Auto-merge is not supported for stacked pull requests.gh pr merge:This pull request is part of a stack and must be merged using the asynchronous merge REST API.
Verwende die Async-API, zuerst den untersten PR (GitHub retargetet den nächsten von selbst auf den Basis-Branch, gib ihm ~30s):
sha=$(gh pr view $PR -R $R --json headRefOid -q .headRefOid)
u=$(gh api -X PUT repos/$R/pulls/$PR/merge-async -f merge_method=squash -f sha=$sha \
-f merge_action=direct_merge --jq '.details.uuid // .status')
gh api repos/$R/pulls/$PR/merge-async/$u --jq '.status+" "+(.details.message//"")' # abfragen, bis nicht mehr "pending"
- Das Mergen eines gestapelten PRs auf diese Weise merged auch jeden offenen PR darunter, also merge nur den Layer, den du tatsächlich landen willst.
- Nach jedem Squash ist der nächste Layer oft DIRTY bezüglich Dateien, die beide berühren (eine ADR, eine Versionsliste): merge den Basis-Branch hinein, löse Konflikte, pushe, warte auf CI, dann merge-async ihn.
- Freigaben überleben Basis-Syncs in Repos, die keine veralteten Reviews verwerfen (prüfe
required_pull_request_reviews.dismiss_stale_reviewsim Branch-Schutz). Das eigentliche Tor ist oft die CODEOWNERS-Freigabe für jeden berührten Pfad, nicht die Anzahl der Freigaben (pr-approval-gatelistet die Lücken). - Um jeden offenen PR zwischen den Landungen auf dem neuesten Stand mit seinem Basis-Branch zu halten, führe einen Base-Sync-Durchlauf durch: merge den Basis-Branch in jeden offenen PR-Branch und push (normale Merges, kein Force-Push).
Konfliktauflösung
- Workflow-Dateien, die von mehreren Layern bearbeitet werden (wiederverwendbare Build-Workflows, ein CI-Orchestrator-Workflow): behalte BEIDE Änderungen, wähle keine Seite aus.
- Format-Kollisionen (ein Spotless/Prettier-Anwendungs-Layer vs. Inhaltsänderungen auf dem Basis-Branch):
nimm den INHALT des Basis-Branchs für die konfliktreiche Datei, dann führe den Formatierer erneut darauf aus
(
mvn -pl <module> -am spotless:applyoderpnpm fmt). Die Formatierer-Seite zu nehmen, entfernt echte Änderungen; den Basis-Branch ohne Neuformatierung zu nehmen, scheitert am Format-Gate. Formatierer-Durchläufe benötigen den Build zur Auflösung (manche Repos holen Abhängigkeiten aus einer privaten Registry, die zuerst einen Cloud-Login benötigt; aktualisiere diesen, wenn der Build keine Abhängigkeiten holen kann).
Sicherheit
- Führe keinen Force-Push durch, überschreibe keine Geschichte und pushe nicht in geschützte Branches.
- Befolge die Commit-Authorship-Konventionen des Repos (manche verbieten AI-Co-Author-Trailer).
- Halte vor jedem Merge in den Basis-Branch für eine menschliche Review-Freigabe inne.
- Falls ein CI-Whitelist-PR für Zwischenbranches existiert (der Anti-Pattern, den dieses Skill ersetzt), empfehle, ihn zu schließen; falls er bereits gemerged wurde, mache die hinzugefügten Branchnamen und Ereignistypen rückgängig.
Verwandte Skills
opening-a-stacked-pr-sequence: Öffnen des Stacks (Bases, Merge-Reihenfolge) und der Restack-Fix pro Merge-Typ (stack.sh restack).pr-approval-gate: Welche Code-Owner jeder Layer noch benötigt.resolve-pr-threads-and-conflicts-and-merge: Einen Layer landen, sobald seine Threads und Freigaben erledigt sind.