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
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 48/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- git, github, go, graphql
- Lĩnh vực
- backend-api-design, cli
Hướng nghiên cứu
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.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
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".
- Ngôn ngữ chính
- Go
- Star
- 1.5k
- Fork
- 73
- Merge trung bình
- 1 ngày 8 giờ
- Pull request đã merge (30 ngày)
- 7
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của github/gh-stack
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 92/100
-
feature request topic: cli - general
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
-
feature request topic: auto-merge
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
-
bug topic: docs
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 68/100
Tất cả issue của github/gh-stack
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
NVIDIA/gpu-operator#2955 ·
-
agentic-workflows
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
-
Broadcast Documentation Đang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100
kovidgoyal/kitty#10516 ·
-
CVE-2024-24786 CPE mismatch Đang mởbug
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100
cisagov/vulnrichment#337 ·
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 72/100