threads / discuss / 64001

"lock file exists" when fetching in bare clone of repository

Subject: "lock file exists" when fetching in bare clone of repository

## tl;dr

16 messages between Aug 20, 2025 and Sep 2, 2025.

replies: 15people: 5as markdown or json

Joe Drew· Aug 20, 2025, 20:54 UTC · lore
Hi there!
I've got what I think is a bug in recent versions of git, at least as packaged for macOS by homebrew.
I'm cloning a fresh copy of a repository into a bare directory:
        $ mkdir tmp
        $ cd tmp
        $ git init --bare
        $ git remote add origin -- <VALID-URL>.git
        $ git fetch -f origin 'refs/heads/*:refs/heads/*' 'refs/tags/*:refs/tags/*'
I'm doing this in a sort of roundabout way because this is exactly what `go mod download` does.
The fetch fails, returning status 255, with the below error message:
        remote: Enumerating objects: 18733, done.
        remote: Counting objects: 100% (562/562), done.
        remote: Compressing objects: 100% (448/448), done.
        remote: Total 18733 (delta 399), reused 144 (delta 112), pack-reused 18171
        Receiving objects: 100% (18733/18733), 25.32 MiB | 7.93 MiB/s, done.
        Resolving deltas: 100% (14608/14608), done.
        From <VALID-URL>
        [.... quite a number of branches and tags .....]
        error: cannot lock ref 'refs/heads/ptv-2164': Unable to create '/Users/joe.drew/tmp/./refs/heads/ptv-2164.lock': File exists.
        Another git process seems to be running in this repository, e.g.
        an editor opened by 'git commit'. Please make sure all processes
        are terminated then try again. If it still fails, a git process
        may have crashed in this repository earlier:
        remove the file manually to continue.
This file doesn't exist; in fact, no such files exist:
        $ ls refs/heads
        $
