Summary
My advice is to ignore this entry. You don't care about it, so ... don't care about it.
Long
Every Git repository must have a HEAD
setting. The HEAD
in a normal repository—the kind in which you do your own work, for instance—stores the current branch name, or else the repository is in what Git calls detached HEAD mode and HEAD
stores the current commit hash ID as a raw big-ugly-hash-ID. So HEAD
in this kind of repository represents the currently checked out commit. As there is always—well, almost always—a currently checked out commit, Git can simply read HEAD
to find out which branch name or hash ID locates that commit.1
But even in a "bare" clone, which does not have a currently-checked-out commit—it has no working tree so that it literally can't have a checked out commit—Git still requires that HEAD
exist, and contain a branch name. That name represents the default branch name, as Kim noted in a comment. This should be the name of some branch that does exist in that repository.2 The point of this default branch name is simple: you, as a client, will run:
git clone ssh://git@github.com/user/repo.git
for example, or a similar git clone
request to some other URL, perhaps on GitHub or perhaps elsewhere. You can add:
-b somebranch
to tell your git clone
operation that it should do a git switch soembranch
immediately after cloning: that will be the (only) actual branch name in your new clone. But if you don't bother with the -b
option—and most people mostly don't—your own Git software will ask the hosting site's Git software: Say, which branch name do you recommend? The answer that comes back is the branch name stored in the HEAD
.3
The point of doing all this is simple enough. Git can work with no branch names at all. Git does not actually need branch names. Git only needs commits and their big ugly hash IDs. It's us humans, not Git, who need branch names: the hash IDs are too big and ugly for us. But when you make a new clone, Git turns all the original repository's branch names into your own remote-tracking names: their main
or master
becomes your origin/main
or origin/master
, for instance. That creates room for you to have your branch names, which may well hold different big-ugly-hash-IDs.
So, when you first clone some existing Git repository—let's say it is one with four branch names in it—you'll have four remote-tracking names, one for each of the original repository's branch names. You will have zero of your own branch names. That's not a good start, because humans don't work well without the names. So the last step of git clone
is to create one new branch name, in your new clone, and then switch to that branch name by checking out the right commit. Which commit is the right commit? Well, obviously,4 it's the commit obtained by using the corresponding remote-tracking name:
- They have branches
br1
, br2
, br3
, and main
.
- Their
br2
means commit c3ff4cec66ec004d884507f5296ca2a323adbbc5
.
- Therefore your
origin/br2
means commit c3ff4cec66ec004d884507f5296ca2a323adbbc5
.
- Therefore, if you said
-b br2
, the correct commit hash ID for your newly created br2
branch is c3ff4cec66ec004d884507f5296ca2a323adbbc5
.
If you didn't say -b br2
, your Git asks their Git which of the four names they recommend, and as long as they don't come back and give your Git a wrong one like master
(see footnote 2 and Google hosting), your Git can use the corresponding origin/main
or origin/br1
or whatever that your Git just created to get the right hash ID.
1The almost always case occurs when the repository is on what Git calls an orphan branch. Here, HEAD
still contains a branch name, but it's the name of a branch that does not yet exist.
2If the HEAD
of a bare repository contains the name of a branch that does not yet exist, that bare repository is "on" an orphan branch, as noted in footnote 1. This is not harmful to the bare repository, but it does mean that when you use git clone
to clone the bare repository, the other Git software gets, from the bare clone, the name of a branch that doesn't exist. That other Git software—on the client that's making the clone—then tries to check out that branch by its name, and can't, because there is no such name. This causes the client Git to spit out some annoying error messages, confusing the users at the client end. Hence the advice that the bare repository contain the name of a branch that does exist, here.
Web hosting sites like Bitbucket, GitHub, and GitLab must provide some way—some hosting-provider specific method—for you to set the "default branch", as there's nothing built in to the Git push protocol to do this. If they don't provide that, you may be stuck with an unwanted branch name. Google, for some reason, fail to provide such a way, and you're stuck with having to name a branch master
or else have client clones produce this annoying message. It doesn't mean you can't use the hosting service, but it sure is annoying.
3If the hosting site has a detached HEAD, or is running a truly ancient Git server, there's no way for it to provide a branch name here. In that case, your own local Git software can fall back on a compiled-in default name, and if even that doesn't work, it will complain and not check anything out after all, printing out an annoying message instead. You will be forced to run your own git checkout
or git switch
command to create the first and only-so-far branch in your new repository. See also footnote 2.
4It's clearly obvious to everyone who's been using Git for a decade, which makes it obvious and trivial like in your typical math proof. See also this list of proof techniques.
OK, but what does this have to do with origin/HEAD
?
Let's now look at origin
: origin
is the remote name, and is the source of the origin/
part in origin/br1
, origin/br2
, origin/br3
, and origin/main
.5 The name origin
, stored in your .git/config
(which you can look at, or even edit, if you like—it's a plain-text file—just be careful when editing it!)—serves a number of functions. The primary two, which are always there immediately upon the creation of a remote like origin
, are these:
- It stores a url, so that
git fetch origin
knows where to reach the other Git software.
- It stores a default fetch refspec, so that
git fetch origin
knows which names to create or update in your own repository. Using --single-branch
at git clone
time alters this default fetch refspec.
We won't go into any detail on the fetch refspec here, and aren't too worried about the URL either, but I'll note that if the URL changes, it's easy to edit .git/config
and fix it. You can do this instead of using git remote set-url origin new-url
, as long as you're careful to use an editor that preserves the .git/config
file format (a modified INI format; must be saved as UTF-8).
In effect, then, origin
represents the other Git repository: the one you just cloned. That's why you have remote-tracking names: each one represents a branch name in that other repository.
Your own Git has its own HEAD
, but if that other Git over at origin
is a repository—and it must be, because you just cloned it—then it too must have its own HEAD
, representing the "default branch" in that repository. The authors of Git decided that it would make sense to copy their HEAD
to your origin/HEAD
.
Now, the mechanism by which HEAD
contains a branch name—assuming, of course, that HEAD
does contain a branch name, which gets us right back to footnotes 2 and 3 when we're talking about the server-side Git repository—is what Git calls a symbolic reference. So your own Git software uses another symbolic reference in your clone to represent the symbolic reference that, we assume, exists in the origin
clone. If they have HEAD -> main
, then, you should have origin/HEAD -> origin/main
. If they have HEAD -> master
, you should have origin/HEAD -> origin/master
.
Your Git (your software working with your repository) simply re-creates the adjusted symbolic reference that your Git assumes their Git has, based on the default-branch information your Git got from their Git. And that's what your origin/HEAD
represents after git clone
.
5You can, if you like, use some name other than origin
. You can also, or instead, add more remotes to your clone after the first standard origin
remote. But we won't get into these details here.
But what use is origin/HEAD
?
None.
Okay, that's not really true: the correct answer is almost none.
You can change the symbolic reference stored in your own origin/HEAD
any time you like, using:
git remote set-head origin <name>
You can delete origin/HEAD
using:
git remote set-head --delete origin
You can have your Git call up the origin
Git software, as if doing the initial clone, and ask them what their HEAD
is now and have your Git update your origin/HEAD
appropriately with:
git remote set-head --auto origin
This lets you change your own origin/HEAD
to whatever you like, which may not be the same as what they actually have in their HEAD
. Git's rule here is a little odd, but does make sense, sort of: all your origin/*
names are yours, they're just automatically matched up to theirs by default, except that you have to manually use --auto
to automatically match up origin/HEAD
...?
But what good is something that you can set, if you can't query it? Well, that's where the real weirdness comes in. You can query it, in several ways:
- You can run
git branch -r
or git branch -a
and see it. What good is that? I don't know, perhaps you can come up with something.
- You can run
git rev-parse
to read it, in one of three ways; see below.
- Last, you can use the word
origin
, naked like this, to read it ... sometimes. Sometimes, the word origin
means the remote. Sometimes, it means origin/HEAD
. When does it mean which? That's the tricky part.
Using git rev-parse
As noted above, what Git really needs, to get its Gitty bits of dirty-work done, are hash IDs. Branch names—and other names like tag names—are just clever methods by which Git allows us humans to use human-oriented names to find the hash IDs.
The git rev-parse
command is a general purpose plumbing command6 in Git that lets you do the same things other Git commands do, to work with branch and tag and other names. Because it's a very central Git command, it does a lot of other things too; we're only going to worry on the branch and remote-tracking name oriented ones here. We will run it like this:
$ git rev-parse --symbolic-full-name origin/HEAD
refs/remotes/origin/master
$ git rev-parse --abbrev-ref origin/HEAD
origin/master
These show two ways to examine the actual origin/HEAD
in a clone of the Git repository for Git. As you can see from the above, in my clone, origin/HEAD
is a symbolic name for origin/master
(whose full name is refs/remotes/origin/master
, but we can usually abbreviate as just origin/master
).
To get the hash ID of origin/master
, we can again use git rev-parse
:
$ git rev-parse origin/master
c000d916380bb59db69c78546928eadd076b9c7d
So rev-parse
can find the raw hash ID of a commit, turning a name—a branch name like main
or master
, or a remote-tracking name like origin/master
—into a commit hash ID. That's what has happened here. But suppose I give git rev-parse
the name origin
? That's not a branch name, not a tag name, and not even a remote-tracking name. Let's try it:
$ git rev-parse origin
c000d916380bb59db69c78546928eadd076b9c7d
Let's try this too:
$ git rev-parse origin/HEAD
c000d916380bb59db69c78546928eadd076b9c7d
OK, this last one makes sense: origin/HEAD
is a remote-tracking name that is a symbolic reference to origin/master
, which in turn means c000d916380bb59db69c78546928eadd076b9c7d
. But why did origin
, without /HEAD
or /master
or whatever added, also turn into c000d916380bb59db69c78546928eadd076b9c7d
?
The answer is in the gitrevisions documentation, which lists a six-step process for resolving a potentially ambiguous name. If we give the string zog
to git rev-parse
, is that a branch name? Is it a tag name? Is it a remote-tracking name ... well, it's definitely not a remote tracking name, as those have both a remote and a name, and zog
has no slash in it. But to answer the question of what kind of name it is and whether it's valid at all—maybe it's just an error—git rev-parse
goes through the six steps listed. Follow the link above and scroll down a little until you see the text that begins with:
<refname>, e.g. master, heads/master, refs/heads/master
A symbolic ref name. ...
Read through the six steps. Note that step 6 reads:
- otherwise,
refs/remotes/<refname>/HEAD
if it exists.
Note carefully the order of the six steps. Step 3 mentions refs/tags/<refname>
; refs/heads/<refname>
is later, at step 4. This means that if zog
is both a tag and a branch name, git rev-parse
will use the tag name!
This is in fact true of most Git commands. The git branch
and git switch
commands are some of the big exceptions here: they know, or at least assume, that you're probably giving them a branch name, so they generally try names as branch names before trying them in any other way.
This fact—that tag names take priority, except when they don't—is one of several reasons to avoid having, e.g., zog
be both a branch name and a tag name. Git is a computer, following precise rules in a precise order—but each Git command can have different rules, so while Git won't get them mixed up, you, as a human, probably will.
In any case, the fact that step 6 exists, and if given origin
will look for refs/remotes/origin/HEAD
, means that you can—in some places—use the name origin
by itself to mean origin/HEAD
, which in turn means whatever name origin/HEAD
is a symbolic reference for. So that's a way you can use origin
. But sometimes, when you use origin
, Git will treat that as a remote, rather than as a name to resolve through git rev-parse
. As a human who can get confused about when a dual-use zog
-branch-and-zog
-tag gets used as a branch name or as a tag name, you as a human can also get confused about when origin
as a name gets used as a remote name, and when it's turned into origin/HEAD
. So just don't do that.
6Git divides its commands (not always entirely successfully) into "plumbing" commands that do some low-level task and "porcelain" commands that users run. A "porcelain" command might be implemented by three or more "plumbing" commands plus some kind of nicer user interface. The original git commit
worked this way, for example. See What does the term "porcelain" mean in Git? and Which are the plumbing and porcelain commands?
Conclusion
The above is really just a very long way to go into the fact that Git has all kinds of fancy stuff that you probably shouldn't use because it's a bad idea. Don't use these esoteric methods of working unless they solve some very specific problem you have that you can't solve some other, clearer way. This whole thing is almost certainly a bad idea. Git has to keep supporting it for backwards compatibility, but you don't have to use it. Don't use it.