From: Felipe Contreras <hidden> Date: 2016-06-15 22:57:16
Hi,
After thinking a bit more about, I think the current 'upstream' branch serves
most of the purposes; actually tracks an upstream branch; makes sense to rebase
onto that, makes sense to fetch from that remote, merge, and pull.
The only job it doesn't make sense to use the 'upstream' branch for is to push,
so here's a new notion of 'downstream' branch.
These patches are only exploratory, 'git branch -v' doesn't show these
branches, there's no @{downstream}, no documentation, and there isn't even a
way to set it.
If there's no downstream branch configured, nothing changes.
Felipe Contreras (3):
remote: don't override default if cur head remote is '.'
pull: trivial cleanups
push: add separate 'downstream' branch
builtin/push.c | 65 ++++++++++++++++++++++++++++++++++++----------------------
git-pull.sh | 15 +++++++++-----
remote.c | 10 ++++++---
remote.h | 3 +++
4 files changed, 61 insertions(+), 32 deletions(-)
--
1.8.3.rc1.579.g184e698
From: Felipe Contreras <hidden> Date: 2016-06-15 22:57:16
'git fetch .' makes no sense, so let's keep using the default ('origin')
when the current branch's remote is '.'.
Also update 'git pull' so that pulling works the same way as before.
Signed-off-by: Felipe Contreras <redacted>
---
git-pull.sh | 9 ++++++++-
remote.c | 2 +-
2 files changed, 9 insertions(+), 2 deletions(-)
@@ -220,7 +220,14 @@ test true = "$rebase" && {done}orig_head=$(gitrev-parse-q--verifyHEAD)-gitfetch$verbosity$progress$dry_run$recurse_submodules--update-head-ok"$@"||exit1+# get the default remote+remote="$(gitconfig"branch.$curr_branch_short.remote")"+iftest$#==0&&test-n"$remote"+then+gitfetch$verbosity$progress$dry_run$recurse_submodules--update-head-ok"$remote"||exit1+else+gitfetch$verbosity$progress$dry_run$recurse_submodules--update-head-ok"$@"||exit1+fitest-z"$dry_run"||exit0curr_head=$(gitrev-parse-q--verifyHEAD)
@@ -183,7 +181,7 @@ error_on_no_merge_candidates () {echo"You asked to pull from the remote '$1', but did not specify"echo"a branch. Because this is not the default configured remote"echo"for your current branch, you must specify a branch on the command line."-elif[-z"$curr_branch"-o-z"$upstream"];then+elif[-z"$curr_branch_short"-o-z"$upstream"];then.git-parse-remoteerror_on_missing_default_upstream"pull"$op_type$op_prep\"git pull <remote> <branch>"
From: Felipe Contreras <hidden> Date: 2016-06-15 22:57:16
It doesn't make sense to push to the upstream branch, so create new
configurations for the notion of 'downstream' branch, which is basically
the branch to push to by default.
The upstream branch is remote+merge, the downstream branch is
pushremote+push.
[branch "master"]
remote = origin
merge = refs/heads/master
pushremote = github
push = refs/heads/master
Signed-off-by: Felipe Contreras <redacted>
---
builtin/push.c | 65 ++++++++++++++++++++++++++++++++++++----------------------
remote.c | 8 ++++++--
remote.h | 3 +++
3 files changed, 50 insertions(+), 26 deletions(-)
@@ -99,8 +101,8 @@ static NORETURN int die_push_simple(struct branch *branch, struct remote *remoteadvice_maybe=_("\n""To choose either option permanently, ""see push.default in 'git help config'.");-die(_("The upstream branch of your current branch does not match\n"-"the name of your current branch. To push to the upstream branch\n"+die(_("The %s branch of your current branch does not match\n"+"the name of your current branch. To push to the %s branch\n""on the remote, use\n""\n"" git push %s HEAD:%s\n"
@@ -117,6 +120,8 @@ static void setup_push_upstream(struct remote *remote, int simple){structstrbufrefspec=STRBUF_INIT;structbranch*branch=branch_get(NULL);+constchar*related,*kind,*pushremote_name;+if(!branch)die(_("You are not currently on a branch.\n""To push the history leading to the current (detached HEAD)\n"
@@ -124,26 +129,38 @@ static void setup_push_upstream(struct remote *remote, int simple)"\n"" git push %s HEAD:<name-of-remote-branch>\n"),remote->name);-if(!branch->merge_nr||!branch->merge||!branch->remote_name)-die(_("The current branch %s has no upstream branch.\n"-"To push the current branch and set the remote as upstream, use\n"-"\n"-" git push --set-upstream %s %s\n"),-branch->name,-remote->name,-branch->name);-if(branch->merge_nr!=1)-die(_("The current branch %s has multiple upstream branches, "-"refusing to push."),branch->name);-if(strcmp(branch->remote_name,remote->name))-die(_("You are pushing to remote '%s', which is not the upstream of\n"++if(branch->push){+kind=_("downstream");+related=branch->push;+pushremote_name=branch->pushremote_name;+}else{+if(!branch->merge_nr||!branch->merge||!branch->remote_name)+die(_("The current branch %s has no upstream branch.\n"+"To push the current branch and set the remote as upstream, use\n"+"\n"+" git push --set-upstream %s %s\n"),+branch->name,+remote->name,+branch->name);+if(branch->merge_nr!=1)+die(_("The current branch %s has multiple upstream branches, "+"refusing to push."),branch->name);++kind=_("upstream");+related=branch->merge[0]->src;+pushremote_name=branch->remote_name;+}++if(strcmp(pushremote_name,remote->name))+die(_("You are pushing to remote '%s', which is not the %s of\n""your current branch '%s', without telling me what to push\n""to update which remote branch."),-remote->name,branch->name);-if(simple&&strcmp(branch->refname,branch->merge[0]->src))-die_push_simple(branch,remote);+remote->name,kind,branch->name);+if(simple&&strcmp(branch->refname,related))+die_push_simple(branch,remote,kind,related);-strbuf_addf(&refspec,"%s:%s",branch->name,branch->merge[0]->src);+strbuf_addf(&refspec,"%s:%s",branch->name,related);add_refspec(refspec.buf);}
Hm. Some thoughts:
fetch and push aren't symmetric. By default fetches are batched: when
you say 'git fetch', it fetches all the refs and uses the
remote.<name>.fetch refspec to update the refs on your side. Now, I
would argue that this is the correct design, because I rarely want to
fetch just one ref (and that is broken, by the way: refs on my side
aren't updated for some weird reason). The other reason this is
correct is because fetching has nothing to do with local branches: how
you _merge_ your remote branches to your local branches is entirely
orthogonal to the issue (and that is controlled by
branch.<name>.merge).
Now, push is a different ball game altogether. There are people who
do batched pushes (push.default = matching has been the default for 8
years now). However, the support for a batched push in a triangular
workflow is very limited: I can't say git push master hot-branch
pickaxe-doc, and expect them to go to the right remotes (this idea has
already been discussed and rejected). Back to your patch: if you want
to support batched pushes to map refs correctly, you should write a
patch for remote.<name>.push. It has a very valid usecase too: there
are people who use Gerrit and they shouldn't have to do git push
<name>:refs/for/<name> every single time. Neither should they have to
configure each branch.<name>.push. The ref-mapping is an inherent
property of the remote, not of the local branch. And
branch.<name>.merge is entirely orthogonal to ref-mapping, as I
already explained.
That said, I think the concept of a downstream can be useful. I have
branch.hot-branch.remote set to origin, and
branch.hot-branch.pushremote set to ram. Now, I push some changes: my
prompt still says > (indicating unpushed changes), and this is very
annoying. I would definitely like git to be able to recognize that
I'm ahead of upstream, but in-sync with my downstream. So, your
branch.<name>.push should probably be named
branch.<name>.downstreamref and be used only for informational
purposes (@{d} and git status)? Wait, why do we need it at all? Is
there something that we cannot infer from a proposed
remote.<name>.push? Why will we ever need to override that refspec
mapping with a local branch configuration?
This one obviously looks good. I'm not sure why you sent it along
with the other two patches though.
On a related note, I'm interested in fixing pull. What I have on paper so far:
- pull.condition = clean-worktree | ff-update | no-local-changes |
always | never
- pull.action = merge | rebase* | reset
- pull.resetType = soft | hard | merge | keep
- pull.autostash = true | false
(ff-update is satisfied when FETCH_HEAD is directly ahead of
refs/remotes/<branch>, while no-local-changes is satisfied when
FETCH_HEAD is directly ahead of refs/heads/<branch>)
Any clue how to design branch-specific pull configuration?
This one obviously looks good. I'm not sure why you sent it along
with the other two patches though.
Because this patch depends on the previous one.
On a related note, I'm interested in fixing pull. What I have on paper so far:
- pull.condition = clean-worktree | ff-update | no-local-changes |
always | never
- pull.action = merge | rebase* | reset
- pull.resetType = soft | hard | merge | keep
- pull.autostash = true | false
I don't understand that. But my only concern is that there's no way to
do something like:
% git fetch backup 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
--
Felipe Contreras
Hm. Some thoughts:
fetch and push aren't symmetric. By default fetches are batched: when
you say 'git fetch', it fetches all the refs and uses the
remote.<name>.fetch refspec to update the refs on your side. Now, I
would argue that this is the correct design, because I rarely want to
fetch just one ref (and that is broken, by the way: refs on my side
aren't updated for some weird reason). The other reason this is
correct is because fetching has nothing to do with local branches: how
you _merge_ your remote branches to your local branches is entirely
orthogonal to the issue (and that is controlled by
branch.<name>.merge).
Now, push is a different ball game altogether. There are people who
do batched pushes (push.default = matching has been the default for 8
years now).
And is going to change soon.
However, the support for a batched push in a triangular
workflow is very limited: I can't say git push master hot-branch
pickaxe-doc, and expect them to go to the right remotes (this idea has
already been discussed and rejected). Back to your patch: if you want
to support batched pushes to map refs correctly, you should write a
patch for remote.<name>.push. It has a very valid usecase too: there
are people who use Gerrit and they shouldn't have to do git push
<name>:refs/for/<name> every single time. Neither should they have to
configure each branch.<name>.push. The ref-mapping is an inherent
property of the remote, not of the local branch. And
branch.<name>.merge is entirely orthogonal to ref-mapping, as I
already explained.
That said, I think the concept of a downstream can be useful. I have
branch.hot-branch.remote set to origin, and
branch.hot-branch.pushremote set to ram. Now, I push some changes: my
prompt still says > (indicating unpushed changes), and this is very
annoying. I would definitely like git to be able to recognize that
I'm ahead of upstream, but in-sync with my downstream. So, your
branch.<name>.push should probably be named
branch.<name>.downstreamref and be used only for informational
purposes (@{d} and git status)?
That makes absolutely no sense.
[branch "master"]
remote = origin
merge = refs/heads/master
pushremote = github
downstreamref = refs/heads/whaaa:refs/heads/master
What is the point of 'refs/heads/whaaa'?
Wait, why do we need it at all? Is
there something that we cannot infer from a proposed
remote.<name>.push? Why will we ever need to override that refspec
mapping with a local branch configuration?
[branch "master"]
remote = origin
merge = refs/heads/master
pushremote = github
push = refs/heads/fc/master
[branch "fc/old-remote/hg"]
remote = .
merge = refs/heads/master
pushremote = github
push = refs/heads/fc/remote/hg
Tell me how you express that without 'remote.branch.push'.
Cheers.
--
Felipe Contreras
Your point being? How will this patch interact with push.default = matching?
quoted
branch.<name>.push should probably be named
branch.<name>.downstreamref and be used only for informational
purposes (@{d} and git status)?
That makes absolutely no sense.
I said downstreamref, not downstreamrefspec.
[branch "master"]
remote = origin
merge = refs/heads/master
pushremote = github
push = refs/heads/fc/master
[branch "fc/old-remote/hg"]
remote = .
merge = refs/heads/master
pushremote = github
push = refs/heads/fc/remote/hg
Tell me how you express that without 'remote.branch.push'.
[remote "origin"]
push = refs/heads/master:refs/heads/fc/master
[remote "."]
push = refs/heads/fc/old-remote/hg:refs/heads/fc/remote/hg
Advantage being you can do:
[remote "origin"]
push = refs/heads/*:refs/for/*
While you can't with branch.<name>.push.
Let's see:
% git checkout master
% git push
It will try to push to 'origin/fc/master' not 'github/fc/master',
which is what I intended.
% git checkout fc/old-remote/hg
% git push
It will try to push to 'fc/remote/hg' not 'github/fc/remote/hg', which
is what I intended.
Advantage being you can do:
[remote "origin"]
push = refs/heads/*:refs/for/*
While you can't with branch.<name>.push.
But I can do 'git push origin "refs/head/*:refs/heads/for/*"', not
that I've ever had the need to do something like that, so I don't
care.
--
Felipe Contreras
From: Felipe Contreras <hidden> Date: 2016-06-15 22:57:17
On Thu, May 16, 2013 at 4:34 AM, Ramkumar Ramachandra
[off-list ref] wrote:
Felipe Contreras wrote:
quoted
But my only concern is that there's no way to
do something like:
% git fetch backup 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
[remote "backup"]
fetch = refs/tags/*:refs/tags/*
fetch = refs/heads/*:refs/heads/*
What am I missing?
Actually trying that command:
% git fetch origin 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
fatal: Refusing to fetch into current branch
refs/heads/fc/fast-export/cleanup of non-bare repository
fatal: The remote end hung up unexpectedly
--
Felipe Contreras
% git fetch origin 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
fatal: Refusing to fetch into current branch
refs/heads/fc/fast-export/cleanup of non-bare repository
fatal: The remote end hung up unexpectedly
That's because your HEAD is pointing to something under refs/heads/*:
it would work otherwise. When I'm developing on my personal branch, I
sometimes do 'git fetch origin master:master +pu:pu +next:next', and
it works as expected. When on master branch, I can't git fetch origin
master:master and this is a cute safety feature.
Either way, I think it's a bad practice to fetch directly into
refs/heads/*: you should really be fetching to refs/remotes and then
merging your changes in. I do want shortcuts though, which is why I'm
interested in fixing pull: there is nothing to fix in fetch as far as
I'm concerned.
Major thinko. It should be:
[remote "github"]
push = refs/heads/master:refs/heads/fc/master
push = refs/heads/fc/old-remote/hg:refs/heads/fc/remote/hg
quoted
Advantage being you can do:
[remote "origin"]
push = refs/heads/*:refs/for/*
While you can't with branch.<name>.push.
But I can do 'git push origin "refs/head/*:refs/heads/for/*"', not
that I've ever had the need to do something like that, so I don't
care.
Isn't the entire point of this exercise getting git to dwim without
being explicit?
I don't care about it personally either, which is why I haven't
written a patch yet. However, there are users of Gerrit who would
appreciate this feature: in the remote.pushdefault thread, some people
requested this feature.
From: Felipe Contreras <hidden> Date: 2016-06-15 22:57:17
On Thu, May 16, 2013 at 4:54 AM, Ramkumar Ramachandra
[off-list ref] wrote:
Felipe Contreras wrote:
quoted
% git fetch origin 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
fatal: Refusing to fetch into current branch
refs/heads/fc/fast-export/cleanup of non-bare repository
fatal: The remote end hung up unexpectedly
That's because your HEAD is pointing to something under refs/heads/*:
it would work otherwise. When I'm developing on my personal branch, I
sometimes do 'git fetch origin master:master +pu:pu +next:next', and
it works as expected. When on master branch, I can't git fetch origin
master:master and this is a cute safety feature.
Now you know what's the problem.
Either way, I think it's a bad practice to fetch directly into
refs/heads/*: you should really be fetching to refs/remotes and then
merging your changes in. I do want shortcuts though, which is why I'm
interested in fixing pull: there is nothing to fix in fetch as far as
I'm concerned.
It doesn't matter if you think it's a bad practice or not, 'git push
--mirror' works, and it's possible to update a branch even if HEAD is
point to it. There should be a possibility to do the same with 'git
fetch'.
Right now the user is forced to do something like:
git checkout -q -b tmp &&
git fetch origin 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&
git checkout -q @{-1} &&
git branch -q -D tmp 2> /dev/null
Which doesn't seem to be quite right.
--
Felipe Contreras
It doesn't matter if you think it's a bad practice or not, 'git push
--mirror' works, and it's possible to update a branch even if HEAD is
point to it. There should be a possibility to do the same with 'git
fetch'.
So, introduce a configuration variable to turn off this safety feature?
Major thinko. It should be:
[remote "github"]
push = refs/heads/master:refs/heads/fc/master
push = refs/heads/fc/old-remote/hg:refs/heads/fc/remote/hg
Would I be able to do:
% git branch --set-upstream-to origin/master --set-downstream-to
github/fc/master
?
Would I see these branches when I do 'git branch -vv'?
Would I be able to do 'git push next@{downstream}'?
quoted
quoted
Advantage being you can do:
[remote "origin"]
push = refs/heads/*:refs/for/*
While you can't with branch.<name>.push.
But I can do 'git push origin "refs/head/*:refs/heads/for/*"', not
that I've ever had the need to do something like that, so I don't
care.
Isn't the entire point of this exercise getting git to dwim without
being explicit?
I don't care about it personally either, which is why I haven't
written a patch yet. However, there are users of Gerrit who would
appreciate this feature: in the remote.pushdefault thread, some people
requested this feature.
That is orthogonal to 'branch.A.push' the same way 'remote.B.fetch' is
orthogonal to 'branch.A.merge'.
--
Felipe Contreras
From: Felipe Contreras <hidden> Date: 2016-06-15 22:57:17
On Thu, May 16, 2013 at 5:18 AM, Ramkumar Ramachandra
[off-list ref] wrote:
Felipe Contreras wrote:
quoted
It doesn't matter if you think it's a bad practice or not, 'git push
--mirror' works, and it's possible to update a branch even if HEAD is
point to it. There should be a possibility to do the same with 'git
fetch'.
So, introduce a configuration variable to turn off this safety feature?
I thought you were interested in fixing 'git pull'. My bad.
--
Felipe Contreras
Would I be able to do:
% git branch --set-upstream-to origin/master --set-downstream-to
github/fc/master
?
Would I see these branches when I do 'git branch -vv'?
Would I be able to do 'git push next@{downstream}'?
Hm, losing this functionality in the name of generality would
certainly be very undesirable.
That is orthogonal to 'branch.A.push' the same way 'remote.B.fetch' is
orthogonal to 'branch.A.merge'.
Not at all (which is what I've been trying to say).
remote.<name>.fetch is operated on by fetch, while branch.<name>.merge
is operated on by merge; they are really orthogonal. What happens if
both branch.<name>.push and remote.<name>.push are set? What will
push do?
Perhaps we should get both, and get branch.<name>.push to override
remote.<name>.push. The issue being @{d} will not work if
remote.<name>.push is set. Then again, since we're targeting Gerrit
users here, I don't really think it's an issue: refs/for/master is not
really a "downstream branch"; it's a pseudo-ref that Gerrit handles
internally.
From: Antoine Pelisse <hidden> Date: 2016-06-15 22:57:17
On Thu, May 16, 2013 at 11:36 AM, Felipe Contreras
[off-list ref] wrote:
Actually trying that command:
% git fetch origin 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
fatal: Refusing to fetch into current branch
refs/heads/fc/fast-export/cleanup of non-bare repository
fatal: The remote end hung up unexpectedly
Can you try something like this instead ?
git fetch --update-head-ok # or -u
From: Felipe Contreras <hidden> Date: 2016-06-15 22:57:17
On Thu, May 16, 2013 at 6:31 AM, Ramkumar Ramachandra
[off-list ref] wrote:
Felipe Contreras wrote:
quoted
Would I be able to do:
% git branch --set-upstream-to origin/master --set-downstream-to
github/fc/master
?
Would I see these branches when I do 'git branch -vv'?
Would I be able to do 'git push next@{downstream}'?
Hm, losing this functionality in the name of generality would
certainly be very undesirable.
I don't even know what that means.
quoted
That is orthogonal to 'branch.A.push' the same way 'remote.B.fetch' is
orthogonal to 'branch.A.merge'.
Not at all (which is what I've been trying to say).
remote.<name>.fetch is operated on by fetch, while branch.<name>.merge
is operated on by merge; they are really orthogonal. What happens if
both branch.<name>.push and remote.<name>.push are set? What will
push do?
The same that 'git pull' does when both branch.<name>.merge and
remote.<name>.fetch are set.
Perhaps we should get both, and get branch.<name>.push to override
remote.<name>.push.
Does branch.<name>.merge overrides remote.<name>.fetch? No. They
complement each other.
The issue being @{d} will not work if
remote.<name>.push is set.
Of course it would work. Does @{u} stop working when remote.<name>.fetch is set?
Then again, since we're targeting Gerrit
users here, I don't really think it's an issue: refs/for/master is not
really a "downstream branch"; it's a pseudo-ref that Gerrit handles
internally.
From: Felipe Contreras <hidden> Date: 2016-06-15 22:57:17
On Thu, May 16, 2013 at 6:54 AM, Antoine Pelisse [off-list ref] wrote:
On Thu, May 16, 2013 at 11:36 AM, Felipe Contreras
[off-list ref] wrote:
quoted
Actually trying that command:
% git fetch origin 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
fatal: Refusing to fetch into current branch
refs/heads/fc/fast-export/cleanup of non-bare repository
fatal: The remote end hung up unexpectedly
Can you try something like this instead ?
git fetch --update-head-ok # or -u
Does branch.<name>.merge overrides remote.<name>.fetch? No. They
complement each other.
I often wonder if you are reading what you're responding to:
remote.<name>.fetch is operated on by fetch, while branch.<name>.merge
is operated on by merge; they are really orthogonal.
The same that 'git pull' does when both branch.<name>.merge and
remote.<name>.fetch are set.
Are you reading this?
git pull _fetches_ from remote.<name>.fetch and _merges_ from
branch.<name>.merge. What is "the same" in git push terms? It's a
simple question; which ref does push update: the one specified by
remote.<name>.push or branch.<name>.push?
Of course it would work. Does @{u} stop working when remote.<name>.fetch is set?
It doesn't work when _only_ remote.<name>.fetch is set: you need
branch.<name>.merge to determine @{u}, just like you would need
branch.<name>.push to determine @{d}.
It is a downstream branch.
Which commit does git show @{d} show you? There is no ref called
refs/for/master.
From: Felipe Contreras <hidden> Date: 2016-06-15 22:57:17
On Thu, May 16, 2013 at 8:52 AM, Ramkumar Ramachandra
[off-list ref] wrote:
Felipe Contreras wrote:
quoted
Does branch.<name>.merge overrides remote.<name>.fetch? No. They
complement each other.
I often wonder if you are reading what you're responding to:
And I wonder if you care if what you say is actually true.
remote.<name>.fetch is operated on by fetch, while branch.<name>.merge
is operated on by merge; they are really orthogonal.
You keep saying they are orthogonal, but they are not. Read
remote.c::branch_get().
And try this:
[remote "origin"]
url = git@github.com:felipec/git.git
# fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
Can you merge? No. Can you do master@{u}? No. Several other things don't work.
quoted
The same that 'git pull' does when both branch.<name>.merge and
remote.<name>.fetch are set.
Are you reading this?
git pull _fetches_ from remote.<name>.fetch and _merges_ from
branch.<name>.merge. What is "the same" in git push terms? It's a
simple question; which ref does push update: the one specified by
remote.<name>.push or branch.<name>.push?
[remote]
pushdefault = origin
[remote "origin"]
push = refs/head/*:refs/heads/for/*
[branch "master"]
pushremote = github
push = refs/heads/new-master
[branch "next"]
pushremote = origin
push = refs/heads/new-next
% git checkout master && git push
Where should it go? github/new-master. Obviously.
% git checkout next && git push
Where should it go? origin/new-next. Obviously.
% git checkout main && git push
Where should it go? origin/for/maint. Obviously.
I don't see what the big deal is.
quoted
Of course it would work. Does @{u} stop working when remote.<name>.fetch is set?
It doesn't work when _only_ remote.<name>.fetch is set: you need
branch.<name>.merge to determine @{u}, just like you would need
branch.<name>.push to determine @{d}.
So? The answer is no.
quoted
It is a downstream branch.
Which commit does git show @{d} show you? There is no ref called
refs/for/master.
The same commit 'git show @{-10000}' shows you. The fact it doesn't
resolve to a commit doesn't mean @{-100} is not the nth-hundredth
previous checkout.
--
Felipe Contreras