However, the branch _does_ exist on the remote.
This error _does not_ happen in the Apple-supplied version of git: `git version 2.39.5 (Apple Git-154)`, but does in 2.51.0, which I installed with homebrew. (If this is a packaging error, I'll happily report to homebrew.)
I'd be more than happy to continue debugging this, though I can't share the repository as it's proprietary. I've also attached the git diagnostics, but they're not particularly helpful, I don't think.
[System Info]
git version:
git version 2.51.0
cpu: arm64
no commit associated with this build
sizeof-long: 8
sizeof-size_t: 8
shell-path: /bin/sh
feature: fsmonitor--daemon
libcurl: 8.7.1
zlib: 1.2.12
SHA-1: SHA1_DC
SHA-256: SHA256_BLK
default-ref-format: files
default-hash: sha1
uname: Darwin 24.6.0 Darwin Kernel Version 24.6.0: Mon Jul 14 11:30:40 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T6041 arm64
compiler info: clang: 17.0.0 (clang-1700.0.13.3)
libc info: no libc information available
$SHELL (typically, interactive shell): /opt/homebrew/bin/bash
[Enabled Hooks]
CONFIDENTIALITY NOTICE AND DISCLAIMER : This telecommunication, including any and all attachments, contains confidential information intended only for the person(s) to whom it is addressed. Any dissemination, distribution, copying or disclosure is strictly prohibited and is not a waiver of confidentiality. If you have received this telecommunication in error, please notify the sender immediately by return electronic mail and delete the message from your inbox and deleted items folders. This telecommunication does not constitute an express or implied agreement to conduct transactions by electronic means, nor does it constitute a contract offer, a contract amendment or an acceptance of a contract offer. Contract terms contained in this telecommunication are subject to legal review and the completion of formal documentation and are not binding until same is confirmed in writing and has been signed by an authorized signatory.
Jeff King· Aug 20, 2025, 21:33 UTC · re: Joe Drew · lore

Re: "lock file exists" when fetching in bare clone of repository

On Wed, Aug 20, 2025 at 08:54:37PM +0000, Joe Drew wrote:
Show 13 quoted lines
> The fetch fails, returning status 255, with the below error message:
> 
>         remote: Enumerating objects: 18733, done.
>         remote: Counting objects: 100% (562/562), done.
>         remote: Compressing objects: 100% (448/448), done.
>         remote: Total 18733 (delta 399), reused 144 (delta 112), pack-reused 18171
>         Receiving objects: 100% (18733/18733), 25.32 MiB | 7.93 MiB/s, done.
>         Resolving deltas: 100% (14608/14608), done.
>         From <VALID-URL>
> 
>         [.... quite a number of branches and tags .....]
> 
>         error: cannot lock ref 'refs/heads/ptv-2164': Unable to create '/Users/joe.drew/tmp/./refs/heads/ptv-2164.lock': File exists.
Show 13 quoted lines
> 
>         Another git process seems to be running in this repository, e.g.
>         an editor opened by 'git commit'. Please make sure all processes
>         are terminated then try again. If it still fails, a git process
>         may have crashed in this repository earlier:
>         remove the file manually to continue.
> 
> This file doesn't exist; in fact, no such files exist:
> 
>         $ ls refs/heads
>         $
> 
> However, the branch _does_ exist on the remote.

This is a wild guess, but: are there any case collisions with that branch name (e.g., PTV-2164 or something) in the upstream repo?

If so, and assuming you're on a case-insensitive filesystem, then the lock files would collide. You wouldn't see anything after the fact because git-fetch itself would create the colliding lockfile, and then clean it up after hitting the fatal error.

If that is the case, you can try using the reftables backend in v2.51.0. It doesn't use the filesystem for its ref storage or locking. Something like:

  git init --bare --ref-format=reftable
> This error _does not_ happen in the Apple-supplied version of git:
> `git version 2.39.5 (Apple Git-154)`, but does in 2.51.0, which I
> installed with homebrew. (If this is a packaging error, I'll happily
> report to homebrew.)

That is definitely weird, and not something I'd expect if it's just a case collision. Is it possible for you to build Git from source? If so, and the problem happens with your build of 2.51.0 but not v2.39.5, it would be very enlightening to see the results of "git bisect". We can provide more guidance if you need with that process.

-Peff
Patrick Steinhardt· Aug 21, 2025, 07:15 UTC · re: Jeff King · lore

Re: "lock file exists" when fetching in bare clone of repository

On Wed, Aug 20, 2025 at 05:33:23PM -0400, Jeff King wrote:
Show 37 quoted lines
> On Wed, Aug 20, 2025 at 08:54:37PM +0000, Joe Drew wrote:
> 
> > The fetch fails, returning status 255, with the below error message:
> > 
> >         remote: Enumerating objects: 18733, done.
> >         remote: Counting objects: 100% (562/562), done.
> >         remote: Compressing objects: 100% (448/448), done.
> >         remote: Total 18733 (delta 399), reused 144 (delta 112), pack-reused 18171
> >         Receiving objects: 100% (18733/18733), 25.32 MiB | 7.93 MiB/s, done.
> >         Resolving deltas: 100% (14608/14608), done.
> >         From <VALID-URL>
> > 
> >         [.... quite a number of branches and tags .....]
> > 
> >         error: cannot lock ref 'refs/heads/ptv-2164': Unable to create '/Users/joe.drew/tmp/./refs/heads/ptv-2164.lock': File exists.
> 
> > 
> >         Another git process seems to be running in this repository, e.g.
> >         an editor opened by 'git commit'. Please make sure all processes
> >         are terminated then try again. If it still fails, a git process
> >         may have crashed in this repository earlier:
> >         remove the file manually to continue.
> > 
> > This file doesn't exist; in fact, no such files exist:
> > 
> >         $ ls refs/heads
> >         $
> > 
> > However, the branch _does_ exist on the remote.
> 
> This is a wild guess, but: are there any case collisions with that
> branch name (e.g., PTV-2164 or something) in the upstream repo?
> 
> If so, and assuming you're on a case-insensitive filesystem, then the
> lock files would collide. You wouldn't see anything after the fact
> because git-fetch itself would create the colliding lockfile, and then
> clean it up after hitting the fatal error.
That's a likely failure mode indeed.
Show 5 quoted lines
> If that is the case, you can try using the reftables backend in v2.51.0.
> It doesn't use the filesystem for its ref storage or locking. Something
> like:
> 
>   git init --bare --ref-format=reftable

Note that you can also `git clone --ref-format=reftable` directly, so there's no need to go via git-init(1) first.

Show 10 quoted lines
> > This error _does not_ happen in the Apple-supplied version of git:
> > `git version 2.39.5 (Apple Git-154)`, but does in 2.51.0, which I
> > installed with homebrew. (If this is a packaging error, I'll happily
> > report to homebrew.)
> 
> That is definitely weird, and not something I'd expect if it's just a
> case collision. Is it possible for you to build Git from source? If so,
> and the problem happens with your build of 2.51.0 but not v2.39.5, it
> would be very enlightening to see the results of "git bisect". We can
> provide more guidance if you need with that process.

Curious indeed. Another relevant part could be the new batched mode that we have recently introduced in git-fetch(1). Previously we would've updated refs one by one, and potentially that led to us writing the same ref multiple times due to case-insensitivity? The result would be quite broken in that case, but at least the fetch would've finished. Does make me wonder though what subsequent fetches did, and whether we always tend to flip-flop the value of such a ref.

But with batched transactions that's not possible anymore, as we would indeed try to lock the same ref twice. Assuming that this really is the case, I wonder whether we should detect this case, tell the user that it's impossible to store all refs on their system, and then continue regardless while ejecting that specific ref from the transaction.

Patrick
Jeff King· Aug 21, 2025, 07:27 UTC · re: Patrick Steinhardt · lore

Re: "lock file exists" when fetching in bare clone of repository

On Thu, Aug 21, 2025 at 09:15:47AM +0200, Patrick Steinhardt wrote:
Show 18 quoted lines
> > > This error _does not_ happen in the Apple-supplied version of git:
> > > `git version 2.39.5 (Apple Git-154)`, but does in 2.51.0, which I
> > > installed with homebrew. (If this is a packaging error, I'll happily
> > > report to homebrew.)
> > 
> > That is definitely weird, and not something I'd expect if it's just a
> > case collision. Is it possible for you to build Git from source? If so,
> > and the problem happens with your build of 2.51.0 but not v2.39.5, it
> > would be very enlightening to see the results of "git bisect". We can
> > provide more guidance if you need with that process.
> 
> Curious indeed. Another relevant part could be the new batched mode that
> we have recently introduced in git-fetch(1). Previously we would've
> updated refs one by one, and potentially that led to us writing the same
> ref multiple times due to case-insensitivity? The result would be quite
> broken in that case, but at least the fetch would've finished. Does make
> me wonder though what subsequent fetches did, and whether we always tend
> to flip-flop the value of such a ref.

Ah, good catch. That would explain the behavior change perfectly. My money is on a case collision, then. :)

