Referencing the upstream/remote PR branch
Rob-Hague opened this issue · 3 comments
Describe the feature or problem you’d like to solve
Often I want to checkout a PR with gh pr checkout NNN
and then reference the upstream/"remote-tracking branch" in some way, e.g. git log @{u}
or git reset @{u}
. Unfortunately, because of the way the branch is configured, I cannot refer to the upstream branch in this way. The error is something like "fatal: upstream branch 'refs/heads/XXX' not stored as a remote-tracking branch".
Proposed solution
I don't have a great solution in mind. The cli currently configures the branch like so:
branch.<name>.remote=<fork url>
branch.<name>.pushremote=<fork url>
branch.<name>.merge=refs/heads/XXX
My first thought was just to set branch.<name>.remote=upstream
and branch.<name>.merge=refs/pull/NNN/head
, but it seems one cannot push to refs/pull/*.
Then I thought to add back branch.<name>.pushremote=<fork url>
but AFAICT there exists no branch-level config for defining the refspec to push to, i.e. a push version of branch.<name>.merge
. (We want to fetch from refs/pull/NNN/head
at the defined upstream
remote but push to refs/heads/XXX
at the fork)
So I think having a remote-tracking branch basically requires a remote to be defined, which the cli currently doesn't do for PRs. That's nice because it keeps the remotes clean but personally I would prefer (in the absence of a better solution) if the cli defined a new remote where necessary so that I can have a remote-tracking branch for a PR and refer to it with @{u}
.
Additional context
There is a workaround which is to use FETCH_HEAD
, i.e. git reset FETCH_HEAD
. But one must make sure to do git fetch
beforehand when switching regularly between branches.
@Rob-Hague : Thank you for opening this issue! ❤ I'll admit I also don't have a great solution in mind just yet.
There's 2 things I'd like to do to investigate this further:
- Understand how
git
is treating@{u}
relative tobranch.<name>.remote=upstream
and whether this only works on repository remotes forgit log
and other commands. - Bring this back to the team to discuss how additional remotes may affect other
git
and/orgh
operations.
At the least, this could be a flag or configuration someone elects to turn on assuming no glaring issues. 🤔
Notes
branch.<name>.remote
documentation
branch.<name>.remote
When on branch
<name>
, it tellsgit fetch
andgit push
which remote to fetch from or push to. The remote to push to may be overridden withremote.pushDefault
(for all branches). The remote to push to, for the current branch, may be further overridden bybranch.<name>.pushRemote
. If no remote is configured, or if you are not on any branch and there is more than one remote defined in the repository, it defaults to origin for fetching andremote.pushDefault
for pushing. Additionally,.
(a period) is the current local repository (a dot-repository), seebranch.<name>.merge
's final note below.
The salient bit being "it tells git fetch
and git push
which remote to fetch from or push to" makes me wonder if this is a use case for git
to expand this to other commands.
remote_for_branch(...)
logic in git
At a glance, it seems the blast radius of the configuration above is fairly limited, however one particular bit of code is ref-filter.c
saying only local branches may have an upstream:
else if (atom_type == ATOM_UPSTREAM) {
const char *branch_name;
/* only local branches may have an upstream */
if (!skip_prefix(ref->refname, "refs/heads/",
&branch_name)) {
v->s = xstrdup("");
continue;
}
branch = branch_get(branch_name);
refname = branch_get_upstream(branch, NULL);
if (refname)
fill_remote_ref_details(atom, refname, branch, &v->s);
else
v->s = xstrdup("");
continue;
Just going back in time to confirm we've been consistent with branch-specific remotes and seeing the design considerations of the time (no issue behind this unfortunately) 🤔
@Rob-Hague : While we figure this out, have you tried manually adding remotes for the repo and modifying the branches to use said remote? seeing how it affected other operations?
Thanks for looking into it. Here are some scenarios (run from my fork of https://github.com/sshnet/SSH.NET)
1.
gh pr checkout 1392
# output
From https://github.com/sshnet/SSH.NET
* [new ref] refs/pull/1392/head -> nullable
Switched to branch 'nullable'
# output from git config --list
branch.nullable.remote=https://github.com/mus65/SSH.NET.git
branch.nullable.pushremote=https://github.com/mus65/SSH.NET.git
branch.nullable.merge=refs/heads/nullable
git log @{u}
does not work here
2.
gh pr checkout 1392
git remote add mus65 https://github.com/mus65/SSH.NET.git
git config branch.nullable.remote mus65
git fetch
# output from git config --list
branch.nullable.remote=mus65
branch.nullable.pushremote=https://github.com/mus65/SSH.NET.git
branch.nullable.merge=refs/heads/nullable
Now git log @{u}
works as desired here
3.
git remote add mus65 https://github.com/mus65/SSH.NET.git
gh pr checkout 1392
# output
From https://github.com/mus65/SSH.NET
* [new branch] nullable -> mus65/nullable
Switched to a new branch 'nullable'
branch 'nullable' set up to track 'mus65/nullable'.
# output from git config --list
branch.nullable.remote=mus65
branch.nullable.merge=refs/heads/nullable
git log @{u}
works as desired here.
Actually I didn't realise it works in (3) i.e. when the remote is preexisting. Still, it would be nice if the cli could add the remote for me, assuming that one cannot achieve the ideal scenario:
gh pr checkout 1392
# output from git config --list
branch.nullable.remote=upstream
branch.nullable.merge=refs/pull/1392/head
branch.nullable.pushremote=https://github.com/mus65/SSH.NET.git
branch.nullable.???push???=refs/heads/nullable