threads / discuss / 61698

Confusing treatment of "head" in worktree on case-insensitive FS

Subject: Confusing treatment of "head" in worktree on case-insensitive FS

## tl;dr

4 messages between Jun 29, 2024 and Jul 1, 2024.

replies: 3people: 3as markdown or json

Julia Evans· Jun 29, 2024, 14:39 UTC · lore
Hello,

When I run `git rev-parse head` inside a worktree on a case insensitive filesystem, the result is different from `git rev-parse HEAD`.

Steps to reproduce, on a case insensitive filesystem:

$ git init $ git commit --allow-empty -m'test' $ git worktree add /tmp/myworktree $ cd /tmp/myworktree $ git commit --allow-empty -m'test' $ git rev-parse head adf59ca8da0ee5c4eb455f87efecc6c79eaf030f $ git rev-parse hEAd adf59ca8da0ee5c4eb455f87efecc6c79eaf030f $ git rev-parse HEAD fbb28196d08d74aa61f65e5cee3cb11cc24c338a

This also happens when using any other command involving "head", like `git show head`, `git diff head`, etc.

What did you expect to happen?

If `git rev-parse head` returns anything at all (of course on a case-sensitive filesystem it just returns an error), I'd expect it to be the same as `git rev-parse HEAD`.

I accidentally got into the happen of using `git diff head` instead of `git diff HEAD` on a case insensitive filesystem and was thrown off by this when I ran into it.

What happened instead?

When in a worktree, `head` appears to get resolved to the value of `HEAD` in `git rev-parse --git-common-dir`, while `HEAD` gets resolved to the value of `HEAD` in the worktree.

[System Info] git version: git version 2.39.2 (Apple Git-143) cpu: arm64 no commit associated with this build sizeof-long: 8 sizeof-size_t: 8 shell-path: /bin/sh feature: fsmonitor--daemon uname: Darwin 23.4.0 Darwin Kernel Version 23.4.0: Fri Mar 15 00:12:41 PDT 2024; root:xnu-10063.101.17~1/RELEASE_ARM64_T8103 arm64 compiler info: clang: 14.0.3 (clang-1403.0.22.14.1) libc info: no libc information available $SHELL (typically, interactive shell): /usr/bin/fish

Jeff King· Jul 1, 2024, 03:31 UTC · re: Julia Evans · lore

Re: Confusing treatment of "head" in worktree on case-insensitive FS

On Sat, Jun 29, 2024 at 10:39:29AM -0400, Julia Evans wrote:
> When I run `git rev-parse head` inside a worktree on a case insensitive filesystem,
> the result is different from `git rev-parse HEAD`.

This is a known issue in general. It can get even more confusing when you pack refs, since we do case-sensitive matches within the packed-refs file, and then the filesystem gives us case-insensitive matching for loose refs.

I don't know of a good short-term solution, but the long-term one is for us to eventually move to ref storage that doesn't rely on the filesystem (like the recent reftables work).

With the current tip of 'master', you can do:
  git init --ref-format=reftable

I think initial support there shipped in v2.45.0, and I think things should mostly work. But I won't be too surprised if you find a corner case or rough edge somewhere. Bug reports welcome. ;)

Show 13 quoted lines
> Steps to reproduce, on a case insensitive filesystem:
> 
> $ git init
> $ git commit --allow-empty -m'test'
> $ git worktree add /tmp/myworktree
> $ cd /tmp/myworktree
> $ git commit --allow-empty -m'test'
> $ git rev-parse head
> adf59ca8da0ee5c4eb455f87efecc6c79eaf030f
> $ git rev-parse hEAd
> adf59ca8da0ee5c4eb455f87efecc6c79eaf030f
> $ git rev-parse HEAD
> fbb28196d08d74aa61f65e5cee3cb11cc24c338a

I admit this is an unexpected case, as I'd expect both on-disk files to be spelled "HEAD". I didn't dig into the details, though, so it's possible there's something we could be doing differently or better. But I do think it's mostly the tip of the iceberg for case-insensitivity issues with refs.

-Peff
Phillip Wood· Jul 1, 2024, 13:17 UTC · re: Jeff King · lore

Re: Confusing treatment of "head" in worktree on case-insensitive FS