Show 5 quoted lines
> But with batched transactions that's not possible anymore, as we would
> indeed try to lock the same ref twice. Assuming that this really is the
> case, I wonder whether we should detect this case, tell the user that
> it's impossible to store all refs on their system, and then continue
> regardless while ejecting that specific ref from the transaction.

Maybe it is too optimistic to think that they will just start using reftable in Git 3.0, and we can avoid extra code to detect this?

-Peff
Patrick Steinhardt· Aug 21, 2025, 10:09 UTC · re: Jeff King · lore

Re: "lock file exists" when fetching in bare clone of repository

On Thu, Aug 21, 2025 at 03:27:06AM -0400, Jeff King wrote:
Show 32 quoted lines
> On Thu, Aug 21, 2025 at 09:15:47AM +0200, Patrick Steinhardt wrote:
> 
> > > > This error _does not_ happen in the Apple-supplied version of git:
> > > > `git version 2.39.5 (Apple Git-154)`, but does in 2.51.0, which I
> > > > installed with homebrew. (If this is a packaging error, I'll happily
> > > > report to homebrew.)
> > > 
> > > That is definitely weird, and not something I'd expect if it's just a
> > > case collision. Is it possible for you to build Git from source? If so,
> > > and the problem happens with your build of 2.51.0 but not v2.39.5, it
> > > would be very enlightening to see the results of "git bisect". We can
> > > provide more guidance if you need with that process.
> > 
> > Curious indeed. Another relevant part could be the new batched mode that
> > we have recently introduced in git-fetch(1). Previously we would've
> > updated refs one by one, and potentially that led to us writing the same
> > ref multiple times due to case-insensitivity? The result would be quite
> > broken in that case, but at least the fetch would've finished. Does make
> > me wonder though what subsequent fetches did, and whether we always tend
> > to flip-flop the value of such a ref.
> 
> Ah, good catch. That would explain the behavior change perfectly. My money is
> on a case collision, then. :)
> 
> > But with batched transactions that's not possible anymore, as we would
> > indeed try to lock the same ref twice. Assuming that this really is the
> > case, I wonder whether we should detect this case, tell the user that
> > it's impossible to store all refs on their system, and then continue
> > regardless while ejecting that specific ref from the transaction.
> 
> Maybe it is too optimistic to think that they will just start using
> reftable in Git 3.0, and we can avoid extra code to detect this?

Probably, yeah. I also think it shouldn't be too involved to have the logic in Git. We can mark transactions as allowing for failures now, so in the best case we'd just do that and then check whether we have any errors.

