Everything was in sync with the Remote repository on GitHub.
Then I renamed a local branch in Sublime Merge (right-click → rename branch).
Then I pushed – This did not sync the remote repo, instead I now had two identical branches there, one with the old and one with the new name. So I deleted the old branch via GitHub, because I have no idea how to do it from Sublime Merge.
Now, when I try to pull in Sublime Merge, it says:
Ihre Konfiguration gibt an, den Merge mit Referenz 'refs/heads/<old-branch-name>' des Remote-Repositories durchzuführen, aber diese Referenz wurde nicht angefordert.
English translation via Google Translate:
Your configuration says to merge with reference 'refs/heads/<old-branch-name>' of the remote repository, but this reference was not requested.
What can I do to fix this problem? I guess I have to remove the old branch entry from my configuration, but how and where?
Short version: use
git branch --set-upstream-toto fix the upstream setting, e.g.:(note that the
origin/version of the name goes after the equal sign, with no space, in this format; the branch name itself is optional and if you leave it out Git will assume "the current branch name").Longer: how to understand what's going on here
Branch names, in Git, don't actually matter very much. This means you can change them whenever you want. But there are some issues here, because each Git repository has its own (separate) branch names. Changing the name in one repository doesn't affect the name in another.
Besides branch names, Git has other kinds of names, such as tag names (often written with a
v, e.g.,v1.7orv2.12.1) and remote-tracking names.1 You'll likely be at least acquainted with, and perhaps quite familiar with, names likeorigin/mainororigin/master, and these are examples of these remote-tracking names.Whenever you run
git fetch—including thegit fetchthatgit pullruns—you are instructing your Git software to, using data from your repository, call up some other Git software at some URL. The URL itself is typically the one that Git saved at the time you rangit cloneto create your Git repository. Git will have saved that URL under the nameorigin. This name—origin—is actually change-able, but people don't normally changeoriginmuch if at all.This short name, usually
originhere, is just a way of helping you keep track of the fact that there's another Git repository "out there" on the Internet somewhere. The short name both stores the URL, so that you can rungit fetchlater without having to remember it yourself, and forms the basis for remote-tracking names.So, when you do run
git fetch—including thegit fetchthatgit pull runs, and thegit fetchthatgit cloneran when you made the repository in the first place—your Git software calls up this other Git software, which reads from some existing Git repository. That Git repository has its own branch and tag names. It may even have remote-tracking and/or other kinds of names. Your Git software can see all of these names—or at least, all the ones they're willing to show you—and the Git hash IDs that go with them. In fact, there's a command you can use to dump all this out:will call up the other Git over at
origin, have it list out the names it will let you see, and will then just print them all out without actually doing anything useful with them.But when you run
git fetch, Git uses this same mechanism to see all their branch and tag names, and uses that to get commits from them (and to get tags if that's enabled, as it normally is by default). Then—once your Git has any new commits from their set of commits added to your repository—your Git normally updates all your remote-tracking names. This involves creating new names, if they show a name that you don't have yet as a remote-tracking name, or updating the existing names, if they show a name that you do have.What all this means is that if you clone a repository that, at the time you clone it, has branches named
main,br1, andbr2, you'll get, in your own clone:At this point you have no branches, which is a really miserable way to work in Git (although it can be done). So
git clonenow creates one of these three branch names in your repository, based on the-bargument you gave togit clone. If you didn't give a-bargument, and most people mostly don't, you get the name that their software recommends, which is typicallymainormaster.2But now suppose that you use
git pushto delete the namebr1on the hosting site and replace it instead with the nameanch. If you now rungit ls-remote origin, you'll see the three namesmain,br2, andanch. That's what your Git software will see too, sogit fetch originwill create ananch, spelling itorigin/anch. That's your new remote-tracking name for theiranch.What happens to
br1? Nothing. It's still there, as a stale left-over. Your Git software creates or updates appropriate remote-tracking names for the branch names they have now, but doesn't clean out the dead ones for names your Git software created earlier, that no longer have any obvious purpose.1Git now fairly consistently calls these "remote-tracking branch names", but the word branch here is just cluttering things up, in my opinion. These names are "remote-tracking", so that part makes sense. What they track is some other repository's branch names, hence the rest of the name—but since they are in your repository, and are not actually branch names, I think it makes more sense to leave out the word branch entirely. They are names, and they track—as in "follow along with"—a remote's branch names, but they're not branch names at all.
2To change the recommendation on GitHub, use GitHub's web interface to set the "default branch". To change it on Bitbucket, use Bitbucket's web interface. To change it on Google Hosting ... well, they forgot to give you a way to change it! Oops. But this is the general idea: you can't do it from within Git, you have to use some add-on. That's probably a mistake in Git, but given that it's been this way for almost two decades now, it will probably stay this way for a while yet.
The special properties of a branch name
All the different varieties of names, in Git, hold exactly one (1) hash ID or—more formally—object ID, a big ugly string of letters and digits like
c3ff4cec66ec004d884507f5296ca2a323adbbc5. This, which is really just a hexadecimal encoding of a very large number, serves to locate an object in Git's "object database", which is one of the two databases that make up the core of a Git repository (the other database being the names-to-IDs database). It's the combination of these two databases that makes a Git repository work; these two databases, plus a minimal set of ancillary files, are the necessary basic parts of any Git repository.What makes a branch name particularly magic in Git comes in three parts:
It's forced to hold only a commit hash ID. No other kind of object is allowed. (There are three other kinds of objects, and tag names, for instance, can hold any of the four kinds.)
You can get "on" a branch, using
git checkoutor (since Git 2.23)git switch.Branch names support particular settings that other names lack.
It's actually item 2 that's the most crucial here. Being "on" a branch is how Git keeps track of the latest commit. Whenever you are "on" some branch and make a new commit, Git updates the stored hash ID to use the new, unique, apparently-random hash ID of the new commit. Git needs the hash ID of a commit, in order to find that commit. Branch names store the hash ID of the latest commit. Commits themselves store the hash IDs of earlier commits, so by finding the latest, Git can find earlier ones.
Given that a branch name will auto-update when you make a new commit, this makes branch names work to find all the commits. But since that's not the purpose of this answer / article, we'll stop here without further explanation. Instead, we'll move on to item 3, the settings.
The settings for any one particular branch name include:
git pull, if you choose to usegit pull; andThe first item is strictly for
git pull. The second item is also used bygit pull, and bygit rebaseandgit mergeas well.The
git pullcommand means:git fetch, thengit mergebut sometimesgit rebase.Because it runs these back-to-back, it can actually tell a little bit more about what's going on than each command alone would know (which means that you get a vaguely-informative error message, as we'll see).
3The reason for this funny phrasing is that, for historical reasons, the actual encoding of this "upstream" setting is rather peculiar. To refer to a branch in your repository,
branch.B1.remoteis set to the literal string.(a period) andbranch.B1.mergeis set torefs/heads/B2, whereB1andB2are the two branch names as they appear in your repository. To refer to a remote-tracking name in your repository, however, these two are set toRandrefs/heads/B2whereRis the remote andB2is the branch name as seen on the remote. These are then run through a mapping determined by the refspec(s) in the default fetch settings, which I'm not going to go into for space reasons. It's crazy-complicated, even though the normal effect is just that, e.g., branchmainhasorigin/mainas its upstream.The
git pulloperationThe
git fetchcommand needs one or two pieces of information, and thegit mergeorgit rebaseoperation also needs one more pieces of information. In particular,git fetchneeds to know which remote togit fetchfrom. Are we doinggit fetch origin, or maybegit fetch rumpelstiltskinorgit fetch bruceor...? Of course it almost always turns out to beoriginanyway (and that's a fallback default built in togit fetch), but it would like to know.The second command, whether it's merge or rebase, needs to know at least one thing. What it really needs is a hash ID. It can take a name:
or:
for instance both work just fine, providing you have an
origin/br2of course, but in both cases Git is going to turn that name into a raw hash ID to get its job done. Git may then use the name, e.g.,git mergewill generate a default merge message from the name. However, ifgit pullis runninggit merge,git pullgenerates its own merge message and overrides this default anyway.In any case, both of these bits of information are encoded into a branch's upstream setting. The upstream of a branch named
Bis typicallyorigin/B. That is, if you're on your branchmain, you made that based onorigin'smainwhich you callorigin/main, and you intend your new commits to go to theirmainand so on.That's all fine, and when you create a branch
br1based on their branchbr1which you callorigin/br1, you get a branchbr1with an upstream setting oforigin/br1. You'll see this if you run:(or
git branch --verbose --verboseif you like the long spellings). But if you then usegit pushto change the name, in the other repository, frombr1toanch, and then run:your Git will bring over a new name,
anch, and turn that intoorigin/anch. If you're thinking ahead, you will also rename your localbr1toanch:(just before or after renaming the branch over in GitHub or wherever). But: your branch, now known as
anch, still says that its upstream isorigin/b1. You've renamedb1locally, and—whether using a web interface orgit push—you've renamedb1over on the hosting site, but the upstream setting in your repository is just a specially-formatted string, and it has not changed.Running:
tells your Git: look up
origin, use that to get the URL, call up that other Git software, inspect the set of branch names, and clean out any old stale names I have. That will toss out your staleorigin/b1. But youranchbranch still has the oldorigin/br1as its upstream setting. So now you need:to set the upstream of the current branch (which you now call
anch) to be yourorigin/anch, which is what you call theiranch.Without this,
git pullthinks that it should rungit fetch originand then use whatever update got made toorigin/br1. Since no update did get made,git pullcomplains:(though actually the English version is a bit different than the reverse translation from the German translation).
Were you to run
git mergeorgit rebasewithout supplying additional arguments, you'd get a similar (but somewhat different) complaint, e.g.:Note that this requires first cleaning out the "stale" name to get the error. If we don't run
git fetch --prunefirst, there's a "stale"origin/br1lying around. The rebase or merge command will use the hash ID stored in that stale name! The resulting rebase or merge will probably do nothing at all, but in general this isn't a great idea.Some slight flaws with
git fetchIdeally, it would be nice if
git fetchwould always do--prune. You can make it do that:This makes
git fetch, including the one run bygit pull, automatically do a prune operation whenever it can. There are some small bug-ettes here, though they are generally pretty safe: if you ever hit a weird corner case, just rungit fetchagain to fix things up, leaving out any extra arguments that caused you to hit the mini-bug.There are two other things I must mention here:
git fetchso that it can't update most remote-tracking names.In particular, if you run:
you're telling your own Git that, whatever the list of branch names the other Git spills out for us, we should only update our
origin/main. (The slash here is implied; this goes back to footnote 3 and the hysterical raisins mentioned therein.) When you use this form ofgit fetch, fetch cannot prune (and will not do so even withfetch.pruneset totrue). If your Git version is really old, it won't even updateorigin/main: it will just leave, in.git/FETCH_HEAD, just enough information for this old version of Git to finish agit pulloperation.Note that when you run
git pulllike this;this runs
git fetch, notgit fetch origin main, so these two odd cases don't happen. But when you run:the
git pullcommand passes this entire set of arguments on togit fetch, resulting ingit fetch origin main, so in ancient Git, these two special cases (no pruning and no remote-tracking update) do happen, and in modern Git, the one case (no pruning) happens.The fact that it's this complicated, and that I need to mention this, is a sort of indictment of Git. I find Git to be very useful and mostly pretty good, but it does have a lot of instances of meaning 3 here.