TL;DR
If your push.default
setting is simple
(or is unset and defaults to simple
), git push
will:
- Require that your branch have an upstream set. If not, you get the message
fatal: The current branch name has no upstream branch ...
- Require that the upstream name match—minus the
remote
part, that is—the current branch name. That is, if your branch is named xyz
, the upstream name must be origin/xyz
, assuming the remote is named origin
.
So if you have no upstream set, you get error #1; if you have an upstream set, but it's not one that Git "likes" due to the push.default
setting, you get error #2.
Of the two ways you're using git checkout -b
, one of them creates an upstream setting, and one does not. The actual upstream setting is one that makes push.default
complain.
Long
There is not really a good short answer here, because the history of this stuff—what was originally called tracking and is now called setting an upstream, and the way this upstream thing interacts with each of multiple different Git commands—is long and frankly kind of boring. :-) Unfortunately you'll need to know at least a little of it, in order to make sense of the way modern (2.0 or newer) Git works. That's because the Git folks try to maintain compatibility all the way back to Git 1.5, even though nobody actually uses Git 1.5 (hardly anyone uses Git 1.7 any more, even, and anything before 2.0 is really pretty obsolete).
Before I dive into any of this, remember too that git pull
is really two Git commands combined into one. What git pull
does is first, run git fetch
. Then, after the fetch works, it runs a second Git command, usually git merge
. It works better to talk about these separately—the opposite of push
is not pull
but rather fetch
. That's due to a historic error, of sorts, as we'll see.
The long and boring history, shortened as much as I can
To start close to the beginning and go very fast, the root of the problem is this: The original git pull
was a pretty short-and-sweet script: it ran git fetch
, then it ran git merge
, and that was it. Fetch and merge were usually the underlying actions you needed, so that's what it did. But this original git pull
command was not sufficient, because usually isn't always. The script would sometimes destroy your own repository too, if something went wrong or you ran it at a bad time (I had this happen to me at least once).
The script got tweaked many times—it got a lot better, though I got burned enough to learn to avoid it—and eventually re-written from scratch. The current git pull
, as of Git 2.6.0 and later, is no longer a shell script, and probably doesn't break repositories any more. :-) (I still rarely use it myself.) Meanwhile git fetch
, git push
, and git status
all acquired new features, and all of this stuff got tangled up.
The end result of all this tweaking and rewriting is that, today, each of your branches—your master
, your dev
, any other branch that you create and you control—is allowed to have either no upstream, or one upstream. But what exactly is an upstream, and what good is it? Why do you get these different messages, one complaining that the upstream name does not match, and another complaining that there is no upstream? And why is all of this so messy and complicated? We can answer the last part right away: it's messy and complicated because every mistake that was made, over the evolution of these features, is still supported, just in case someone depends on it.
Defining the upstream setting of a branch name: prologue
The upstream of a branch name is, internally inside Git, extremely simple, but it is defined as two parts. The two parts are a remote—which we haven't defined yet—and then a name that's meant to be meaningful on the remote. Hence, before we define upstream we have to define remote.
Defining a remote and the corresponding remote-tracking names
When you first run git clone
, you have to give it a URL. Back in the bad old days, you had to give Git a URL every time, over and over again. Obviously, typing in the same URL repeatedly is dumb. We have a computer, why not have it remember the URL?
That's the primary job of a remote. A remote is just a short, easy-to-type string like origin
. This remembers a URL: you can now git fetch origin
or git push origin
, and your Git finds the URL using the string remote.origin.url
as a key in a tiny database. Of course, if origin
remembers a URL, it can remember more stuff for you too, so there are more things you can set up using this short name, and there's one super-important one that Git sets up automatically for you.
Take a look at your .git/config
file: view it in a file viewer, for instance, or run git config --local --list
(or try both). Note that you have a:
[remote "origin"]
url = ...
or:
remote.origin.url=...
setting here. That's where Git is saving the URL.
What's at this URL? If you were to call it up with a browser or using curl
or something, well, it might require you to authenticate / log in / whatever first, but in the end, what's at the URL is another Git repository. That other Git, being a Git, has its branches. You can have your Git call that Git, as if making a phone call or sending text messages, via that URL. The two Gits will then have a conversation. The exact conversation they'll have depends on what Git command you ran, but there's one you can run any time, that just shows stuff from their Git:
git ls-remote origin
This might show just a little, or a lot, depending on how much stuff they have to tell you. Here's a snippet of what using it on a Git repository for Git shows:
3034dab9ed6b11970a53099a7b3ca981f1461365 HEAD
98e06ded345450b3b07099d3ed1abf58fc95f5b6 refs/heads/maint
3034dab9ed6b11970a53099a7b3ca981f1461365 refs/heads/master
0f2c4a37fdba75d06ae7254c4b30ed7739985214 refs/heads/next
[snip]
213030c8af8ad9f9060cc264395817adb4ede44e refs/tags/v2.2.3
441c4a40173fe1ee8a5c0094e587dfc47e2a6460 refs/tags/v2.2.3^{}
90141c859541f8daa08bdb0621c64cbd7dadbd8c refs/tags/v2.20.0
5d826e972970a784bd7a7bdf587512510097b8c7 refs/tags/v2.20.0^{}
[snip]
Basically, git ls-remote
lets your Git ask their Git: What branches and tags do you have? What are their corresponding hash IDs? That's what spills out here. When your Git calls up their Git and gets things from them, your Git gets to know their branch names. Your Git then renames these branch names: Their master
becomes your origin/master
. Their maint
becomes your origin/maint
. Their next
becomes your origin/next
. This renaming—which is actually crazy-flexible; the simple renaming here is just the default—sticks origin/
in front of their names so that you can tell them apart from your names. The literal string origin/
comes from the fact that you chose to call this origin
when you set up the remote.
Wait, I didn't choose origin
, Git did that!
Well, yes—but that's just the default. If you run:
git clone -o boo <url>
you'll get a clone that, instead of origin
, has boo
as its remote. Instead of origin/master
, you'll have boo/master
, and so on. If you didn't choose to override origin
, then, in effect, you chose to use origin
.
In any case, this is what origin
is all about: it's the name of a remote, and it becomes the prefix of the various remote-tracking names that your Git uses to remember their Git's branch names, the last time you had your Git call up their Git.
This stuff, with remotes and remote-tracking names, was new in about Git version 1.5 or so (details of things pre-1.5 are lost in the mists of time; I didn't start using Git myself until around 1.5.something or maybe even 1.6, and the release notes only go back to Git 1.5.0.1). Before then, Git didn't have remotes at all, and there were other ways to abbreviate URLs. Those other ways still work, but you should use remotes and the remote-tracking names. They are much better.
If there's an origin/master
, it makes sense to link your master
to it
Suppose your Git calls up their Git and your Git finds that they have updated their master
. If you do this calling-up with git fetch
, your Git not only sees the update, it also picks up the new commits they made. Your Git stores these new commits in your repository—without affecting anything you're doing right now, which sometimes crucial! It's always safe to run git fetch
because of this "don't touch other things" trick—and then your Git updates your origin/master
to remember the commits you just got from them.
If you're in the middle of something delicate, such as working on a merge, you can just ignore this update for now. But if / when you're at a nice easy stopping point, or ready to use the new commits you got from them ... well, now it's time for you to do something with your master, based on what they did with their master. This is where having origin/master
set as the upstream of your master
becomes useful. So this gets us back to the second half of setting an upstream.
Note, by the way, that when you use git fetch
, there's a good chance—probably at least 80%—that you'll eventually want to merge or rebase using what you just brought in. So it makes sense to combine the two operations into a git pull
. I still don't, for three reasons: (1) I've been burned too often and have different habits; (2) 80, or even 90 or 95, percent still isn't 100%; (3) I really like to inspect what came in, with the fetch, before merging. (Maybe reason 3 is part of reason 1, but sometimes it drives whether I use merge
or rebase
, not just when I incorporate the new commits.)
Defining the upstream setting of a branch name
The upstream for your branch is usually just the remote-tracking name that you use, in your repository, to remember the commits they—whoever they are—have in their Git, that your Git has copied via git fetch
. In other words, you usually want Git to set the upstream of master
to be origin/master
.
But, as I mentioned near the beginning, the upstream is actually set in two parts. One is to set the remote setting of your branch master
to be origin
. The other is to set the merge setting of your branch master
to be master
:
$ git config --local --list
[snip]
branch.master.remote=origin
branch.master.merge=refs/heads/master
[snip]
These two settings make the upstream of my master
be my origin/master
. You can try thinking about this as just stringing the remote and the merge together, but actually that doesn't quite work.1
You can also set the upstream of one of your (local) branches to be another (local) branch! For instance, I can create a branch zorg
that has master
as its upstream. If I do, the git config
above will say:
branch.zorg.remote=.
branch.zorg.merge=refs/heads/master
The upstream is not ./master
but rather just master
, in this case. You should use the front-end command git branch --set-upstream-to
to hide all this weirdness about the remote
vs merge
parts of the setting; git branch
knows exactly how to combine everything, and deal with the backwards-compatibility that makes this configuration also work with an ancient Git.
To find the current upstream setting of some branch, use git branch -vv
or git rev-parse --symbolic-full-name
, e.g.:
$ git branch -vv
* master 9c9b961d7e [origin/master] The sixth batch
[snip]
The text in square brackets shows the current upstream.
$ git rev-parse --symbolic-full-name master@{upstream}
refs/remotes/origin/master
This exposes some more details of how remote-tracking names really work: the full name is actually refs/remotes/origin/master
, whereas the full name of your master is refs/heads/master
(note heads
instead of remotes/origin
).
1Not only is there a wrinkle for the special remote name .
, the merge
setting is also mapped through the remote.remote.fetch
setting. The idea here is that the merge
setting listed in branch.name.merge
is the name of the branch as seen on the remote, so if you have re-mapped their names to unusual remote-tracking names, your Git will do the same mapping automatically, as necessary. If you know exactly what you're doing, you can use git config
twice, on the two settings, to set or obtain the upstream setting of any particular branch. It's much easier to use git branch
or git rev-parse
, though.
Whew, OK, so that's what an upstream is ... but what good is it?
Here, all that history, and those historical mistakes, come right back into the picture. In the bad old days you just ran:
git pull <url> <branch>
which had the annoying problem of typing out long URLs. So this became the shorter:
git pull <remote> <branch>
where remote
was almost always just origin
. But if we have an upstream setting, which lists both the remote
part and the branch
part internally, git pull
can just figure both out for you! So now you could just run:
git pull
The pull
script would find the current branch name—e.g., master
—and then get the branch.master.remote
and branch.master.merge
settings, and automatically run the same thing as it would for git pull origin master
.
Thanks to all the backwards compatibility, all of that still works exactly as before, and that's the first thing that an upstream does for you. It lets you run git pull
with no arguments. That git pull
either literally runs git fetch
and then runs git merge
, or, as of Git 2.6, uses the code from git fetch
and git merge
, and automatically fetches from the right Git, and merges with the right commit.
Similarly, setting an upstream lets you run git fetch
with no arguments: Git looks at the current branch, gets its remote
—it doesn't need the entire upstream, just the remote—and does a git fetch
to the right URL. Setting an upstream lets you run git merge
or git rebase
with no arguments too: Git looks at the current branch, figures out the upstream—this time it needs the whole thing—and does a git merge origin/master
or git rebase origin/master
if / as appropriate.
So, for these four related commands—git fetch
, git merge
, git rebase
, and git pull
—setting an upstream means you don't have to type in as much with each command. That's really pretty much it.
Note that git pull
always runs git fetch
first, but you can choose to have it runs git rebase
second, instead of having it run git merge
second. In both cases, the upstream setting still controls the arguments to the merge
or the rebase
, if you run the shortened command.2
2If you don't use your upstream—if you type in git pull remote branch
—the second command, whichever it is, uses the tip commit you brought in from the named remote, without using the upstream setting at all. This gets into some complicated details, including the fact that, again for backwards compatibility reasons, git fetch
always writes something about everything it fetches to a special file named .git/FETCH_HEAD
. This dates back to before the invention of remote-tracking names, though it's still actually useful for one-time fetch-and-use operations.
What good is an upstream, part 2: git status
and git branch
If you use git branch -vv
or git status
, sometimes you will see an annotation like this:
[origin/master: ahead 1, behind 10]
or:
Your branch and 'origin/master' have diverged,
and have 1 and 10 different commits each, respectively.
These counts—which Git makes by inspecting which commits are reachable from your branch and its upstream, which is another whole very long discussion; I'll just refer to Think Like (a) Git here—tell you what kind of work you might have to do to re-sychronize your branch with its upstream. To do that, your branch has to have an upstream.
What good is an upstream, part 3: git push
As with git fetch
, git merge
, git rebase
, and the do-two-of-those git pull
command, git push
can be run with no arguments at all. To make that work, Git needs to know two things: Where do I call up the other Git? and What branch name do I ask them to set?
Again, the upstream provides both pieces of information. But there are more historical mistakes here that make this complicated. That's where the push.default
setting comes in.
It's worth noting here that there's an asymmetry between git fetch
and git push
. When you use git fetch
to have your Git call up the Git at origin
, your Git gets a list from them of all of their branches. Your Git can then take all their new commits, as found by using all of their branches, and rename all of those to your origin/*
remote-tracking names.
This makes it totally safe to run git fetch
at any time. Your Git is not going to disturb any of your work; it's just going to get new commits from them and/or update your remote-tracking names. None of your branches are affected at all!
But, while git push
is the closest thing there is to the opposite of git fetch
, it's different in a very important way. When you run git push origin master
, for instance, you have your Git call up their Git, offer them new commits if needed, and then end this conversation with a polite request: Other Git, please set your master
to identify the same commit, by raw hash ID, as my own master
. That is, you're going to mess directly with their branches. There's no remote-tracking name over there—no alice/master
, no bob/master
, no riki/master
, there is only just the one master
. If they accept your request, they change their branch immediately.
You might argue that this is the opposite of what git pull
does, since git pull
(a) fetches and then (b) merges or rebases, which changes your branch in some way. But step (b) always changes, or tries to change, your branch non-destructively. The push operation is only non-destructive—and only accepted when made as a polite request like this—if it is a fast-forward operation (which again we haven't defined, and I won't here). There's no merging or rebasing available: only fast-forwarding is allowed. You can actually run a git fetch
that performs fast-forwarding on your own branches, instead of updating your remote-tracking names; and if you do that, you make fetch and push into true opposites (but there are more wrinkles and I don't want to get into details here).
Anyway, this now leads back to history and compatibility—and a sort of "breaking change" that resulted in Git going from version 1.9 to version 2.0, rather than version 1.10 (note the current Git is version 2.22, well past a dot-ten release).
Before Git version 2.0, git push origin
, with no additional arguments, defaulted to having your Git call up the other Git at origin
and get a list of all its branches, just like git ls-remote
or git fetch
would do. What Git did then was clever, but—as it turned out—another mistake: your Git would match up your branch names against their branch names. If you had a master
and they had a master
, Git would add master
to its list. If you had a dev
and they had a dev
, Git would add dev
to its list. Having matched up all of your names, your Git would then ask (politely by default, or forcibly with --force
) their Git to set all of these matching branch names, based on all of your branch names.
What this means is that if they—origin
—had a dev
, and then you made your own different dev
from your master
, and ran git push origin
without naming anything specifically, your Git would ask their Git to set their dev
to match your dev
, even though your dev
is not related to their dev
.
After this behavior burned too many Git beginners, the Git folks decided to change it. They added a bunch of new ways to have git push
behave. They created a git configuration setting, push.default
, that you could set. One setting is called matching
: that's what Git 1.x did by default. The new "safe" setting is called simple
: that's what Git 2.x does by default.
For a very long transition period, git push
would complain if you had not set push.default
yourself, telling you that the default behavior was/would-be different in Git 1.x vs Git 2.x. If you lived through that transition—or are still using a really ancient Git—you will have seen these complaints, and perhaps even have set your push.default
setting.
Modern Git no longer complains; it just uses simple
by default. The simple
setting requires that you have an upstream set and that the two names match. If you are pushing from your dev
, you must be pushing to their dev
. If you are pushing from your master
, you must be pushing to their master
.
Note that all of these constraints apply only when you run git push
in the convenient short-cut mode, where you let your upstream setting tell Git what to push and where. If you run, say, git push origin master
, you are overriding the current branch, the upstream, and all of this other stuff: you are telling your Git to call up their Git and ask them to set their master
based on your master
, even if you're currently on your own dev
.
New branches vs upstreams
Of course, you can create a new branch, which won't have an upstream yet, using something like:
git checkout -b foo
or:
git branch foo
or:
git checkout -b foo origin/master
or:
git branch foo origin/master
or even:
git checkout -b foo master
and so on.
There are lots of ways to create branches. When you do create a new branch, you can choose to have the new branch have an upstream setting immediately, provided you use some other branch name to create it. The reason for the provided part is that the upstream is the name of another branch. If you create a branch without using another name, what other name would Git use?
OK, but there's a really tricky bit here. Actually, there are several tricky bits—some of this is historical I think, and some just the fact that Git is very configurable. First, consider what Git calls DWIM (Do What I Mean). Suppose that you have just cloned a repository, and are on your master
, but the origin
repository has master
, dev
, feature/short
, and feature/tall
. You can now run:
git checkout feature/short
for instance, even though you don't have a feature/short
. This invokes Git's "DWIM mode": Git sees that you do have origin/feature/short
, and turns this into:
git checkout -b feature/short --track origin/feature/short
This --track
option uses the old (bad) verb meaning set an upstream. This creates the feature/short
branch and sets its upstream to origin/feature/short
.
This same technique works with all the other branch names, and in fact, is how git clone
made your master
in the first place. At the end of the git clone
process, your Git ran, in effect, git checkout master
. You didn't have a master, but you did have an origin/master
. So your Git made your master
and set its upstream to origin/master
, as the last step of your git clone
.
If you use:
git branch name start-point-name
your Git will often, but not always, use the start-point-name
to set an upstream for name
. You can configure this with the branch.autoSetupMerge
and branch.autoSetupRebase
options. The first has three possible values: false
, true
, and always
for branch.autoSetupMerge
. The second has four: never
, local
, remote
, and always
for branch.autoSetupRebase
. These are described reasonably-fully in the git config
documentation.
Some of these automatically-set-up upstreams have names that satisfy git push
's simple
setting, and some do not. The "DWIM mode" ones always do, so that's particularly convenient.
A new branch that has no corresponding name in the other Git
Suppose you do:
git checkout -b dev
to create a new branch dev
in your own Git, and there's no dev
in their Git, over at origin
. You might like to set the upstream of dev
to origin/dev
, so your next command is:
git branch --set-upstream-to origin/dev
This is what happens:
$ git checkout -b dev
Switched to a new branch 'dev'
$ git branch --set-upstream-to origin/dev
error: the requested upstream branch 'origin/dev' does not exist
hint:
[snip]
I'll show the rest of the hint in a moment, but the point here should be clear enough: origin
does not have a dev
, so we do not have an origin/dev
. Hence we cannot set the upstream of our dev
to be our origin/dev
!
What we'll have to do—well, the sensible option for what we should do anyway—is: go ahead and do some work if we want, and soon—maybe even right now—ask their Git to create their dev
based on our dev
. As soon as they create a dev
, our Git will create, in our repository, our origin/dev
. Then we'll have a name we can --set-upstream-to
.
So, here is the rest of the hint
output:
hint: If you are planning on basing your work on an upstream
hint: branch that already exists at the remote, you may need to
hint: run "git fetch" to retrieve it.
hint:
hint: If you are planning to push out a new local branch that
hint: will track its remote counterpart, you may want to use
hint: "git push -u" to set the upstream config as you push.
That git push -u
recommendation is just a short-cut. We can now run:
git push origin dev
which will immediately create a dev
on their system, matching our dev
(pointing to the same commit hash). Then we can run:
git branch --set-upstream-to origin/dev
But it might be nice if we could ask our Git to do both of these at the same time, and that's what git push -u
does:
git push -u origin dev
has our Git call up their Git, ask them to create a new dev
based on our dev
, and then—if that succeeds—do a git branch --set-upstream-to
as well.
You only have to do this once, and even then only if you're using some of the various push.default
settings. You can use the -u
option more often if you want: it just keeps running that extra git branch --set-upstream-to
. If your branch already has an upstream, you'll re-set it: if that's no change, the re-setting is harmless. If your branch doesn't have an upstream, you'll set it: now you have an upstream. It almost seems like -u
should be the default, doesn't it? But that wouldn't be backwards-compatible: git push
didn't set an upstream before, so it doesn't now either.
There are tons of additional configuration options
There are many more configuration options you can set, that affect git fetch
, git push
, git pull
, and more. You can, for instance, fetch from one remote and automatically push to a different one—or a different URL for the same remote, which is probably only useful if you have the ability to fetch without authenticating but can only push with authentication and authentication is slow or painful for some reason.
Some of these options influence things like why git push -u
isn't the default, too. For the most part, most of these things are set up as well as they can be, given the backwards-compatibility constraints that Git faces. If you find some aspect of Git annoying, check to see if there's a configuration to fix that, because there often is. In this particular case, for instance, you could change your push.default
. But consider, too, that in some cases—including this one—the defaults were arrived-at through painful experience, kind of like the way I avoid using git pull
. :-)