We could also think about generating a hint that tells the user exactly what's going on. E.g. something like this:

    You're on a case-insensitive filesystem, and the remote you are
    trying to fetch from has references that only differ in casing. It
    is impossible to store such references with the "files" backend. You
    can either accept this as-is, in which case you won't be able to
    store all remote references on disk. Or you can alternatively
    migrate your repository to use the "reftable" backend with the
    following command:
        git refs migrate --ref-format=reftable
    Please keep in mind that not all implementations of Git support this
    new format yet. So if you use tools other than Git to access this
    repository it may not be an option to migrate to reftables.

The last part is what I'm working on right now. libgit2 is fully functional now, but what is still lacking is reviews.

Patrick
Junio C Hamano· Aug 21, 2025, 16:05 UTC · re: Patrick Steinhardt · lore

Re: "lock file exists" when fetching in bare clone of repository

Patrick Steinhardt <ps@pks.im> writes:
>     You're on a case-insensitive filesystem, and the remote you are
>     trying to fetch from has references that only differ in casing. It
>     is impossible to store such references with the "files" backend. You
"backend." -> "backend on your system."
>     can either accept this as-is, in which case you won't be able to
>     store all remote references on disk. Or you can alternatively

I do not see the former as a viable choice, though. When this happens, the clone or fetch fails and the user cannot catch up to the upstream development, no? You have to futz with the fetch refspec to cause refs your filesystem cannot store ignored in order to make progress on other refs, but that is making the user do more than accepting this as-is.

Show 13 quoted lines
>     migrate your repository to use the "reftable" backend with the
>     following command:
>
>         git refs migrate --ref-format=reftable
>
>     Please keep in mind that not all implementations of Git support this
>     new format yet. So if you use tools other than Git to access this
>     repository it may not be an option to migrate to reftables.
>
> The last part is what I'm working on right now. libgit2 is fully
> functional now, but what is still lacking is reviews.
>
> Patrick
Patrick Steinhardt· Aug 22, 2025, 06:13 UTC · re: Junio C Hamano · lore

Re: "lock file exists" when fetching in bare clone of repository

On Thu, Aug 21, 2025 at 09:05:30AM -0700, Junio C Hamano wrote:
Show 17 quoted lines
> Patrick Steinhardt <ps@pks.im> writes:
> 
> >     You're on a case-insensitive filesystem, and the remote you are
> >     trying to fetch from has references that only differ in casing. It
> >     is impossible to store such references with the "files" backend. You
> 
> "backend." -> "backend on your system."
> 
> >     can either accept this as-is, in which case you won't be able to
> >     store all remote references on disk. Or you can alternatively
> 
> I do not see the former as a viable choice, though.  When this
> happens, the clone or fetch fails and the user cannot catch up to
> the upstream development, no?  You have to futz with the fetch
> refspec to cause refs your filesystem cannot store ignored in order
> to make progress on other refs, but that is making the user do more
> than accepting this as-is.

Oh, yes, right now it's too involved. What I'm proposing is to mark the transaction as allowed-to-fail, and in that case we'd be able to fetch and store refs in this case again. The result would still be broken, but it would be broken in a similar way as before. There's one difference though: we'd accept the _first_ conflicting ref now, whereas before we accepted the _last_ conflicting ref.

In any case, I very much feel like we should know to warn about this case and guide readers towards a proper solution.

Patrick
Karthik Nayak· Aug 22, 2025, 08:01 UTC · re: Patrick Steinhardt · lore

Re: "lock file exists" when fetching in bare clone of repository

Patrick Steinhardt <ps@pks.im> writes:
Show 25 quoted lines
> On Thu, Aug 21, 2025 at 09:05:30AM -0700, Junio C Hamano wrote:
>> Patrick Steinhardt <ps@pks.im> writes:
>>
>> >     You're on a case-insensitive filesystem, and the remote you are
>> >     trying to fetch from has references that only differ in casing. It
>> >     is impossible to store such references with the "files" backend. You
>>
>> "backend." -> "backend on your system."
>>
>> >     can either accept this as-is, in which case you won't be able to
>> >     store all remote references on disk. Or you can alternatively
>>
>> I do not see the former as a viable choice, though.  When this
>> happens, the clone or fetch fails and the user cannot catch up to
>> the upstream development, no?  You have to futz with the fetch
>> refspec to cause refs your filesystem cannot store ignored in order
>> to make progress on other refs, but that is making the user do more
>> than accepting this as-is.
>
> Oh, yes, right now it's too involved. What I'm proposing is to mark the
> transaction as allowed-to-fail, and in that case we'd be able to fetch
> and store refs in this case again. The result would still be broken, but
> it would be broken in a similar way as before. There's one difference
> though: we'd accept the _first_ conflicting ref now, whereas before we
> accepted the _last_ conflicting ref.

