Re: [PATCH v3 0/4] refs/files: fix issues with git-fetch on case-insensitive FS
- From
Karthik Nayak <karthik.188@gmail.com>
- Date
- Sep 17, 2025, 20:40 UTC
- Message-ID
- <CAOLa=ZSfqcaP1+yxGzY=kEZJPeUqkqm3rO1ak6QihCQ5urT-0A@mail.gmail.com>
- In-Reply-To
- <xmqqjz1xc8nm.fsf@gitster.g>
Junio C Hamano <gitster@pobox.com> writes:
Show 16 quoted lines
> Karthik Nayak <karthik.188@gmail.com> writes: > >>> I've noticed another issue though, at the moment it's possible to create >>> a ref like `refs/heads/foo.Lock`. I can image this gives issue when the >>> remote has `refs/heads/foo` and `refs/heads/foo.Lock` and you pull those >>> in on a case-insensitive FS. Unfortunately I wasn't able to verify this. >>> But anyhow, I don't think that any reason to hold back on this current >>> patch series. I approve. >> >> We don't fetch locks from remote. The locking mechanism is simply a >> construct used to update files locally in race-free manner. Locking a >> file ensures no other concurrent writes can happen. > > I am not Toon, but I think Toon meant "foo.Lock" to be a funnily > named but a valid branch the remote has. >
Oops. Thanks for explaining.
> Doesn't the remote advertise refs/heads/foo and refs/heads/foo.Lock > in such a case? And we can ask for both of them. When updating > foo, we would locally create "refs/heads/foo.lock" and then rename > it to refs/heads/foo", right?
Yeah with batched updates, it would
1. create 'refs/heads/foo.lock' 2. before creating 'ref/heads/foo.Lock.lock', it would try to verify if 'refs/heads/foo.Lock' already exists. Which it does from #1, so not create it at all.
To be honest, I'm not sure what the best way to solve this would be for case-insensitive filesystems using the files backend.
> I think Toon's point was what happens > when the fetch of "foo.Lock" from there somehow has completed first.
That shouldn't happen AFAIK since the reference updates are ordered lexicographically.
> Or we can simply fetch foo.Lock branch and then foo branch in a > separate invocation of "git fetch"---now wouldn't the second > invocation have trouble creating the lock file foo.lock for foo on > certain filesystems?
So if we:
1. fetch 'refs/heads/foo.Lock' to create 'refs/heads/foo.Lock' locally 2. fetch 'refs/heads/foo' after
#2 would fail since it would determine that 'refs/heads/foo.lock' already exists. This would be the same behavior without batched updates too though.
---
Overall, I think the only issue left would be when you try to fetch 'refs/heads/foo' and 'refs/heads/foo.Lock' together on a case-insensitive filesystem with files backend. For such a system, with batched updates, the latter reference wouldn't be created.
Thinking more about it, without batched updates, both would be created, since we create the locks individually in separate transactions. But after the first fetch, we would never be able to update 'refs/heads/foo' since it would always think that a lock file for that reference exists. So it was always broken in some sense. Sigh!
>> In-short when you fetch references, the prepare stage of the reference >> transaction will create the necessary lock files. This locks in the >> updates with guarantee that no other process can update/create the refs. >> The commit phase simply removes the lock files post updating the refs.