Repository navigation
gh stack resolves the API repository from the origin remote, ignoring --remote, gh repo set-default, and its own state file — silently operating on the wrong repository in multi-remote clones #381
Description
Activity
Flynnjustin24
- addedbugBug ReportsBug Reportstopic: cli - submit`gh stack submit`: remote/SSH handling, config flags, draft PRs, and submit UX or failures.`gh stack submit`: remote/SSH handling, config flags, draft PRs, and submit UX or failures.
on Aug 24, 2026 Still reproducible in v0.1.1
Setup:
origin= upstream org repofork= personal fork. Stack configured withgh-stack.remote=fork.Symptom:
gh stack submit Checking stack state... Pushing to fork... ⚠ failed to create PR for hotload-driver: creating PR: GraphQL: Head sha can't be blank, Base sha can't be blank, No commits between main and hotload-driver, Head ref must be a branch (createPullRequest) ✓ Pushed and synced 1 branchesPush succeeds to the fork remote, but
createPullRequestGraphQL mutation fails becausegh stackresolves the API target fromoriginand sendsheadRefName: "fix-xyz"without the fork owner prefix — GitHub looks for the branch in the upstream org repo where it doesn't exist.GH_DEBUG=1confirms two separate graphql calls: one resolves the stack state againstorigin, the push goes tofork, then PR creation targetsoriginwith a head ref that doesn't exist there.Workaround (requires write access to upstream):
git config gh-stack.remote origin gh stack submit
Pushes the branch to the upstream repo directly, bypassing the fork mismatch. Not viable for contributors without push access — issue #46 covers that broader case.
Same bug on v0.1.1 (gh 2.97.0), in the other common fork layout:
originis the fork andupstreamis the template it was forked from. Three things this adds to the report:1. The repo is picked by remote name, and
upstreamwins overorigin.Config.Repo()calls go-gh'srepository.Current(), which takes the first GitHub remote after sorting byremoteNameSortScore(upstream3,github2,origin1). It never readsremote.<name>.gh-resolved. So withorigin https://github.com/myorg/app.git (fetch/push) upstream https://github.com/myorg/template.git (fetch/push)plus
gh repo set-default myorg/appandgit config remote.pushDefault origin, everygh stackAPI call goes tomyorg/template.gh pr viewresolvesmyorg/appcorrectly in the same checkout.2. Colliding PR numbers make branches look merged, and
sync --prunecould then delete live branches. The template had merged PRs #115/#116, and the app had open PRs #115/#116 for the stacked branches.gh stack init feat/a feat/badopted both branches fine.gh stack view --jsonattachedmyorg/template's merged Enhancement: Allowgh stack linkto parse PR URLs, not just PR numbers #115/Feature request: merge when ready button in web #116 to them.gh stack link 115 116failed withPR #115 (https://github.com/myorg/template/pull/115) cannot be added to a stack: it has been merged(exit 5). Adding--remote originchanged nothing.
Because those branches read as merged,
gh stack sync --prunewould delete local branches that still have open PRs. I didn't run it to confirm.3.
submit --autoexits 0 when PR creation fails. It pushed toorigincorrectly, then failed to create both PRs in the wrong repo:GraphQL: Head sha can't be blank, Base sha can't be blank, No commits between main and feat/a, Head ref must be a branch (createPullRequest)The exit status was still 0, so a script or agent reads that as success.
Workaround (the same as above):
GH_REPO=myorg/app gh stack link --remote origin 115 116created the stack, andGH_REPO=myorg/app gh stack view --jsonthen shows the right, open PRs.Suggested fix: resolve the repo the way
ghdoes. That meansGH_REPO, then thegh-resolvedremote, then the remote selected by--remote/remote.pushDefault, and only then the name heuristic. Also,submitshould exit non-zero when any PR create fails. Related: #496, #509.Follow-up to my comment above (same setup:
origin=myorg/app,upstream=myorg/template, v0.1.1). TheGH_REPOworkaround is not reliable once any command has run without it.Wrong-repo PRs stick in the stack file.
syncStackPRs(cmd/utils.go) skips any branch whose trackedpullRequestismerged, because "the merged state is final". Onegh stackrun withoutGH_REPOstoresmyorg/template's merged #115/#116 in.git/gh-stackfor the branches. After that,GH_REPO=myorg/app gh stack view --jsonalso shows those merged template PRs. It did so both withGH_REPOas a prefix and withexport GH_REPO=…. Right after aGH_REPO=… gh stack linkthe view was correct, and minutes later it was wrong again. The fix has to cover the stored refs too. Otherwise, a merged ref from the wrong repo stays in the file for good.gh stack merge <n>can target the wrong repo. A bare number is tried as a stack number, then as a PR number, in whichever repo was resolved. Heremyorg/templatealso had a merged #117, sogh stack merge 117would have acted on it.What worked for merging:
gh pr mergerefuses a stacked PR ("This pull request is part of a stack and must be merged using the asynchronous merge REST API"). The async merge endpoint, with the repo named in the path, worked:gh api -X PUT repos/myorg/app/pulls/<top PR>/merge-async -f merge_method=merge -f sha=<head sha> # 202 + uuid, then poll: gh api repos/myorg/app/pulls/<top PR>/merge-async/<uuid> # -> merged
That merged the top PR and the one below it together.
The top-level
repositoryfield thatinitwrites into.git/gh-stackis never read, as the original report says. So runninginitwithGH_REPOset doesn't help either.Another reproduction on v0.1.0, with the layout from the original report:
originis the upstream repo (umputun/revmux) and a second remoteforkis my fork (mskonovalov/revmux), where every stack branch lives.gh repo set-default mskonovalov/revmuxwas set.gh stack submitpushed to the fork but tried to create the PRs againstumputun/revmux.Prefixing the command with
GH_REPO=mskonovalov/revmuxcreated the stack on the fork. Given the stale-state issue described above, agents in this layout need the prefix on everygh stackcommand, not onlysubmit. As far as I can see, the bundled skill (skills/gh-stack/SKILL.md, v0.1.1) doesn't mention this. A short note there would help until the repository resolution is fixed.- added a commit that references this issue
on Oct 11, 2026
Summary
In a clone with multiple remotes, every
gh stackPR/stack API operation is bound to the repository of theoriginremote, regardless of:--remoteflag (which only changes the push target),gh repo set-default/remote.<name>.gh-resolved(the standard gh CLI repo resolution),repositoryfield in.git/gh-stack(editing it has no effect on subsequent runs).If
originpoints at a different repository than the one your branches and PRs live in — the completely standard fork workflow whereoriginis upstream and you push to your fork —gh stack submitpushes to the right remote but then tries to create PRs and stacks on the wrong repository. In--auto/ non-interactive mode this happens silently: there is no "Multiple remotes found" prompt (the interactive path in #219 at least shows one).In my case the only reason no PRs were actually opened against the upstream project is that my branches don't exist there, so every
createPullRequestfailed. If upstream had happened to contain same-named branches,gh stack submit --autowould have opened a pile of PRs against a repository I never pointed it at. For agent/scripted use this is a dangerous default.Environment
origin=github.com/argoproj/argo-workflows(upstream),joibel=github.com/Joibel/argo-workflows(fork, where all stack branches and 8 open PRs live)gh repo set-default Joibel/argo-workflowsis set (remote.joibel.gh-resolved = base)Steps to reproduce
originpointing at upstream and a second remote pointing at your fork.gh stack init --base <trunk> <branch1> ... <branchN>— adopts the branches fine, but records"repository": "github.com:<origin-owner>/<repo>"in.git/gh-stack.gh stack submit --remote <fork-remote> --autoObserved
Push goes to the fork remote correctly ("Pushing to joibel... ✓ Pushed and synced 8 branches").
All PR operations target the
originrepository: existing fork PRs are not detected, and the extension attemptscreatePullRequestagainst upstream, failing with:(Same error signature as Various issues exhibited when local and remote base branch names are not the same #219, different root cause — here the refs genuinely don't exist because it's querying the wrong repository.)
The interactive TUI confirms it: the header shows
Repo: argoproj/argo-workflowseven when invoked with--remote joibel.None of the following change the target repository:
--remote,gh repo set-default, editing therepositoryfield in.git/gh-stack.Workaround
GH_REPO=<fork-owner>/<repo> gh stack submit --remote <fork-remote> --autoworks perfectly — existing PRs are detected ("PR #NN for is up to date") and the stack is created.Expected
GH_REPO, thengh-resolved/gh repo set-default, then remote heuristics), or at minimum follow the remote selected with--remote, since a stack's branches and PRs must by definition (FR: support for cross-fork stacked PRs #46) live in the repository the branches are pushed to.origin.gh stack initshould surface/validate which repository it bound the stack to.Related
--autoskips.gh stack submitfails when remote uses a custom SSH host alias #45, Remapping hostname prevents usinggh stack submit#337 — same code area (deriving the API repo from remote URLs) tripping on SSH aliases / hostname remaps.origindoubly wrong when pushes go elsewhere.