I replied to one of the earlier emails, but ended up only sending it to Peff by mistake. Re-sending parts of it now.

The fix itself isn't too involved:
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 088b52c740..5c31b02e6b 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -776,6 +776,8 @@ static enum ref_transaction_error
lock_raw_ref(struct files_ref_store *refs,
            goto retry;
        } else {
            unable_to_lock_message(ref_file.buf, myerr, err);
+			if (myerr == EEXIST)
+				ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
            goto error_return;
        }
    }


and this would fix the issue, when tested on a case-insensitive filesystem:

  $ ~/code/git/build/bin-wrappers/git fetch -f origin
'refs/heads/*:refs/heads/*'
  remote: Enumerating objects: 15, done.
  remote: Counting objects: 100% (15/15), done.
  remote: Compressing objects: 100% (9/9), done.
  remote: Total 15 (delta 3), reused 0 (delta 0), pack-reused 0 (from 0)
  Unpacking objects: 100% (15/15), 3.54 KiB | 3.54 MiB/s, done.
  From https://gitlab.com/knayakgl/case-insensitive-refs
   * [new branch]      SampLe     -> SampLe
   * [new branch]      Sample     -> Sample
   * [new branch]      main       -> main
   * [new branch]      sample     -> sample
   * [new branch]      SampLe     -> origin/SampLe
   * [new branch]      Sample     -> origin/Sample
   * [new branch]      main       -> origin/main
   * [new branch]      sample     -> origin/sample
  error: some local refs could not be updated; try running
   'git remote prune origin' to remove any old, conflicting branches
  error: fetching ref refs/heads/sample failed: refname conflict
  error: fetching ref refs/remotes/origin/Sample failed: refname conflict
  error: fetching ref refs/remotes/origin/sample failed: refname conflict

  $ git for-each-ref
  24abc5ed19b517692bb4245eb4185dd5a86d7376 commit	refs/heads/SampLe
  24abc5ed19b517692bb4245eb4185dd5a86d7376 commit	refs/heads/main
  24abc5ed19b517692bb4245eb4185dd5a86d7376 commit	refs/remotes/origin/SampLe
  24abc5ed19b517692bb4245eb4185dd5a86d7376 commit	refs/remotes/origin/main

But like you noted, we end up saving the first reference rather than the
last. Either isn't ideal, so while there is a change in behavior, this
is better than the complete failure of the command.

> In any case, I very much feel like we should know to warn about this
> case and guide readers towards a proper solution.
>
> Patrick
Junio C Hamano· Aug 22, 2025, 17:47 UTC · re: Karthik Nayak · lore

Re: "lock file exists" when fetching in bare clone of repository

Karthik Nayak <karthik.188@gmail.com> writes:
Show 16 quoted lines
> The fix itself isn't too involved:
>
> diff --git a/refs/files-backend.c b/refs/files-backend.c
> index 088b52c740..5c31b02e6b 100644
> --- a/refs/files-backend.c
> +++ b/refs/files-backend.c
> @@ -776,6 +776,8 @@ static enum ref_transaction_error
> lock_raw_ref(struct files_ref_store *refs,
>             goto retry;
>         } else {
>             unable_to_lock_message(ref_file.buf, myerr, err);
> +			if (myerr == EEXIST)
> +				ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
>             goto error_return;
>         }
>     }

We assume that the existing lock is what _we_ created to lock the ref in the other case, not somebody else locked-and-died some time ago, or somebody else locked-and-about-to-update-competing-with-us?

Without this change we'd return REF_TRANSACTION_ERROR_GENERIC; does the caller treat NAME_CONFLICT any specially? Or is the "fix" you talk about just about giving a different message and no other behaviour changes involved? I guess a more specific message that is 99% of the time more correct is an improvement over an overly generic "some error happened" message. But I thought the original issue was that the user cannot make any progress when the ref updates are transactional. Does returning NAME_CONFLICT from here tell the machinery that we are allowed to break transactional semantics somehow?

