镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

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

@Joibel

Summary

In a clone with multiple remotes, every gh stack PR/stack API operation is bound to the repository of the origin remote, regardless of:

  • the --remote flag (which only changes the push target),
  • gh repo set-default / remote.<name>.gh-resolved (the standard gh CLI repo resolution),
  • the repository field in .git/gh-stack (editing it has no effect on subsequent runs).

If origin points at a different repository than the one your branches and PRs live in — the completely standard fork workflow where origin is upstream and you push to your fork — gh stack submit pushes 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 createPullRequest failed. If upstream had happened to contain same-named branches, gh stack submit --auto would have opened a pile of PRs against a repository I never pointed it at. For agent/scripted use this is a dangerous default.

Environment

  • gh version 2.96.0
  • gh-stack v0.1.0
  • Clone with 7 remotes; 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-workflows is set (remote.joibel.gh-resolved = base)

Steps to reproduce

  1. Clone a repo with origin pointing at upstream and a second remote pointing at your fork.
  2. Create branches with open PRs on the fork (head and base both on the fork).
  3. gh stack init --base <trunk> <branch1> ... <branchN> — adopts the branches fine, but records "repository": "github.com:<origin-owner>/<repo>" in .git/gh-stack.
  4. gh stack submit --remote <fork-remote> --auto

Observed

  • Push goes to the fork remote correctly ("Pushing to joibel... ✓ Pushed and synced 8 branches").

  • All PR operations target the origin repository: existing fork PRs are not detected, and the extension attempts createPullRequest against upstream, failing with:

    ⚠ failed to create PR for <branch>: creating PR: GraphQL: Head sha can't be blank,
    Base sha can't be blank, No commits between <base> and <branch>,
    Head ref must be a branch, Base ref must be a branch (createPullRequest)
    

    (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-workflows even when invoked with --remote joibel.

  • None of the following change the target repository: --remote, gh repo set-default, editing the repository field in .git/gh-stack.

Workaround

GH_REPO=<fork-owner>/<repo> gh stack submit --remote <fork-remote> --auto works perfectly — existing PRs are detected ("PR #NN for is up to date") and the stack is created.

Expected

  • Repo resolution should follow the standard gh CLI rules (GH_REPO, then gh-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.
  • If resolution is ambiguous in non-interactive mode, fail with an explicit "multiple remotes, specify GH_REPO or --remote" error rather than silently picking origin.
  • gh stack init should surface/validate which repository it bound the stack to.

Related

Activity

  1. flynnjustin24 commented on Jul 31, 2026

    @flynnjustin24

    Flynnjustin24

  2. added
    bugBug Reports
    topic: cli - submit`gh stack submit`: remote/SSH handling, config flags, draft PRs, and submit UX or failures.
    on Aug 24, 2026
  3. asinghalBloxer commented on Sep 8, 2026

    @asinghalBloxer

    Still reproducible in v0.1.1

    Setup: origin = upstream org repo fork = personal fork. Stack configured with gh-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 branches
    

    Push succeeds to the fork remote, but createPullRequest GraphQL mutation fails because gh stack resolves the API target from origin and sends headRefName: "fix-xyz" without the fork owner prefix — GitHub looks for the branch in the upstream org repo where it doesn't exist.

    GH_DEBUG=1 confirms two separate graphql calls: one resolves the stack state against origin, the push goes to fork, then PR creation targets origin with 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.

  4. webbson commented on Sep 25, 2026

    @webbson

    Same bug on v0.1.1 (gh 2.97.0), in the other common fork layout: origin is the fork and upstream is the template it was forked from. Three things this adds to the report:

    1. The repo is picked by remote name, and upstream wins over origin. Config.Repo() calls go-gh's repository.Current(), which takes the first GitHub remote after sorting by remoteNameSortScore (upstream 3, github 2, origin 1). It never reads remote.<name>.gh-resolved. So with

    origin    https://github.com/myorg/app.git        (fetch/push)
    upstream  https://github.com/myorg/template.git   (fetch/push)
    

    plus gh repo set-default myorg/app and git config remote.pushDefault origin, every gh stack API call goes to myorg/template. gh pr view resolves myorg/app correctly in the same checkout.

    2. Colliding PR numbers make branches look merged, and sync --prune could then delete live branches. The template had merged PRs #115/#116, and the app had open PRs #115/#116 for the stacked branches.

    Because those branches read as merged, gh stack sync --prune would delete local branches that still have open PRs. I didn't run it to confirm.

    3. submit --auto exits 0 when PR creation fails. It pushed to origin correctly, 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 116 created the stack, and GH_REPO=myorg/app gh stack view --json then shows the right, open PRs.

    Suggested fix: resolve the repo the way gh does. That means GH_REPO, then the gh-resolved remote, then the remote selected by --remote / remote.pushDefault, and only then the name heuristic. Also, submit should exit non-zero when any PR create fails. Related: #496, #509.

  5. webbson commented on Sep 25, 2026

    @webbson

    Follow-up to my comment above (same setup: origin = myorg/app, upstream = myorg/template, v0.1.1). The GH_REPO workaround 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 tracked pullRequest is merged, because "the merged state is final". One gh stack run without GH_REPO stores myorg/template's merged #115/#116 in .git/gh-stack for the branches. After that, GH_REPO=myorg/app gh stack view --json also shows those merged template PRs. It did so both with GH_REPO as a prefix and with export GH_REPO=…. Right after a GH_REPO=… gh stack link the 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. Here myorg/template also had a merged #117, so gh stack merge 117 would have acted on it.

    What worked for merging: gh pr merge refuses 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 repository field that init writes into .git/gh-stack is never read, as the original report says. So running init with GH_REPO set doesn't help either.

  6. mskonovalov commented on Sep 30, 2026

    @mskonovalov

    Another reproduction on v0.1.0, with the layout from the original report: origin is the upstream repo (umputun/revmux) and a second remote fork is my fork (mskonovalov/revmux), where every stack branch lives. gh repo set-default mskonovalov/revmux was set. gh stack submit pushed to the fork but tried to create the PRs against umputun/revmux.

    Prefixing the command with GH_REPO=mskonovalov/revmux created the stack on the fork. Given the stale-state issue described above, agents in this layout need the prefix on every gh stack command, not only submit. 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.

  7. added a commit that references this issue on Oct 11, 2026
    9c2aaca
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugBug Reportstopic: cli - submit`gh stack submit`: remote/SSH handling, config flags, draft PRs, and submit UX or failures.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions