Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

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

オープン
#519 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
48/100
issue の種類
バグ
明瞭さ
おおむね明確
活発さ
活発
技術スタック
git, github, go, graphql

調査の方向性

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.

索引モデルが issue の本文から書いたものです。

説明

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": true and still lists the merged pull request as a member.
  • Both stack members target the default branch directly (base.ref = default branch for each).

Reproduction

  1. Create a 2-entry stack on the default branch.
  2. Merge the bottom pull request. The stack stays open and keeps the merged pull request as position 1.
  3. Let the default branch advance by several commits over the following weeks.
  4. 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.)
  • statusCheckRollup reported SUCCESS — but for the stale head A.

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".

主要言語
Go
スター
1.5k
フォーク
73
平均マージ
1日 8時間
マージ済み PR(30日)
7

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

github/gh-stack のほかの issue

github/gh-stack の issue をすべて見る

似ている issue

Go の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。