Home > other >  names match, but: "The upstream branch of your current branch does not match the name of your c
names match, but: "The upstream branch of your current branch does not match the name of your c

Time:12-14

git push issue.

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:refs/remotes/origin/main

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

    git push origin HEAD


git status
On branch main

I've read fatal: The upstream branch of your current branch does not match the name of your current branch, however in my case names match.

I've read Local and remote branch name is same still getting "The upstream branch of your current branch does not match the name of your current branch ", where a person renamed branches both local and remote, an answer advised to use git branch -vv to check for remote info, in my case output 'main [origin/main]' (and on remote there is main).

I have a clue that when I did git pull the output had -> main (forced update) (ends with up to date). Could it be relevant? What could be the cause of my issue? TIA

Added:

I've tried to git branch --unset-upstream, after that git pull as expected "no tracking". Then git branch --set-upstream-to origin/main, after that git pull as expected "up to date". git push same fatal:...

git pull no longer writes "forced update" btw.

CodePudding user response:

You have several different issues going on here.

First, the error message itself tells us what's wrong, though as it does so often, Git obfuscates it a bit:

fatal: The upstream branch of your current branch does not match
the name of your current branch.

What's missing here is a few magic words. For instance, suppose it said:

fatal: Your 'push.default' setting is 'simple', but the upstream
branch name 'origin/xyzzy' does not match the name of your current
branch 'plugh'.

That would provide a few more clues. It might still not be good enough (it's hard for me to tell since I'm positively soggy with Git knowledge: too much studying of the Enemy, as it were) but it would at least mention the push.default setting of simple.

The purpose of this setting is, well, to generate exactly the error you got here. It's the default setting since Git 2.0 because it keeps you from accidentally using one name (plugh, perhaps) in your repository and a different name (xyzzy, perhaps) in the "other" repository you use (stored on GitHub or whatever).

Using different names on the two "sides" is allowed, and in some cases—not common ones, but cases that do actually occur—it's sometimes necessary. But normal humans find it confusing and a source of mistakes, so Git now rejects it by default, as long as you're using the "simple" setting for git push.

You therefore have two choices at this point, if your upstream is correct:

  • you can change your push.default setting, to tell Git that you're a very advanced user and it should stop doing these safety checks, or
  • you can use a more explicit git push command every time.

If your upstream is wrong—which is probably true—you should fix it.

A brief bit on the "upstream" setting

I've tried to git branch --unset-upstream ...

It's important to know what the "upstream" setting of any particular branch name is and does for you. You've already seen how to clear it—how to tell Git that you wish for the current branch (or some other particular branch name you specify) should have no upstream after all—and perhaps how to set it (there are multiple ways to set it). But: what good is it?

The main thing an upstream does is let you run git push, git merge, git rebase, and/or git pull without typing anything additional past the verb. That is, you can type in git push and press ENTER or RETURN or whatever the button is labeled on your keyboard, and Git will now know that you would like to git push your ... well, actually, this depends on your push.default setting. So that's kind of complicated. But if push.default is set to simple (or not set at all so that it defaults to simple), Git will assume that you'd like to push your current branch, whatever its name is, to its upstream and require that the two names "match" after stripping off the origin/ or whatever.

