1

I am not very sure why does git push show two different messages for these branches. What is the difference between creating these two branches one with origin/master and the other one without.

For the first one: git checkout -b dev origin/master

D:\Source\Projects\dev -> origin\fortnight (dev -> origin)(fortnight@1.0.0)

git push

fatal: The upstream branch of your current branch does not match the name of your current branch. To push to the upstream branch on the remote, use

git push origin HEAD:master

To push to the branch of the same name on the remote, use

git push origin dev

To choose either option permanently, see push.default in 'git help config'.

And for the other one: git checkout -b uat

D:\Source\Projects\uat -> origin\fortnight (uat -> origin)

git push

fatal: The current branch uat has no upstream branch. To push the current branch and set the remote as upstream, use

git push --set-upstream origin uat
Rahul Chakrabarty
  • 2,149
  • 7
  • 39
  • 70
  • Possible duplicate of [VS Code - Connect to remote GIT repository and PUSH local files to new remote repository](https://stackoverflow.com/questions/43364233/vs-code-connect-to-remote-git-repository-and-push-local-files-to-new-remote-re) – Christoph Jul 28 '19 at 19:17

1 Answers1

11

TL;DR

If your push.default setting is simple (or is unset and defaults to simple), git push will:

  1. Require that your branch have an upstream set. If not, you get the message fatal: The current branch name has no upstream branch ...
  2. 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. :-)

torek
  • 448,244
  • 59
  • 642
  • 775