There are multiple parts to the right answer, which is why this is so confusing. To understand this properly, let's start by defining some terms:
A remote is a simple name, like origin
or upstream
. This name lets Git store a URL—technically, one or more URLs, but usually just one—so that instead of typing out https://user@host.dom.ain/some/fairly/long/path/to/some/other/repo.git
or similar, you can just type origin
.
Git has one built in, more or less standard, remote named origin
, which is created by git clone
and automatically remembers the URL you used when you ran git clone
.
A reference or ref is something you use to refer to commits, such as a branch name or tag name like master
or develop
. References have long forms: master
is really refs/heads/master
, for instance. Most of the time you can just use the short form and not worry about this, but the long form is there, and is what Git uses internally, for handling tricky cases like if you accidentally make a tag master
. (Don't do that on purpose, but if you do it by mistake, the long form will always let you fix things.)
A refspec is, essentially, a pair of references separated by a colon :
character. For instance, master:master
is a refspec, as is refs/heads/develop:refs/heads/develop
. But that's a more complicated form of refspec: you can drop the colon and second name, in a lot of cases, in which case it looks like a reference.
What git push
needs is, in order, one remote followed by one or more refspecs.
This in turn means the answer to your question:
git push origin NEW_BRANCH
... does NEW_BRANCH refer to the local branch name or to the remote branch name (if there is a difference)?
is actually a bit more complicated, because that NEW_BRANCH
isn't a branch name after all, it's a refspec. It just looks like a branch name!
What git push
does is to call up another Git. The other Git "lives" (or at least answers the Internet-phone-call your Git makes) at the URL, which your Git finds by looking up the remote. Then the two Gits have a conversation, in which your Git finds out what commits their Git has, offers their Git new commits if needed, and finally, asks their Git to set some of their branch names to remember some commits found in your Git repository. (By this point, they have those commits as well, if they didn't before, thanks to the in-between conversation.)
So the NEW_BRANCH
refspec that you give here is actually both names. When you use the form that has the colon in it, you can use two different names, or even use a raw hash ID on your side:
git push origin master:somebranch
which has your Git offer your new commits and then set their somebranch
to point to the same commit that your master
points to, or:
git push origin a123456:refs/heads/somebranch
which has your Git make sure they have commit a123456...
and then set their somebranch
to point to that particular commit.1
I don't understand the need for [remote and refspec]
Well, in fact, often you don't need them. You might think that this would mean always, but it doesn't, for multiple historical reasons.
First, Git didn't always have remotes at all, so in place of the remote name, you can just write out a URL.2 If you don't use either a remote or a URL, Git will figure out a default (often origin
). But if you need to list a refspec, you must provide either a remote or a URL, because the remote-or-URL has to go in that position in the arguments.
Second, Git used to default to pushing multiple branches at once using a rather overly enthusiastic default refspec. Today, it defaults to pushing one branch using a sane refspec. This ought to—and does!—make it not need the refspec, but only once some condition is met. And, you can change this default, using push.default
; if you do, that changes the conditions under which you can omit the refspec(s), and hence the remote name.
Using today's default push.default
of simple
, Git will automatically figure out and use the correct remote and refspec if:
- The current branch has an upstream set, and
- The upstream names a branch of the same name on the remote.
The remote here can be any of your remotes: if branch xyz
has an upstream of foo/xyz
, the remote is foo
and the branch on foo
is xyz
so conditions 1 and 2 are both met and git push
will do the right thing.
When you first create a new branch, its upstream setting—if any—is determined by how you create that branch. Using git checkout -b name
gives you a new branch name
that has no upstream by default. Using git checkout --track remote/name
gives you a new branch name
that has remote/name
as its upstream, and there are various other options that do set some upstream.
1If you use this form, you usually have to spell out the full reference name. The reason is that when you use the shortened names, like git push origin x234
, Git scans through your references to figure out whether, say, x234
is a branch name or a tag name. That lets your Git tell their Git: set your refs/heads/x234 (branch) or set your refs/tags/x234 (tag).
2In those really old versions of Git, you always had to supply a URL. As you might imagine, this was kind of painful. That led to several experiments, which ultimately produced the idea of a remote, and once there was a standard remote named origin
, that allowed you to omit the remote entirely, as long as you could also omit all the refspecs.
The experiments are all still supported as well. You can use work:foo
plus an insteadOf
entry to map work:
to a host name and optional path therein.