Similarly, you can run git merge and Git will know you mean git merge whatever-the-upstream-setting-is, and you can run git rebase and Git will know you mean <git rebase upstream. You get a similar shortcut with git pull (although I'd encourage you to avoid git pull until you know exactly how the merge and rebase commands work and when-and-why git pull runs them).

Having an upstream set also means that git status gives you extra information, and it allows you to use shortcut syntax like @{upstream}. So it's there to provide various convenience features, much like git pull is just a convenience wrapper meaning run git fetch, then run a second Git command.

But that's all it is: convenience. If you find it convenient, feel free to use it. To the extent that it's not convenient, avoid it. Some people never bother with upstream settings, and that's fine.

Being explicit

The git push command:

  • sends commits from your own repository to some other Git software operating on some other Git repository, then
  • asks or commands that other Git repository to set some of its names (branch names, tag names, whatever—usually just one branch name though) to remember some particular hash ID.

To make this work, git push needs a minimum of two pieces of information:

  • Where should I, Git, find this other Git repository?
  • What commit(s) should I send and what names should I ask them to create or update?

The first question needs you to supply a URL. URLs are often long and fiddly (ssh://[email protected]/someverylongusername/someverylongreponame.git), and for that reason alone, Git has remotes: short names that let Git store URLs and other information. The standard first name origin is virtually always "the other Git repository" when you have exactly two Git repositories, e.g., the one on GitHub and the one on your own laptop.

Let's pause for a moment and look a bit at the git fetch command, which is the closest thing Git has to the opposite of git push. This too needs the URL of some other Git repository, but unlike git push, that's all it really needs. We can run:

git fetch origin

and that tells our Git software to call up some other Git software, e.g., over at GitHub, and look into the repository whose URL we had Git save under the name origin. Git will collect, from that repository, all its branch names (and by default some or all of its tag names) and find which commits they have that are new to us and bring them over. Now we have all the commits we had before, plus any that were new to us, so we have everything! That's how Git works: you bring over (git fetch) everything and from then on you don't have to go out on the network ever again, except for two cases:

  • you want to give your new commits to someone else, or
  • you believe that, since the last time you got all their new-to-you commits, they've created even more commits, and you'd like to get those.

Since the fetch default is just "get everything", we can just:

git fetch origin

and be done with it. Our own Git updates our laptop Git repository such that our remote-tracking names—our origin/* names—now remember the latest commit in each of their branch names. If they have branches main and plugh, our origin/main and our origin/plugh are now updated. And that's it: git fetch doesn't do anything with these updates other than store them so that we can do something with them later, if and when we choose.

In the ancient past, git push was more like this and if you ran:

git push origin

Git would push all your branches—or, well, not exactly all: the old default was matching. We won't go into any detail here, but this turned out to be a horrible mistake, so Git 2.0 changed it.

The end result of this change is that you now have to type:

git push origin xyzzy

to tell Git to look at your branch named xyzzy, see if you have any commits that are new to them, package up and send over those commits if/as needed, and then ask them to set their xyzzy branch to match your xyzzy branch. Note the crucial assumption here that you and they both call this by the same name.

If, for whatever reason (pride? prejudice? Sense and sensibility? Persuasion?), you choose to use a different name in your Git repository than they are using in their Git repository, Git can handle that. To make that work, you run:

git push origin xyzzy:plugh

for instance, if your local name is xyzzy and their ("remote") name is plugh, such that the upstream of xyzzy would be origin/plugh if you choose to do that.

Typing in this kind of explicit git push command bypasses any push.default setting. So you can always use this version. But if you'd like to run git push without typing more stuff, you need to know what push.default does.

Being implicit via remote-tracking names

The remote-tracking names I mentioned above, origin/*, are your own Git's way of remembering the branch names found in some other Git repository. Side note: Git formally calls these remote-tracking branch names now; in the past, Git documentation was not very careful about what it called them, and a lot of people call them "remote branches", which is sort of sensible, but also misleading: what would you call the branch names in the remote repository? If you use "remote branches" to mean "branch names in the remote repository" and to mean "remote-tracking names my local repository", how do you tell them apart?

Typically, you want the upstream of some branch name B to be origin/B in your own repository. Using git branch --set-upstream-to=origin/B B will set the upstream of B appropriately. Without a branch name, as in git branch --set-upstream-to=origin/xyzzy, this will set the upstream of the current branch, which then should probably be xyzzy.

There's a bit of a chicken-and-egg problem here. Suppose you create a new branch, feature/tall, for writing code for making the robot taller. This branch is literally new, as in you just created it. There's no feature/tall over on the origin Git repository. So there's no origin/feature/tall in your repository. That means Git won't let you set the upstream of feature/tall to origin/feature/tall.

Fortunately, there's a way to have git push itself run the git branch --set-upstream-to command for you:

git push -u origin feature/tall

tells your Git, explicitly (see above about being explicit), that you'd like your new feature/tall commits sent to the other Git repository and you'd then like your Git to ask their Git to set their feature/tall to remember the new commits. If they say OK to this request, your Git then creates your origin/feature/tall, since they said OK; and having worked, your own Git then runs git branch --set-upstream-to=origin/feature/tall for you, all in one convenient glorp.

Alternative push.default settings

There are currently five valid values here:

  • simple: describe above.
  • current: uses the current branch name on both sides, so git push origin pushes whatever your current branch name is, to the same name in the other repository.
  • upstream: uses the current branch name's upstream setting. This is the same as simple except that it removes the "name must match" requirement. This may be the setting you want, if you really want different names in the two different repositories.
  • matching: this makes Git behave the way it did before Git 2.0. You almost certainly don't want this setting.
  • nothing: this forces you to type in an explicit command. I tried this once and didn't like it, but there's no accounting for taste.

Forced update

I have a clue that when I did git pull the output had -> main (forced update)

This actually came from git fetch (the fetch step of the fetch-and-one-more-command that git pull runs). To understand what it means, we have to delve a bit deeper into how branch names really work.

In Git, branch names aren't really very important or relevant to Git. What Git cares about is commits (or more precisely, all four object types within the object database, but the one you see every day is the commit type). These objects—the commits included—are numbered, with big ugly random-looking numbers expressed in hexadecimal. Git literally needs these numbers to find the objects. So it's the numbers and the underlying objects that matter to Git.

But these numbers are pretty useless to humans:

$ git log
commit 2e71cbbddd64695d43383c25c7a054ac4ff86882 (...)
Author: Junio C Hamano ...
 ...

commit 805265fcf7a737664a8321aaf4a0587b78435184
Merge: 215ae4f264 cddd68ae33
Author: Junio C Hamano ...

Those big ugly hash IDs, whether full (2e71cbbddd64695d43383c25c7a054ac4ff86882 for instance) or abbreviated (cddd68ae33), are pretty useless to humans.

In each commit, Git stores:

  • a full snapshot of every file, in a special format that saves a lot of space; and
  • some metadata giving information like the name and email address of the author.

Inside the metadata for any one given commit, Git stores the hash ID(s) of previous or parent commit(s): the parent of 2e71cbbddd64695d43383c25c7a054ac4ff86882 above is 805265fcf7a737664a8321aaf4a0587b78435184.

Ignoring merge commits for now, what this does is enable Git to find every commit in the repository as long as it knows the hash ID of the last commit(s) in some chain(s). That is, suppose we have a small repository with perhaps just eight commits in it. Instead of keeping a list of every commit hash ID, we simply have Git record the hash ID of the latest commit—we'll call this one H for Hash—in some table somewhere:

            <-H   [table entry records H hash]

That "arrow" sticking out of H, pointing backwards, represents the parent hash stored in H. This will be some big ugly number again, but let's just call it G:

        <-G <-H

Since G is a commit, it too has a backwards arrow pointing to a still-earlier commit:

... <-F <-G <-H

and this goes on all the way back to the very first commit A, which, being first, has no parent and therefore doesn't have the arrow.

By remembering the hash ID of—or pointing to—commit H, Git can now find all commits:

...--F--G--H   <-- main

And as you've probably guessed by now, the entry in the table in this case is the branch name main.

Let's imagine now that we create a second branch name, maybe feature or topic or whatever, that *also points to commit H, like this:

...--F--G--H   <-- main, topic

If we're "on" branch topic and make a new commit, here's what happens:

  • Git saves a full snapshot of every file in the new commit, and
  • Git sets up the new commit's database with our name and email address and makes the new commit—let's call it I—point back to existing commit H.

Let's draw that in:

             I   <-- topic (HEAD)
            /
...--F--G--H   <-- main

To keep track of which name we're using, I added the special name HEAD here, which Git also does: the name HEAD holds the branch name, and the branch name holds the arrow, and that's how Git knows that commit I is the current commit and that topic is the current branch name.

Every time we make a new commit, Git makes the new commit point back to the current commit, then makes the current branch name point to the new commit. The result is that the branch name always points to the latest commit, and Git can always find all the previous commits by starting there and working backwards. And that's all there really is to a branch name—well, except for stuff like setting its upstream name.

Let's imagine we've done this for a couple of commits on a branch named br1, then switched back to branch main:

          I--J   <-- br1
         /
...--G--H   <-- main (HEAD)

We now make a new name br2, which also points to H, and then make two more commits:

          I--J   <-- br1
         /
...--G--H   <-- main
         \
          K--L   <-- br2 (HEAD)

Note that commits up through H are on all three branches, while commits I-J are only on br1 and commits K-L are only on br2, at the moment.

It's possible to take the existing branch name main and "slide it forward"—in the direction that the internal arrow commits don't go—so that main now selects either commit J or commit L. For instance:

git switch main
git merge --ff-only br1

would do that with commit J:

          I--J   <-- main (HEAD), br1
         /
...--G--H
         \
          K--L   <-- br2

Git calls this kind of name motion a fast forward. It's certainly a "forward" (against-the-backwards-arrows) move; "fast" might refer to the fast-forward mode of old cassette and video tape systems, now found as fast-forwards on DVRs and DVDs and the like. But in any case that's what Git calls this. It's a natural thing for branch names: it merely causes the branch name to accumulate more commits. Once we do this particular fast-forward, commits I-J are now on both main and br1.

If we decide to forcibly move the name main to point to L directly, though, that's a non-fast-forward operation. Git makes us use git reset (generally with --hard for this case):

git reset --hard br2

results in:

          I--J   <-- br1
         /
...--G--H
         \
          K--L   <-- main (HEAD), br2

and now commits I-J are no longer on main: they're only on br1 again. Commits up through L, except for I-J, are on both main and br2.

Updating a branch name in a "fast-forward" manner is something Git is quite willing to do. You ask Git to do it and Git just trusts that you mean it and does it. Updating a branch name in a "non-fast-forward" manner, however, generally requires using a "force" option. When using git push, that's precisely the difference between a regular push and a git push --force: a push that merely adds commits to some branch on the remote Git repository is accepted without force, but a non-fast-forward push, that removes commits from the end of some branch, requires the force flag.

Because remote-tracking names represent some other Git repository's branch names, these same rules apply to your own remote-tracking names. However, because remote-tracking names represent that other Git repository's branch names, your own Git software is normally willing to update each remote-tracking name by force if needed. So when you run:

git fetch origin

your Git calls up the other Git, at origin, and finds all their names and hash IDs. Your Git collects from the other Git any new commits it needs, and then your Git updates your remote-tracking names. If those updates do nothing at all, Git says nothing at all. For each update that adds commits, your Git prints the usual update line, e.g.:

   63bc7701e8..36f55fa649  next       -> origin/next

For an update that removes some commit(s), your Git prints:

   58abe16800...54681ef965 seen       -> origin/seen  (forced update)

Compare these two lines. The first one:

  • has no plus sign
  • has two dots
  • does not say (forced update)

The second one:

  • has a plus sign
  • has three dots
  • does say (forced update)

This means that the update to my origin/next was a fast-forward, and the update to my origin/seen was a non-fast-forward that required a forced update.

Now that the update has happened, though, a second git fetch run immediately has no new commits and nothing to update:

$ git fetch
$ 

Summary

Your "forced update" means someone (perhaps you) removed some commits from main on the remote, since you last ran git fetch to that remote.

Your error came about because the upstream of your current branch doesn't match up, name-wise, and you have fetch.default set to simple (or are using the default fetch.default).

CodePudding user response:

I've made repo with git clone --mirror, now with help of @torek in Why line with "fetch" in git config effects how `git branch --set-upstream-to` work? I understand it was root cause of results/issues.

  1. When troubleshooting I noticed in .git/config entry for main was different, merge = refs/remotes/origin/main vs. merge = refs/heads/main. Apparently it set main to track main on remote's remote (further down the chain), git pull considers it not same name as main (comparing taking into account translation of fetch = refs/*:refs/* where as with non-mirrored fetch = refs/heads/*:refs/remotes/origin/* remotes origin/main matches local's main.
  2. Other effect from the same cause I recalled is git-branch --set-upstream-to=main main produces effect of setting main branch to track origin's main and git-branch --set-upstream-to=origin/main main produces config change as described above with output Branch 'main' set up to track remote ref 'refs/remotes/origin/main'.
  3. With fetch set for mirroring, git pull is expected to update all local refs with ones from remote, hence forced update
  •  Tags:  
  • git
  • Related