On 01/07/2024 04:31, Jeff King wrote:
Show 18 quoted lines
> On Sat, Jun 29, 2024 at 10:39:29AM -0400, Julia Evans wrote:
>> $ git init
>> $ git commit --allow-empty -m'test'
>> $ git worktree add /tmp/myworktree
>> $ cd /tmp/myworktree
>> $ git commit --allow-empty -m'test'
>> $ git rev-parse head
>> adf59ca8da0ee5c4eb455f87efecc6c79eaf030f
>> $ git rev-parse hEAd
>> adf59ca8da0ee5c4eb455f87efecc6c79eaf030f
>> $ git rev-parse HEAD
>> fbb28196d08d74aa61f65e5cee3cb11cc24c338a
> 
> I admit this is an unexpected case, as I'd expect both on-disk files to
> be spelled "HEAD". I didn't dig into the details, though, so it's
> possible there's something we could be doing differently or better. But
> I do think it's mostly the tip of the iceberg for case-insensitivity
> issues with refs.

I think what's happening is that the checks in is_current_worktree_ref() are case sensitive so "head" is not treated as local to the current worktree and ends up being resolved in the main worktree. I guess we could make those checks case-insensitive but as you say it'd only be dealing the tip of the iceberg.

On a related note do MacOs and Windows do any unicode normalization when looking up filenames? If so then that's probably another can of worms for filesystem based refs.

Best Wishes
Phillip
Jeff King· Jul 1, 2024, 18:28 UTC · re: Phillip Wood · lore

Re: Confusing treatment of "head" in worktree on case-insensitive FS

On Mon, Jul 01, 2024 at 02:17:21PM +0100, Phillip Wood wrote:
Show 25 quoted lines
> On 01/07/2024 04:31, Jeff King wrote:
> > On Sat, Jun 29, 2024 at 10:39:29AM -0400, Julia Evans wrote:
> > > $ git init
> > > $ git commit --allow-empty -m'test'
> > > $ git worktree add /tmp/myworktree
> > > $ cd /tmp/myworktree
> > > $ git commit --allow-empty -m'test'
> > > $ git rev-parse head
> > > adf59ca8da0ee5c4eb455f87efecc6c79eaf030f
> > > $ git rev-parse hEAd
> > > adf59ca8da0ee5c4eb455f87efecc6c79eaf030f
> > > $ git rev-parse HEAD
> > > fbb28196d08d74aa61f65e5cee3cb11cc24c338a
> > 
> > I admit this is an unexpected case, as I'd expect both on-disk files to
> > be spelled "HEAD". I didn't dig into the details, though, so it's
> > possible there's something we could be doing differently or better. But
> > I do think it's mostly the tip of the iceberg for case-insensitivity
> > issues with refs.
> 
> I think what's happening is that the checks in is_current_worktree_ref() are
> case sensitive so "head" is not treated as local to the current worktree and
> ends up being resolved in the main worktree. I guess we could make those
> checks case-insensitive but as you say it'd only be dealing the tip of the
> iceberg.

Ah, right, that makes perfect sense (well, why it happens that way, not from the perspective of a user :) ).

So one thing we could do (but I am not sure is wise) is for those checks to become case-insensitive for a case-insensitive ref store. And then at least if you use consistent case when writing refs, you should get reasonable behavior (whereas if you make "hEaD" and "HEAD" yourself, all bets are off). But I'd worry about opening up even more weird corner cases. And you can already avoid this problem (I think) by using the case-sensitive spelling "HEAD" on lookups.

> On a related note do MacOs and Windows do any unicode normalization when
> looking up filenames? If so then that's probably another can of worms for
> filesystem based refs.

At least macOS does. That's why we have all of the precompose-unicode code, which tries to normalize arguments to match what the OS will do. In theory we could do something like that for case normalizing, but I don't think it's nearly as simple.

For a read, normalizing "head" to "HEAD" on a case-insensitive filesystem is OK, since the OS will return the same set for each group.

But writing is harder. The unicode normalization in the filesystem is not "preserving". So if I pass in a decomposed string, the filesystem is going to silently convert it to the precomposed form anyway. But case is usually preserving. So if I write "hEaD", I'll get that in the filesystem, and not actually "HEAD". I dunno. Maybe it would be OK if we did that only for root refs which would otherwise be forbidden. But it really feels like opening up a can of complexity worms and corner cases.

-Peff

← back to recent threads