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, sogit 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 assimple
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 commitH
.
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.
- When troubleshooting I noticed in
.git/config
entry formain
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 asmain
(comparing taking into account translation offetch = refs/*:refs/*
where as with non-mirroredfetch = refs/heads/*:refs/remotes/origin/*
remotesorigin/main
matches local'smain
. - Other effect from the same cause I recalled is
git-branch --set-upstream-to=main main
produces effect of settingmain
branch to track origin's main andgit-branch --set-upstream-to=origin/main main
producesconfig
change as described above with outputBranch 'main' set up to track remote ref 'refs/remotes/origin/main'
. - With fetch set for mirroring,
git pull
is expected to update all local refs with ones from remote, henceforced update