In any case, I wonder if refs_verify_refnames_available() should notice that we are using files backend on a case challenged filesystem, and change the behaviour a bit. This additional check needs to be implemented as a backend call via refs->be->something() to tighten the outcome. It appears to me that the function in the current form does not even notice a D/F conflict when there is a branch 'd' and the transaction requests to create a new branch 'D/f' on a case challenged system if files backend is in use, since the function is in the generic layer and behaves case sensitively (which is the right thing to do---we are talking about detecting backend specific glitches here).

Thanks.
Karthik Nayak· Aug 28, 2025, 13:51 UTC · re: Junio C Hamano · lore

Re: "lock file exists" when fetching in bare clone of repository

Junio C Hamano <gitster@pobox.com> writes:
Show 19 quoted lines
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> The fix itself isn't too involved:
>>
>> diff --git a/refs/files-backend.c b/refs/files-backend.c
>> index 088b52c740..5c31b02e6b 100644
>> --- a/refs/files-backend.c
>> +++ b/refs/files-backend.c
>> @@ -776,6 +776,8 @@ static enum ref_transaction_error
>> lock_raw_ref(struct files_ref_store *refs,
>>             goto retry;
>>         } else {
>>             unable_to_lock_message(ref_file.buf, myerr, err);
>> +			if (myerr == EEXIST)
>> +				ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
>>             goto error_return;
>>         }
>>     }
>

Let me preface my response by saying that in my quick to respond bug fix, I think the actual assigned error should be 'REF_TRANSACTION_ERROR_CREATE_EXISTS'.

> We assume that the existing lock is what _we_ created to lock the
> ref in the other case, not somebody else locked-and-died some time
> ago, or somebody else locked-and-about-to-update-competing-with-us?
>

We don't really change the path of exit, but rather just categorize the error. So by marking it as 'REF_TRANSACTION_ERROR_CREATE_EXISTS', we don't really say what kind of error it is.

In batched updates, the transaction allows failures unless a GENERIC error is observed, wherein the transaction would fail. By marking the error as 'REF_TRANSACTION_ERROR_CREATE_EXISTS', we allow batched updates to allow this failure and carry on. Which I think it makes sense for all the scenarios:

  - existing lock created by us due to being on a case insensitive FS
  - somebody else locked-and-died some time ago
  - somebody else locked-and-about-to-update-competing-with-us
Show 11 quoted lines
> Without this change we'd return REF_TRANSACTION_ERROR_GENERIC; does
> the caller treat NAME_CONFLICT any specially?  Or is the "fix" you
> talk about just about giving a different message and no other
> behaviour changes involved?  I guess a more specific message that is
> 99% of the time more correct is an improvement over an overly
> generic "some error happened" message.  But I thought the original
> issue was that the user cannot make any progress when the ref
> updates are transactional.  Does returning NAME_CONFLICT from here
> tell the machinery that we are allowed to break transactional
> semantics somehow?
>

Yes, without this, we'd return REF_TRANSACTION_ERROR_GENERIC. With this change, we'd return REF_TRANSACTION_ERROR_CREATE_EXISTS.

This error type is bubbled up to `files_transaction_prepare()` which tries to lock each reference update by calling `lock_ref_for_update()`. So if the locking fails, we check if the rejection type can be ignored, which is done by calling `ref_transaction_maybe_set_rejected()`.

Only during batched updates would errors be ignore and only for non-generic errors. So this change would specifically only apply for batched updates. Currently that is used by:

    1. git fetch
    2. git receive-pack
    3. git update-ref --batch-updates
And for all three scenarios I think it makes sense to add this in.
Show 14 quoted lines
> In any case, I wonder if refs_verify_refnames_available() should
> notice that we are using files backend on a case challenged
> filesystem, and change the behaviour a bit.  This additional check
> needs to be implemented as a backend call via refs->be->something()
> to tighten the outcome.  It appears to me that the function in the
> current form does not even notice a D/F conflict when there is a
> branch 'd' and the transaction requests to create a new branch 'D/f'
> on a case challenged system if files backend is in use, since the
> function is in the generic layer and behaves case sensitively (which
> is the right thing to do---we are talking about detecting backend
> specific glitches here).
>
> Thanks.
>

Absolutely, this whole thing with the files backend and case-insensitive file-systems is a mess. Yes we'd need to go around and fix that too.

Junio C Hamano· Aug 28, 2025, 16:16 UTC · re: Karthik Nayak · lore

Re: "lock file exists" when fetching in bare clone of repository

