Communitygithub.com

Gestapelte PRs in den Basis-Branch landen

Entflechte eine Kette gestapelter PRs (jeder basiert auf dem Branch des vorherigen) und lande sie nacheinander im Basis-Branch (develop/main/master), ohne eine CI-Trigger-Whitelist für Zwischenbranches zu benötigen. Unterstützt native GitHub-Stacks über die merge-async-API.

Was ist Gestapelte PRs in den Basis-Branch landen?

Dieses Skill automatisiert den Prozess, eine Kette von gestapelten Pull Requests (PRs) sicher in den Ziel-Branch (z.B. main oder develop) zu integrieren. Gestapelte PRs sind PRs, deren Branch auf dem Branch des vorherigen PRs basiert (PR N basiert auf Branch von PR N-1). Das Skill vermeidet den Anti-Pattern, Zwischenbranches in CI-Triggern hart zu codieren, indem es jeden Layer direkt gegen den Basis-Branch validiert. Es unterstützt sowohl klassische Stacks als auch native GitHub-Stacks über die merge-async-API. Der Ablauf umfasst das Ermitteln der korrekten Merge-Reihenfolge basierend auf echten Abhängigkeiten, das schrittweise Retargeting jedes PRs auf den Basis-Branch, das Konflikte lösen durch Einmergen des aktuellen Basis-Branchs, das Warten auf CI-Validierung und das abschließende Squash-Mergen. Das Skill behandelt auch Sonderfälle wie nativen GitHub-Stack (merge-async), Konfliktauflösung bei gemeinsam bearbeiteten Workflow-Dateien und Kollisionen zwischen Formatter- und Inhalts-Layern.

Funktioniert mit✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/sblattj/agent-chandlery/tree/main/skills/landing-stacked-prs

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

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_reviews im Branch-Schutz). Das eigentliche Tor ist oft die CODEOWNERS-Freigabe für jeden berührten Pfad, nicht die Anzahl der Freigaben (pr-approval-gate listet 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:apply oder pnpm 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.

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

Verwandte Skills