You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Detect push target for local branches without upstream configuration #575
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.)
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).
Reacted by Mislav Marohnić, bzub, Frederick Zhang and Geoffrey Martin-Noble
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.
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:
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).
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)
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.
Givengit 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
Givengit 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
Good callout, @gibfahn. If not provided with the --repo flag, the base branch is currently determined by the following:
upstream
github
origin
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 👍
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.
Givengit 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.
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.
Givengit 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.
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?
Givengit 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
Givengit 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
Givengit 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.
Givengit 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
Givengit 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
Givengit 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
Givengit 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
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:
Givengit 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:
Givengit 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
Givengit 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
Givengit 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!
Activity
phil-blain commentedon Aug 3, 2020
Hi!
Ideally, the first step would be to use the existing Git logic for triangular workflows, see:
remote.pushDefault,branch.<name>.pushRemote,@{push}revision syntax.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).
phil-blain commentedon Oct 12, 2020
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:
So it enables a pretty cool workflow in my opinion, which feels really "natural".
So it would be great if
ghcould understand this workflow a little bit more. For example, I don't wantghto dogit push -u origin my-featurewhen I open a pull request, because this defeats the purpose of the above configs; the "upstream" should stayupstream/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
mwaddell commentedon Mar 12, 2022
This makes
gh pr statususeless in github-actions - see pangaeatech/azure-test-deployment#8 (comment)Is there a way to specify the PR URL when calling this?
mwaddell commentedon Mar 22, 2022
Is there a way to explicitly set the upstream config from github actions as a workaround for this?
stephanedaviet commentedon Jun 22, 2022
Maybe related (or not),
gh pr createseems 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:gh pr creategives 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)
mislav commentedon Jun 22, 2022
@stephanedaviet I would say it's related. To work around this, you can say:
gibfahn commentedon Jun 27, 2022
Worth noting that git itself tells you not to use such a configuration (e.g. from
git help remote):(not saying
ghshouldn't support it, just noting that it's not recommended)stephanedaviet commentedon Jun 27, 2022
Good catch! It will make me re-examine my practice.
sudddy commentedon Mar 15, 2023
@mislav I tried the same. Still couldn't create the pr. :'(
Find PRs using @{push}
Find PRs using @{push}
5 remaining items
jtmcg commentedon Feb 4, 2025
Expected Outcomes
gh pr createsupports triangular workflows assembled by remote.pushDefault, @{push} revision syntax,and branch..pushRemote, in that order of precedence (see ParsePRRefs, which adds support for this for other
gh prsubcommands)gh pr statuscommandAcceptance Criteria
Given
git rev-parse --abbrev-ref @{push}resolveswhen I run
gh pr createthen I should create a pr with the headRef pointing at the
@[push}return valueGiven the
branch.<branchName>.pushremotekey is set on my current branchwhen I run
gh pr createthen I should create a pr with the headRef pointing at the remote value in the
branch.<branchName>.pushremotekey and the current branch nameGiven the
remote.pushDefaultkey is set on my current repowhen I run
gh pr createthen I should create a pr with the headRef pointing at the remote value in the
remote.pushDefaultkey and the current branch nameGiven
git rev-parse --abbrev-ref @{push}resolvesand/or given the
branch.<branchName>.pushremotekey is set on my current branchand/or given the
remote.pushDefaultkey is set on my current repowhen I run
gh pr createthen I should create a pr with the headRef pointing at the given values in the following priority order:
@[push}->branch.<branchName>.pushremote->remote.pushDefaultgibfahn commentedon Feb 13, 2025
Two things that I use that I'm not sure are covered in the above criteria:
@{upstream}and@{push}are both set, the PR branch should be@{push}and the target branch should be@{upstream}e.g.
The
GH_HOSTshould be set according to the@{push}and@{up}configured for the current branch.jtmcg commentedon Feb 13, 2025
Good callout, @gibfahn. If not provided with the
--repoflag, the base branch is currently determined by the following:upstreamgithuboriginOnce 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 👍williammartin commentedon Mar 11, 2025
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 resolveAnd Given the
branch.<branchName>.pushremotekey is set on my current branchWhen I run
gh pr createThen I should create a pr with the headRef pointing at the remote value in the
branch.<branchName>.pushremotekey and the current branch nameOf course, please tell me if I've misunderstood, otherwise my understanding of the current A/C is incorrect.
jtmcg commentedon Mar 11, 2025
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,
54 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.
williammartin commentedon Mar 11, 2025
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}resolveswhen I run
gh pr createthen I should create a pr with the headRef pointing at the
@[push}return valueGiven
git rev-parse --abbrev-ref @{push}does not resolveand given the
branch.<branchName>.pushremotekey is set on my current branchwhen I run
gh pr createthen I should create a pr with the headRef pointing at the remote value in the
branch.<branchName>.pushremotekey and the current branch nameGiven
git rev-parse --abbrev-ref @{push}does not resolveand given the
branch.<branchName>.pushremotekey is not set on my current branchand given the
remote.pushDefaultkey is set on my current repowhen I run
gh pr createthen I should create a pr with the headRef pointing at the remote value in the
remote.pushDefaultkey and the current branch nameThanks!
Edit: Happy to take this meta conversation elsewhere if you'd prefer.
jtmcg commentedon Mar 11, 2025
Yeah, you're almost there, @williammartin:
Given
git rev-parse --abbrev-ref @{push}resolvesand given the
branch.<branchName>.pushremotekey is set on my current branchand given the
remote.pushdefaultkey is set for my repowhen I run
gh pr createthen I should create a pr with the headRef pointing at the
@{push}return valueGiven
git rev-parse --abbrev-ref @{push}resolvesand given the
branch.<branchName>.pushremotekey is set on my current branchwhen I run
gh pr createthen I should create a pr with the headRef pointing at the
@{push}return valueGiven
git rev-parse --abbrev-ref @{push}does resolveand given the
remote.pushdefaultkey is set for my repowhen I run
gh pr createthen I should create a pr with the headRef pointing at the
@{push}return valueGiven
git rev-parse --abbrev-ref @{push}does NOT resolveand given the
branch.<branchName>.pushremotekey is set on my current branchand given the
remote.pushdefaultkey is set for my repowhen I run
gh pr createthen I should create a pr with the headRef pointing at the remote value in the
branch.<branchName>.pushremotekey and the current branch namewilliammartin commentedon Mar 11, 2025
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. Ifbranch.<branchName>.pushremotekey is set on my current branch then it doesn't matter whetherremote.pushDefaultis set.In my mind:
Given
git rev-parse --abbrev-ref @{push}resolveswhen I run
gh pr createthen I should create a pr with the headRef pointing at the
@[push}return valueCovers:
Given
git rev-parse --abbrev-ref @{push}resolvesand given the
branch.<branchName>.pushremotekey is set on my current branchand given the
remote.pushdefaultkey is set for my repowhen I run
gh pr createthen I should create a pr with the headRef pointing at the
@{push}return valueGiven
git rev-parse --abbrev-ref @{push}resolvesand given the
branch.<branchName>.pushremotekey is set on my current branchwhen I run
gh pr createthen I should create a pr with the headRef pointing at the
@{push}return valueGiven
git rev-parse --abbrev-ref @{push}does resolveand given the
remote.pushdefaultkey is set for my repowhen I run
gh pr createthen I should create a pr with the headRef pointing at the
@{push}return valueThanks for the discussion, no changes required. We're on the same page about the behaviour itself!
williammartin commentedon Mar 11, 2025
Misclick sorry!
jtmcg commentedon Mar 11, 2025
Ah, I see what you mean, @williammartin! That would have been a better way to write it... Thanks for the discussion!