Karthik Nayak <karthik.188@gmail.com> writes:
Show 14 quoted lines
> This error type is bubbled up to `files_transaction_prepare()` which
> tries to lock each reference update by calling `lock_ref_for_update()`.
> So if the locking fails, we check if the rejection type can be ignored,
> which is done by calling `ref_transaction_maybe_set_rejected()`.
>
> Only during batched updates would errors be ignore and only for
> non-generic errors. So this change would specifically only apply for
> batched updates. Currently that is used by:
>
>     1. git fetch
>     2. git receive-pack
>     3. git update-ref --batch-updates
>
> And for all three scenarios I think it makes sense to add this in.
Thanks for an extra explanation.

These pieces of information would be necessary in the proposed log message when it becomes a real patch.

Karthik Nayak· Sep 1, 2025, 18:17 UTC · re: Junio C Hamano · lore

Re: "lock file exists" when fetching in bare clone of repository

Junio C Hamano <gitster@pobox.com> writes:
Show 21 quoted lines
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> This error type is bubbled up to `files_transaction_prepare()` which
>> tries to lock each reference update by calling `lock_ref_for_update()`.
>> So if the locking fails, we check if the rejection type can be ignored,
>> which is done by calling `ref_transaction_maybe_set_rejected()`.
>>
>> Only during batched updates would errors be ignore and only for
>> non-generic errors. So this change would specifically only apply for
>> batched updates. Currently that is used by:
>>
>>     1. git fetch
>>     2. git receive-pack
>>     3. git update-ref --batch-updates
>>
>> And for all three scenarios I think it makes sense to add this in.
>
> Thanks for an extra explanation.
>
> These pieces of information would be necessary in the proposed log
> message when it becomes a real patch.

Yup. I will send it in soon. Was a bit busy last week with the OSSE and Git mini summit.

Junio C Hamano· Aug 21, 2025, 15:47 UTC · re: Jeff King · lore

Re: "lock file exists" when fetching in bare clone of repository

Jeff King <peff@peff.net> writes:
Show 8 quoted lines
>> But with batched transactions that's not possible anymore, as we would
>> indeed try to lock the same ref twice. Assuming that this really is the
>> case, I wonder whether we should detect this case, tell the user that
>> it's impossible to store all refs on their system, and then continue
>> regardless while ejecting that specific ref from the transaction.
>
> Maybe it is too optimistic to think that they will just start using
> reftable in Git 3.0, and we can avoid extra code to detect this?

Or we can just tell them to use reftable way before Git 3.0; it is not like we feel that the reftable is way too unstable that we won't tag Git 3.0 until it gets ready---it is ready now.

Joe Drew· Aug 22, 2025, 13:28 UTC · re: Jeff King · lore

Re: "lock file exists" when fetching in bare clone of repository

