"TL;DR" summary: it really does work, but it can confuse you, so you probably should re-arrange your branch names at some point. Below is a much expanded version of Magnus Bäck's answer.
There are two entities involved here (or more if you have more than one remote, but let's stick with two :-) , yours and
- your own repo, and
- "the" remote.
Having an ambiguous
refname (see gitrevisions) only affects "your own repo", because the remote has its own names, in its own
.git directory. Moreover, when you type in a
refname and make your git commands resolve it, it's resolved according to those rules listed in gitrevisions.1
This is still not a great situation, because it can confuse the heck out of you. Also, "only affects you" is maybe an exaggeration or a "white lie", because when you use
git push, there are a lot of options:
git push origin mybranch:newbranch. This tells your git to look up the name
mybranch (with the usual resolving, which gets commit
F in Magnus Bäck's answer), and then contact
origin and ask it to update or even create a branch on its side named
git push origin mybranch. This tells your git to look up the name
mybranch as usual, but then contact
origin and ask it to update or create a branch on its side named
mybranch, which (as already noted) would not be a fast-forward and would get refused.
git push or
git push origin, which looks up your
push.default setting.2 If that's set to
simple, git really wants your name to match the remote's name. If it's set to
nothing, git demands that you supply the "other side name" every time. If it's set to
upstream, git uses the "upstream" name,3 which is not required to match the local name.
push.default setting of
upstream is pretty much meant for this kind of situation: not necessarily an ambiguous name, but any case where your name,
mybranch, differs from the name on the remote,
(There's also the question of what happens with
git fetch. See extra info below footnote 3.)
1For historical reasons, or maybe just to be obnoxious, the
git checkout command resolves branch names with different rules. I don't actually know why, but we're kind of stuck with it, at least for the foreseeable future.
2Settings are set/changed/examined with
git config. The
push command also checks
remote.origin.push, if that's set, and even
branch.mybranch.pushremote, if that's set. This gets terribly confusing if you configure a lot of configuration settings! Let's assume you don't configure yourself into weird corners here.
3The "upstream" is found by combining
branch.name.merge. For instance, you can set
newbranch. Then the "upstream" name for
origin/newbranch. Thus, the
upstream setting for
git push automatically work out that
mybranch pushes to
newbranch on remote
origin. This same "upstream" configuration works automatically with
Because there are two entities involved, when
git fetch brings over
origin's branch names, it needs to separate them out from your own branch names. The way it does this is to grab all the branch names from the remote repo, and then insert text in front of them: branch
master on remote
origin becomes local ref-name
refs/remotes/origin/master. Your branches go in
refs/heads/name; their branches go in
refs/remotes/origin/name; and these two name-spaces are guaranteed not to collide, because
remotes/ are different.
There's a configuration entry—don't change this particular one unless you know what you are doing; it's really just there so that mirrors can work differently—that says:
remote.origin.fetch = +refs/heads/*:refs/remotes/origin/*
that does this. That's how
origin/mybranch in the first place, and when you enter the name
origin/mybranch, again because of the rules in gitrevisions, that translates into the full, never-ambiguous name
refs/remotes/origin/mybranch, which then names the desired commit.