Stacked PR keeps a stale head after its branch is rewritten server-side: merge box spins on "loading stack status", and both update-branch and merge refuse
Dieses Issue hat noch niemand übernommen.
Bewertung
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Anfängerfreundlichkeit
- 48/100
- Issue-Typ
- Bug
- Klarheit
- Größtenteils klar
- Aktivitätsstatus
- Aktiv
- Tech-Stack
- git, github, go, graphql
- Bereich
- backend-api-design, cli
Rechercherichtung
Trace stack status handling and the update-branch, merge, and merge-async entry points, using the reported branch-ref and pull-request-head mismatch as the starting case. Reproduce the server-side rewrite followed by the stale stacked pull request, then verify that the head follows the branch or that a supported recovery route remains available. Confirm that the UI leaves the loading state and that merging reports its completed result.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
Summary
A stacked pull request got its branch rewritten by a GitHub-side operation, but the pull request head never followed. The branch ref and refs/pull/<n>/head then pointed at different commits, and the pull request was wedged: the merge box spun on loading stack status indefinitely, mergeable stayed UNKNOWN, and both REST routes out of the state were refused because the pull request is stacked.
Environment
- Native stack feature (stack created in the web UI, not via
gh stack). - Private organization repository.
- Default branch protected by classic branch protection: 6 required status checks (GitHub Actions), strict (branch must be up to date). No rulesets, no merge queue, no required reviews, no CODEOWNERS.
- Stack has 2 entries, base = default branch. Position 1 was merged about four weeks earlier; position 2 stayed open. The stack still reports
"open": trueand still lists the merged pull request as a member. - Both stack members target the default branch directly (
base.ref= default branch for each).
Reproduction
- Create a 2-entry stack on the default branch.
- Merge the bottom pull request. The stack stays
openand keeps the merged pull request as position 1. - Let the default branch advance by several commits over the following weeks.
- Have the remaining branch rewritten through GitHub — the resulting commits carry committer
GitHub, so the rewrite happened server-side, not from a local push.
Observed
After step 4 the branch ref and the pull request disagreed, and stayed that way for ~15 hours until manually unstuck:
refs/heads/<branch> = B rewritten, committer "GitHub", day 2 15:38
refs/pull/<n>/head = A pre-rewrite, committer "GitHub", day 2 01:10
GET /repos/{o}/{r}/pulls/{n} -> head.sha = A
default branch --o--o--o--o--o--o--o (advanced while the PR sat open)
\
refs/pull/<n>/head *A <- what the PR, the API and the merge box see
\
refs/heads/<branch> *B <- what the server-side rewrite actually wrote
The pull request was unmergeable from every direction:
- UI: merge box stuck on loading stack status; the spinner never resolved. No Merge button, no Update branch button, no error text.
- REST / GraphQL:
mergeable: null/UNKNOWN,mergeable_state: "unknown",mergeStateStatus: UNKNOWN— stable across repeated polls spread over many minutes. (Polling normally forces the background recomputation; here it never converged.) statusCheckRollupreportedSUCCESS— but for the stale headA.
Both REST escape hatches refused, each citing the stack:
PUT /repos/{owner}/{repo}/pulls/{n}/update-branch
403 {"message": "Updating a stacked PR's branch via this endpoint is not supported."}
PUT /repos/{owner}/{repo}/pulls/{n}/merge
403 {"message": "Merging stacked PRs via this endpoint is not supported. Use the asynchronous merge endpoint instead."}
The asynchronous endpoint that the second message points to accepted the request and then failed against the stale head:
PUT /repos/{owner}/{repo}/pulls/{n}/merge-async
{"merge_method": "squash", "merge_action": "direct_merge"}
-> {"status": "pending", "details": {"uuid": "...", "expected_head_sha": "A"}}
GET /repos/{owner}/{repo}/pulls/{n}/merge-async/{uuid}
-> {"status": "failed", "details": {"message": "6 of 6 required status checks are expected."}}
All six required checks had completed successfully on A — the very sha the endpoint echoed back as expected_head_sha. So the failure message is doubly misleading: the checks are neither missing nor pending on the head it claims to be merging.
Expected
When a stack rewrites a member branch, the pull request head follows the branch ref, exactly as an ordinary force-push does, and the merge box recovers.
Failing that, at least one route out of the state should stay open. update-branch and merge should not both refuse a stack whose only unmerged member targets the default branch directly — that combination leaves no supported way to fix a stale head, from the UI or the API.
Actual
The pull request was unmergeable from the UI and from every REST route, with no diagnostic anywhere — only an endless loading stack status spinner. Nothing in the UI indicated that the pull request was looking at a different commit than its own branch.
Workaround
Push any new commit to the branch. I merged the default branch into it with a plain local git merge + git push (no force). The synchronize event resynced the pull request head within seconds: mergeable flipped to true, mergeable_state went blocked -> unstable as the required checks re-ran on the new head, and the merge then completed normally.
One more UI symptom, after the merge
Clicking Merge on the recovered pull request also appeared to hang — the merge box kept spinning with no confirmation. The merge had in fact already succeeded: the API reported merged: true with a merge commit sha, and the commit was on the default branch. The merge box simply never updated.
Related but distinct
- #485 — a partial stack merge leaves a stale stack base sha, and Rebase stack replays already-merged commits. Here it is the pull request head that is stale, and no merge queue is involved.
- #450 — the stack view shows stale readiness while the API reports the pull requests clean. Here the API itself is the one reporting
UNKNOWN.
Both of those and this one share a theme: a stack that has had part of it merged keeps stale state somewhere, and the UI surfaces it as an unexplained spinner or an unexplained "not ready".
- Vorherrschende Sprache
- Go
- Sterne
- 1.5k
- Forks
- 73
- Ø Merge
- 1 T. 8 Std.
- Gemergte PRs (30 T.)
- 5
Beitragsleitfaden
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus github/gh-stack
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 85/100
-
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 92/100
-
feature request topic: cli - general
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 68/100
-
feature request topic: auto-merge
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 68/100
-
bug topic: docs
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 68/100
Alle Issues in github/gh-stack
Ähnliche Issues
-
agentic-workflows
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 76/100
-
agentic-workflows
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 70/100
microsoft/agent-framework-go#1179 ·
-
bug
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 76/100
-
[Bug]: OLLAMA_KEEP_ALIVE="5m" / "24h" crashes Ollama embedding and vision models with ValueError Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 75/100
infiniflow/ragflow#20223 · 1 Reaktion ·
-
bug needs triage pkg/translator/faro
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 88/100
open-telemetry/opentelemetry-collector-contrib#51484 · 1 Kommentar ·