> This is a wild guess, but: are there any case collisions with that
> branch name (e.g., PTV-2164 or something) in the upstream repo?
Yes, actually! The only difference is in case (ptv-2164 vs PTV-2164).
Show 5 quoted lines
> If that is the case, you can try using the reftables backend in v2.51.0.
> It doesn't use the filesystem for its ref storage or locking. Something
> like:
>
>  git init --bare --ref-format=reftable
Reftable does, in fact, solve this!
Show 10 quoted lines
>> This error _does not_ happen in the Apple-supplied version of git:
>> `git version 2.39.5 (Apple Git-154)`, but does in 2.51.0, which I
>> installed with homebrew. (If this is a packaging error, I'll happily
>> report to homebrew.)
>
> That is definitely weird, and not something I'd expect if it's just a
> case collision. Is it possible for you to build Git from source? If so,
> and the problem happens with your build of 2.51.0 but not v2.39.5, it
> would be very enlightening to see the results of "git bisect". We can
> provide more guidance if you need with that process.
I bisected (git 2.39.5 built from source _also_ doesn't have this issue), and found the first bad commit:
0e358de64a9e014575d11ef884bfc9beb931e37f is the first bad commit
commit 0e358de64a9e014575d11ef884bfc9beb931e37f
Author: Karthik Nayak <karthik.188@gmail.com>
Date:   Mon May 19 11:58:07 2025 +0200
    fetch: use batched reference updates
    The reference updates performed as a part of 'git-fetch(1)', take place
    one at a time. For each reference update, a new transaction is created
    and committed. This is necessary to ensure we can allow individual
    updates to fail without failing the entire command. The command also
    supports an '--atomic' mode, which uses a single transaction to update
    all of the references. But this mode has an all-or-nothing approach,
    where if a single update fails, all updates would fail.
CONFIDENTIALITY NOTICE AND DISCLAIMER : This telecommunication, including any and all attachments, contains confidential information intended only for the person(s) to whom it is addressed. Any dissemination, distribution, copying or disclosure is strictly prohibited and is not a waiver of confidentiality. If you have received this telecommunication in error, please notify the sender immediately by return electronic mail and delete the message from your inbox and deleted items folders. This telecommunication does not constitute an express or implied agreement to conduct transactions by electronic means, nor does it constitute a contract offer, a contract amendment or an acceptance of a contract offer. Contract terms contained in this telecommunication are subject to legal review and the completion of formal documentation and are not binding until same is confirmed in writing and has been signed by an authorized signatory.
Jeff King· Aug 26, 2025, 11:19 UTC · re: Jeff King · lore

Re: "lock file exists" when fetching in bare clone of repository

On Wed, Aug 20, 2025 at 05:33:23PM -0400, Jeff King wrote:
Show 10 quoted lines
> If so, and assuming you're on a case-insensitive filesystem, then the
> lock files would collide. You wouldn't see anything after the fact
> because git-fetch itself would create the colliding lockfile, and then
> clean it up after hitting the fatal error.
> 
> If that is the case, you can try using the reftables backend in v2.51.0.
> It doesn't use the filesystem for its ref storage or locking. Something
> like:
> 
>   git init --bare --ref-format=reftable
The response below came to me off-list. Here it is quoted with my reply:
On Sun, Aug 24, 2025 at 05:03:20PM +0100, Dave Evans wrote:
Show 22 quoted lines
> I'm sorry but I do not have an email client I could use to post to the
> git mailing list.
> Please could you post my thoughts on this to the list.
>
> https://lore.kernel.org/git/20250820213323.GA1667633@coredump.intra.peff.net/
>
> I use git 2.51.0 built using Macports on a very old version of macOS
>
> There are a couple of repos on github that were showing this problem:
> vectorgraphics/asymptote
> ghc/ghc
>
> Both of them were bare clones.
>
> I applied  --ref-format=reftable to the vectorgraphics/asymptote repo
> and a fetch
> went through to the end successfully
>
> The only snag I can see is that cgit no longer works. It says :
> Failed to open github/vectorgraphics/asymptote: No such file or directory
>
> The version of cgit is about 5 years old

I think that's all expected. Unlike most systems which call Git commands like for-each-ref, etc, to access the ref store, cgit is linked directly against Git's C code (something we don't support, but the cgit authors accepted as a risk). So an old version of cgit will link against an old version of Git, and won't know about reftable at all.

It _might_ not even be able to detect the directory as a git repo, though I thought the reftable format was designed to let even old versions of Git see the repo (e.g., the dummy value in HEAD).

-Peff
Patrick Steinhardt· Sep 2, 2025, 10:55 UTC · re: Jeff King · lore

Re: "lock file exists" when fetching in bare clone of repository

On Tue, Aug 26, 2025 at 07:19:52AM -0400, Jeff King wrote:
Show 33 quoted lines
> On Wed, Aug 20, 2025 at 05:33:23PM -0400, Jeff King wrote:
> > I'm sorry but I do not have an email client I could use to post to the
> > git mailing list.
> > Please could you post my thoughts on this to the list.
> >
> > https://lore.kernel.org/git/20250820213323.GA1667633@coredump.intra.peff.net/
> >
> > I use git 2.51.0 built using Macports on a very old version of macOS
> >
> > There are a couple of repos on github that were showing this problem:
> > vectorgraphics/asymptote
> > ghc/ghc
> >
> > Both of them were bare clones.
> >
> > I applied  --ref-format=reftable to the vectorgraphics/asymptote repo
> > and a fetch
> > went through to the end successfully
> >
> > The only snag I can see is that cgit no longer works. It says :
> > Failed to open github/vectorgraphics/asymptote: No such file or directory
> >
> > The version of cgit is about 5 years old
> 
> I think that's all expected. Unlike most systems which call Git commands
> like for-each-ref, etc, to access the ref store, cgit is linked directly
> against Git's C code (something we don't support, but the cgit authors
> accepted as a risk). So an old version of cgit will link against an old
> version of Git, and won't know about reftable at all.
> 
> It _might_ not even be able to detect the directory as a git repo,
> though I thought the reftable format was designed to let even old
> versions of Git see the repo (e.g., the dummy value in HEAD).

Yeah, in theory old versions of Git should still be able to discover the Git repository. They should bail out due to the "refstorage" extension, but other than that all files that need to exist for Git to discover the repository still exist with the reftable backend.

If that's not the case this is a bug we'd have to fix.
Patrick

← back to recent threads