I initially had two branches in bitbucket - master and feature ( 4/-2 commits as compared to master).
In process of deleting the feature branch - I have messed up with the origin/HEAD. Now my master is changed to HEAD and the feature branch is changed to the default branch master.
Please provide insights and suggestions to fix this.
Before | Current | Expected |
---|---|---|
master | HEAD | master |
feature | master | (delete branch) |
$ git branch -r
origin/HEAD -> origin/master
origin/master
CodePudding user response:
origin/HEAD
represents the default branch on the remote. origin
- is just alias to remote url. And master
- is just name. Git does not have some special meaning of master
. So your feature
branch can't become master
, actually - that does not make sense. But it could be merged into master
- which is apparently your case.
origin/HEAD -> origin/master
means, that your default remote branch is master
branch. And based on the output of git branch -r
it is seen, that on remote server you have only master
, no feature
anymore.
So if you merged feature
into master
, deleted feature
, and git branch -r
gives you the output which you posted - seems that everything is fine.
CodePudding user response:
Here's some additional background information to supplement kosist's answer (q.v.):
HEAD
, in any Git repository, is a very special name. It is normally a symbolic reference, which in effect means that it contains some other branch's name. This gets a bit more complicated on server Git repositories than on clients, but the general idea here is the same regardless of server vs client.When you run
git clone
, you make a new repository by cloning some existing repository. By default, this new repository copies all the commits from the original repository, and none of the branch names.
Of course, the new repository is a Git repository. This means it has a special name, HEAD
, that is normally a symbolic reference. This is your Git repository's HEAD
, and in your repository, you will create new branch names and switch to them, e.g.:
git switch -c newbranch
At this point your HEAD
refers to your new branch newbranch
.
The repository you cloned, which your Git repository calls origin
, still has all of its original branch names. It presumably still has the same branch as its current branch, so that its HEAD
still refers to its main
or master
or whatever.
We now get into one of the complications. A server repository is normally a "bare" repository, i.e., one with no working tree. We use git switch
or git checkout
to update the working tree in our (client) Git repository, and this changes which branch the name HEAD
remembers, in our (client) Git repository. If you could log in to GitHub, or wherever the server may be, and cd path/to/repo.git
and git switch somebranch
there, that would change the branch name stored in the server's Git-repository-HEAD
—but if that server has only bare clones, the git switch
and git checkout
commands don't work in those clones.1
Nonetheless, most service providers (e.g., GitHub) provide some way to change the branch name stored in that service provider's repositories. On GitHub, you do this with their web interface, using their "set default branch name" pages. (You can also use the gh
cli, if you install that.) The only reason to do this is that when users run:
git clone <url>
the new branch they get in their new clone is a copy of the default branch. However, if they write:
git clone -b develop <url>
the new branch they get in their new clone is develop
(a copy of the develop
branch in the server's clone).
So, in short (if it's not too late), the point of setting HEAD
in a server repository is to set the default branch that people running git clone
from some client will get. And that's all it's good for ... well, almost all.
Now, as you probably already know and I just said, when you clone some other repository (e.g., https://github.com/git/git.git), you get all their commits and none of their branches and then your own Git software creates one branch—the default branch, unless you used -b
—but that's not the full story. The full story goes on to mention that each of their branch names becomes one of your remote-tracking names. That is, because this Git-repository-for-Git has branches named seen
and next
for instance, you get origin/seen
and origin/next
in your clone. The really full story then has to go on to add more conditionals here, since --single-branch
or --depth
can turn off this behavior, but we'll stop at this point and just claim this:
- All of their branch names become remote-tracking names in your clone.
So if they have a master
or main
(the Git repository for Git now has both), and a seen
and a next
for instance, you get an origin/master
or origin/main
and an origin/seen
and an origin/next
. So here's Git's somewhat dim-bulb version of a bright idea: They have a HEAD
, so why not make an origin/HEAD
too?
That's exactly what your origin/HEAD
is, or is supposed to be: a copy of their HEAD
. Their HEAD
is a symbolic reference to their main
, so your origin/HEAD
is now a symbolic reference to your origin/main
. Or, their HEAD
is a symbolic reference to their ice-cream-is-tasty
branch, so your origin/HEAD
is now a symbolic reference to your origin/ice-cream-is-tasty
name.
Note that your origin/*
names are not branch names ... well, not exactly. I like to call them remote-tracking names. Git officially calls them remote-tracking branch names, but they don't work like your own branch names. They stick around so that your Git can remember their Git's branch names. Your git fetch
command will update your remote-tracking names. Unless you run git fetch --prune
or git remote origin prune
or similar, though, your Git software won't delete your remote-tracking name when they delete their branch name—so that's one way they don't work right, sort of—and you can't git switch
to one of these remote-tracking names, which is why they're not branch names.2
With all of this, you might expect that every time you run git fetch origin
, your Git would find out if they've moved their HEAD
from (say) their main
to (say) their develop
, and if so, your Git would make your origin/HEAD
a symbolic reference to your origin/develop
. That would brighten up Git's dim bulb a bit, so therefore it doesn't actually happen. Instead, your origin/HEAD
gets set up once, when you run git clone
, and is never touched again.
Well, that is, it isn't touched unless you run git remote set-head
. The git remote
command has a bunch of sub-commands, and the set-head
command is how you change your own origin/HEAD
for your remote named origin
. If you'd like your origin/HEAD
to refer to your origin/seen
, you run git remote set-head
, like this:
$ git branch -r
origin/HEAD -> origin/master
origin/main
origin/maint
origin/master
origin/next
origin/seen
origin/todo
$ git remote set-head origin seen
$ git branch -r
origin/HEAD -> origin/seen
origin/main
origin/maint
origin/master
origin/next
origin/seen
origin/todo
If you'd like your Git to call up their Git (over at origin
, i.e., to call up whatever Git-like software answers at the URL for the original repository) and ask them about their HEAD
and update your origin/HEAD
accordingly, you run git remote set-head origin --auto
:
$ git remote set-head origin --auto
origin/HEAD set to master
and now I'm back to the old origin/HEAD -> origin/master
setup.
1Well, not without a --work-tree
or equivalent, anyway. This lays a different trap for those working with server-side bare repositories, of course.
2Note that git switch
requires the --detach
option when using a remote-tracking name. The git checkout
command doesn't require the option, it just assumes it. Either way you end up in what Git calls detached HEAD mode, which is Git's weird way of saying that you are no longer on any branch at all. Since you aren't on a branch, when you use these names, they must not be branch names, even if Git calls them "remote-tracking branch names". The word branch in here is just plain misleading.
Besides a lot of pointless fussing about, what good is any of this?
Some things will show origin/HEAD -> origin/master
or whatever. We saw above that git branch -r
will do that, for instance. This is, at least potentially, stale information; you must run git remote set-head origin --auto
to update it. Then git branch -r
will only be a few seconds stale, instead of hours, days, weeks, or years stale (depending on how long ago you ran git clone
, vs how long ago you ran git remote set-head
).
It is information, stale or not, but is it any good? We now turn to the gitrevisions documentation, which is very important and which you should probably re-read every week until you're pretty familiar with the whole thing. It's very long and you should probably just study a paragraph or two each time. This documentation tells you how git log
, git checkout
/ git switch
, git branch
, git reset
, and so many other commands use the string arguments you give them to find at least one commit. For instance, when you run:
git log develop
how does Git know what commits to show? The answer starts with this gitrevisions documentation.
In general, you can give a raw commit hash ID to most Git commands—with the important exception of git switch
/ git checkout
, which do something different when you give them a branch name than they would do with a raw hash ID. Giving Git a raw hash ID means that commit, so if origin/main
means 30cc8d0f147546d4dd77bf497f4dec51e7265bd8
, git show origin/main
and git show 30cc8d0f147546d4dd77bf497f4dec51e7265bd8
do exactly the same thing.
Up near the top of this documentation page, under the SPECIFYING REVISIONS section, there's a six-step sequence listed this way:
<refname>, e.g., master, heads/master, refs/heads/master
A symbolic ref name. E.g.master
typically means the commit object referenced byrefs/heads/master
. If you happen to have bothheads/master
andtags/master
, you can explicitly sayheads/master
to tell Git which one you mean. When ambiguous, a<refname>
is disambiguated by taking the first match in the following rules:
- If ...
- otherwise ...
- otherwise ...
- otherwise,
refs/heads/<refname>
if it exists;- otherwise,
refs/remotes/<refname>
if it exists;- otherwise,
refs/remotes/<refname>/HEAD
if it exists.
I've left in only steps 4 through 6 here because they're the ones we're going to encounter now:
- If you say
git show master
orgit show main
, and there's a branch namemaster
ormain
, this matches the step 4 rule and that's the commit you see. - If you say
git show origin/main
and there's a remote-tracking namerefs/remotes/origin/main
, this matches the step 5 rule and that's the commit you see. - But if you just say
git show origin
and there's a remote-tracking namerefs/remotes/origin/HEAD
, this matches the step 6 rule, and that's the commit you see.
So the fact that there's an origin/HEAD
—assuming you haven't deleted it, which you can do with git remote set-head
as it has a --delete
option—means that just writing origin
where a commit hash ID is required causes your Git to find origin/HEAD
, which is presumably a symbolic reference to the default branch over on origin
.
That's it. That's the "good thing", if you consider it a good thing. You can use the name origin
to mean origin/HEAD
. Personally, I consider this a bad thing because it means that if you run:
git push origin origin
it works and the first origin
means the remote named origin
and the second origin
means the commit found by looking up refs/remotes/origin/HEAD
and this is horribly confusing to newbies, and of virtually no use to anyone who understands what I just said without having to plow through the Git manual pages multiple times.
(Is this a rant? Perhaps it is: I think Git is incredibly useful and has a lot of things going for it, and at the same time, it has some really bad user interface decisions embedded in it, that make it really hard for people to use. Each of these features has a reason for its existence, but taken as a whole, they're a nightmarish jumble of traps for the unwary. That's not how software ought to be, if possible. Still, Git is what it is; we just have to be careful how we use it, and—like C in so many ways—be careful which feature set we pick to use.)