'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