'Why does HEAD in my worktree point to the top commit in master?
I created a worktree using git worktree add
and it was working as expected for a few days. Today, I wanted to check out some code at the previous revision, so I temporarily detached HEAD
:
git checkout head^
After I found what I needed, I tried to go back to the tip of my worktree branch:
git checkout <branchname>
But now, HEAD
is pointing to the top commit in my local master
branch. For example:
git show head
shows the most recent commit in master
.
Any ideas why this happened or how to fix it?
EDIT
I'm also seeing some weird, inconsistent behavior. For example, this:
> git reset --hard
HEAD is now at 4f1666467017
> git rev-parse head
e4fab17872cce2f6f37f5b596c7ed2739b135f3f
Solution 1:[1]
TL;DR
Don't spell it head
(lowercase). Spell it HEAD
(all uppercase). Git is case-sensitive, even where typical Windows and macOS file systems are not.
(If typing that many uppercase letters is an issue, use @
, which is a one-letter synonym for HEAD
. This works everywhere in Git versions 2.0 and up, and almost everywhere even in the ancient versions of Git still found on some CentOS systems.)
Longer
The weirdness here is related to the fact that each work-tree has its own private HEAD
file. Different operations may find different HEAD
files by case-folding: there's a .git/worktrees/worktree/HEAD
file for the added work-tree, and a .git/HEAD
file for the main work-tree. When operation A picks .git/HEAD
to match head
while operation B picks .git/worktrees/worktree/HEAD
, you see exactly this behavior. Using all-capitals HEAD
makes Git do the right thing.
In particular, git reset --hard
internally uses HEAD
in all capitals, which will refer to the per-work-tree HEAD
, while git rev-parse head
uses lowercase head
, which will refer to the .git/HEAD
file.
Attached vs detached HEAD
You can check out any historical commit by its raw hash ID:
git checkout a123456
(or in modern Git since 2.23, git switch --detach hash
: note the requirement that you add --detach
, which helps avoid accidents.)
The result is what Git calls a detached HEAD. Normally the special name HEAD
(in all capital letters) is attached, to a branch name:
...--A--B--C--D <-- master (HEAD)
\
E--F--G <-- branch
(the uppercase letters here stand in for the actual commit hash IDs). By being attached like this, HEAD
means master
which means commit D
. So your current commit is now whatever commit D
's real hash ID is. If you run:
git checkout branch
you get:
...--A--B--C--D <-- master
\
E--F--G <-- branch (HEAD)
which means your current branch is branch
and your current commit is now G
.
When you pick a commit by hash ID, though—let's say commit E
—you make HEAD
point directly to that commit:
...--A--B--C--D <-- master
\
E <-- HEAD
\
F--G <-- branch
This is what Git calls "detached HEAD"; your current commit is commit E
.
Now, you didn't use a raw hash ID. Instead, you typed in head^
. But there are lots of ways to name commits. The raw hash ID is the lowest-level way: a hash ID always works and always means that one specific commit, but you can also have Git find the parent of some commit. That's what HEAD^
means: select the parent of the current commit. Suppose the current commit is G
because you're on branch
; then HEAD^
names commit F
, and after:
git checkout HEAD^
you will have:
...--A--B--C--D <-- master
\
E--F <-- HEAD
\
G <-- branch
The lowercase version head
typically works on Windows and macOS, but not on Linux. This is a side effect of the fact that Git sometimes stores these things in files (.git/HEAD
, and .git/refs/heads/master
, for instance). Using head
instead of HEAD
, Git might try to open .git/refs/heads/head
, which doesn't exist, but when Git tries to open .git/head
, it gets .git/HEAD
, which does exist. So on these machines, git checkout head^
means the same thing as git checkout HEAD^
: find the current commit, step back to its first parent, and check out that commit.
To re-attach your HEAD
to some branch, use git checkout branch-name
.
If you're not on Windows or macOS (or even if you are), note that you can also create a branch or tag named head
. If you do, things get a bit squirrelly, especially on Windows and MacOS, as there are now two ways to resolve head
: as the branch or tag, and as .git/HEAD
. It's best to just not do this. If you do it by mistake, simply delete the lowercase version (git branch -d head
for instance).
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |