Skip to content

Detect push target for local branches without upstream configuration #575

Closed

Description

@mislav

Commands such as pr status inspect the current branch and check its upstream configuration to see where it's pushed while resolving the branch to the associated PR.

Some branches might be pushed but have no upstream configuration (simple git push <remote> HEAD withough -u). Right now mechanisms such as prSelectorForCurrentBranch() don't recognize such branches as being published to a remote. We should perhaps iterate over all remotes and assume one to be a push target if we find a same-named tracking branch on one. (This will perhaps be prone to false-positives, but it's a marked improvement over current behavior.)

Reported by @rista404 #567 (comment)

Activity

  1. added
    priority-3Affects a small number of users or is largely cosmetic
    on Mar 3, 2020
  2. phil-blain commented on Aug 3, 2020

    @phil-blain

    Hi!

    Ideally, the first step would be to use the existing Git logic for triangular workflows, see:

    These configurations are unfortunately under-used because under-advertised (and are missing command-line options) but are specifically geared toward triangular workflows (i.e. GitHub-style forking workflows).

  3. phil-blain commented on Oct 12, 2020

    @phil-blain

    Adding a little more details:

    The way I configure my feature branches is that they track the upstream branch in which they will eventually be integrated, and I push them to my own fork. Basically the configs above enable a workflow like this:

    # first time only: setup 'origin' as the default push remote (I usually fork on GitHub, then clone my fork)
    git config [--global] remote.pushDefault origin
    # first time only: change default push behaviour to 'current' 
    git config [--global] push.default current
    
    # create a feature branch
    git fetch upstream
    git checkout -b my-feature upstream/master
    
    # work work work
    
    # push my branch to my fork
    git push # no other arguments needed ! (does `git push origin my-feature`)
    
    # work work work
    
    # rebase on latest master
    git fetch # no other arguments needed!  (does `git fetch upstream`)
    git rebase # no other arguments needed! (does `git rebase upstream/master`)
    # or in one step:
    git pull --rebase  # (does `git pull --rebase upstream master`)
    
    # or if your project's workflow uses merge:
    git fetch # no other arguments needed!
    git merge # no other arguments needed! (does `git merge upstream/master`)
    # or in one step:
    git pull # (does `git pull upstream master`)

    So it enables a pretty cool workflow in my opinion, which feels really "natural".

    So it would be great if gh could understand this workflow a little bit more. For example, I don't want gh to do git push -u origin my-feature when I open a pull request, because this defeats the purpose of the above configs; the "upstream" should stay upstream/master.

    EDIT: here is a GitHub blog post dating from the release of Git 2.5, describing this workflow: https://github.blog/2015-07-29-git-2-5-including-multiple-worktrees-and-triangular-workflows/#improved-support-for-triangular-workflows

  4. mwaddell commented on Mar 12, 2022

    @mwaddell

    This makes gh pr status useless in github-actions - see pangaeatech/azure-test-deployment#8 (comment)

    Is there a way to specify the PR URL when calling this?

  5. mwaddell commented on Mar 22, 2022

    @mwaddell

    Is there a way to explicitly set the upstream config from github actions as a workaround for this?

  6. stephanedaviet commented on Jun 22, 2022

    @stephanedaviet

    Maybe related (or not), gh pr create seems not to work in a fork configuration with only one remote configured and a fetch/push strategy to pull from upstream and push to fork. For instance:

    origin  [email protected]:cli/cli.git (fetch)
    origin [email protected]:org-where-forked/cli.git (push)
    

    gh pr create gives then the following error: pull request create failed: GraphQL: Head sha can't be blank, Base sha can't be blank, No commits between <target-branch> and <current-branch>, Head ref must be a branch (createPullRequest).

    gh version 2.11.3 (2022-05-25)

  7. mislav commented on Jun 22, 2022

    @mislav
    ContributorAuthor

    @stephanedaviet I would say it's related. To work around this, you can say:

    gh pr create -H org-where-forked:<branch>
    
  8. gibfahn commented on Jun 27, 2022

    @gibfahn

    Worth noting that git itself tells you not to use such a configuration (e.g. from git help remote):

    Note that the push URL and the fetch URL, even though they can be set differently, must still refer to the same place. What you pushed to the push URL should be what you would see if you immediately fetched from the fetch URL. If you are trying to fetch from one place (e.g. you're upstream) and push to another (e.g. your publishing repository), use two separate remotes.

    (not saying gh shouldn't support it, just noting that it's not recommended)

  9. stephanedaviet commented on Jun 27, 2022

    @stephanedaviet

    Worth noting that git itself tells you not to use such a configuration (e.g. from git help remote):

    Note that the push URL and the fetch URL, even though they can be set differently, must still refer to the same place. What you pushed to the push URL should be what you would see if you immediately fetched from the fetch URL. If you are trying to fetch from one place (e.g. you're upstream) and push to another (e.g. your publishing repository), use two separate remotes.

    (not saying gh shouldn't support it, just noting that it's not recommended)

    Good catch! It will make me re-examine my practice.

  10. sudddy commented on Mar 15, 2023

    @sudddy

    @mislav I tried the same. Still couldn't create the pr. :'(

  11. added
    gh-prrelating to the gh pr command
    on Oct 2, 2023
  12. 5 remaining items

  13. jtmcg commented on Feb 4, 2025

    @jtmcg
    Contributor

    Expected Outcomes

    Acceptance Criteria

    Given git rev-parse --abbrev-ref @{push} resolves
    when I run gh pr create
    then I should create a pr with the headRef pointing at the @[push} return value

    Given the branch.<branchName>.pushremote key is set on my current branch
    when I run gh pr create
    then I should create a pr with the headRef pointing at the remote value in the branch.<branchName>.pushremote key and the current branch name

    Given the remote.pushDefault key is set on my current repo
    when I run gh pr create
    then I should create a pr with the headRef pointing at the remote value in the remote.pushDefault key and the current branch name

    Given git rev-parse --abbrev-ref @{push} resolves
    and/or given the branch.<branchName>.pushremote key is set on my current branch
    and/or given the remote.pushDefault key is set on my current repo
    when I run gh pr create
    then I should create a pr with the headRef pointing at the given values in the following priority order: @[push} -> branch.<branchName>.pushremote -> remote.pushDefault

  14. gibfahn commented on Feb 13, 2025

    @gibfahn

    Two things that I use that I'm not sure are covered in the above criteria:

    1. If the @{upstream} and @{push} are both set, the PR branch should be @{push} and the target branch should be @{upstream}
    2. This should work even if my git repo has multiple github / GHE instances configured as different remotes.

    e.g.

    ❯ git remote -v
    up	https://github.mycorp.com/myorg/repo (fetch)
    up	https://github.mycorp.com/myorg/repo (push)
    fork	https://github.mycorp.com/me/repo.git (fetch)
    fork	https://github.mycorp.com/me/repo.git (push)
    pub	https://github.com/myorg/repo (fetch)
    pub	https://github.com/myorg/repo (push)
    pubfork	https://github.com/me/repo (fetch)
    pubfork	https://github.com/me/repo (push)

    The GH_HOST should be set according to the @{push} and @{up} configured for the current branch.

  15. jtmcg commented on Feb 13, 2025

    @jtmcg
    Contributor

    Two things that I use that I'm not sure are covered in the above criteria:

    1. If the @{upstream} and @{push} are both set, the PR branch should be @{push} and the target branch should be @{upstream}
    2. This should work even if my git repo has multiple github / GHE instances configured as different remotes.

    e.g.

    ❯ git remote -v
    up https://github.mycorp.com/myorg/repo (fetch)
    up https://github.mycorp.com/myorg/repo (push)
    fork https://github.mycorp.com/me/repo.git (fetch)
    fork https://github.mycorp.com/me/repo.git (push)
    pub https://github.com/myorg/repo (fetch)
    pub https://github.com/myorg/repo (push)
    pubfork https://github.com/me/repo (fetch)
    pubfork https://github.com/me/repo (push)
    The GH_HOST should be set according to the @{push} and @{up} configured for the current branch.

    Good callout, @gibfahn. If not provided with the --repo flag, the base branch is currently determined by the following:

    1. upstream
    2. github
    3. origin
    4. Unstable list of other remotes

    Once these updates are in, I think that we should be able to add @{upstream} support fairly easily (famous last words, though 😅 ). It's probably worth capturing this in another issue for follow up. I'll try to do that once we get this change in 👍

  16. self-assigned this
    on Feb 20, 2025
  17. williammartin commented on Mar 11, 2025

    @williammartin
    Member

    Just as a bit of feedback @jtmcg, I suspect the last section of A/C would be more clearly captured if it were integrated into the previous sections as a given e.g.

    Given git rev-parse --abbrev-ref @{push} does not resolve
    And Given the branch.<branchName>.pushremote key is set on my current branch
    When I run gh pr create
    Then I should create a pr with the headRef pointing at the remote value in the branch.<branchName>.pushremote key and the current branch name

    Of course, please tell me if I've misunderstood, otherwise my understanding of the current A/C is incorrect.

  18. jtmcg commented on Mar 11, 2025

    @jtmcg
    Contributor

    Just as a bit of feedback @jtmcg, I suspect the last section of A/C would be more clearly captured if it were integrated into the previous sections as a given e.g.

    Given git rev-parse --abbrev-ref @{push} does not resolve
    And Given the branch.<branchName>.pushremote key is set on my current branch
    When I run gh pr create
    Then I should create a pr with the headRef pointing at the remote value in the branch.<branchName>.pushremote key and the current branch name

    Of course, please tell me if I've misunderstood, otherwise my understanding of the current A/C is incorrect.

    That is one of the scenarios this AC is meant to capture. There are additional scenarios as well. This is really meant to establish the priority order of the different remote resolutions. I think it turns into, like, 5 4 more explicitly enumerated AC were I to write them all out.

    Sounds like there would have been value in doing so, though, to avoid your confusion.

  19. williammartin commented on Mar 11, 2025

    @williammartin
    Member

    I think it turns into, like, 5 more explicitly enumerated AC were I to write them all out.

    Not following this sorry, which makes me think I'm missing something. What would be missing from the following A/C?

    Given git rev-parse --abbrev-ref @{push} resolves
    when I run gh pr create
    then I should create a pr with the headRef pointing at the @[push} return value

    Given git rev-parse --abbrev-ref @{push} does not resolve
    and given the branch.<branchName>.pushremote key is set on my current branch
    when I run gh pr create
    then I should create a pr with the headRef pointing at the remote value in the branch.<branchName>.pushremote key and the current branch name

    Given git rev-parse --abbrev-ref @{push} does not resolve
    and given the branch.<branchName>.pushremote key is not set on my current branch
    and given the remote.pushDefault key is set on my current repo
    when I run gh pr create
    then I should create a pr with the headRef pointing at the remote value in the remote.pushDefault key and the current branch name

    Thanks!

    Edit: Happy to take this meta conversation elsewhere if you'd prefer.

  20. jtmcg commented on Mar 11, 2025

    @jtmcg
    Contributor

    Yeah, you're almost there, @williammartin:

    Given git rev-parse --abbrev-ref @{push} resolves
    and given the branch.<branchName>.pushremote key is set on my current branch
    and given the remote.pushdefault key is set for my repo
    when I run gh pr create
    then I should create a pr with the headRef pointing at the @{push} return value

    Given git rev-parse --abbrev-ref @{push} resolves
    and given the branch.<branchName>.pushremote key is set on my current branch
    when I run gh pr create
    then I should create a pr with the headRef pointing at the @{push} return value

    Given git rev-parse --abbrev-ref @{push} does resolve
    and given the remote.pushdefault key is set for my repo
    when I run gh pr create
    then I should create a pr with the headRef pointing at the @{push} return value

    Given git rev-parse --abbrev-ref @{push} does NOT resolve
    and given the branch.<branchName>.pushremote key is set on my current branch
    and given the remote.pushdefault key is set for my repo
    when I run gh pr create
    then I should create a pr with the headRef pointing at the remote value in the branch.<branchName>.pushremote key and the current branch name

  21. williammartin commented on Mar 11, 2025

    @williammartin
    Member

    Thanks.

    I guess to me those seem implied. Maybe that's a mistake? Like, if git rev-parse --abbrev-ref @{push} resolves then other configuration doesn't matter. If branch.<branchName>.pushremote key is set on my current branch then it doesn't matter whether remote.pushDefault is set.

    In my mind:

    Given git rev-parse --abbrev-ref @{push} resolves
    when I run gh pr create
    then I should create a pr with the headRef pointing at the @[push} return value

    Covers:

    Given git rev-parse --abbrev-ref @{push} resolves
    and given the branch.<branchName>.pushremote key is set on my current branch
    and given the remote.pushdefault key is set for my repo
    when I run gh pr create
    then I should create a pr with the headRef pointing at the @{push} return value

    Given git rev-parse --abbrev-ref @{push} resolves
    and given the branch.<branchName>.pushremote key is set on my current branch
    when I run gh pr create
    then I should create a pr with the headRef pointing at the @{push} return value

    Given git rev-parse --abbrev-ref @{push} does resolve
    and given the remote.pushdefault key is set for my repo
    when I run gh pr create
    then I should create a pr with the headRef pointing at the @{push} return value

    Thanks for the discussion, no changes required. We're on the same page about the behaviour itself!

  22. williammartin commented on Mar 11, 2025

    @williammartin
    Member

    Misclick sorry!

  23. jtmcg commented on Mar 11, 2025

    @jtmcg
    Contributor

    Ah, I see what you mean, @williammartin! That would have been a better way to write it... Thanks for the discussion!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't workinggh-prrelating to the gh pr commandpriority-3Affects a small number of users or is largely cosmetic

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions