[PATCH v2 4/4] refs/files: handle D/F conflicts during locking
- From
Karthik Nayak <karthik.188@gmail.com>
- Date
- Sep 8, 2025, 12:37 UTC
- Message-ID
- <20250908-587-git-fetch-1-fails-fetches-on-case-insensitive-repositories-v2-4-b2eb2459befb@gmail.com>
- In-Reply-To
- <20250908-587-git-fetch-1-fails-fetches-on-case-insensitive-repositories-v2-0-b2eb2459befb@gmail.com>
The previous commit, added the necessary validation and checks for F/D conflicts in the files backend when working on case insensitive systems.
There is still a possibility for D/F conflicts. This is a different from the F/D since for F/D conflicts, there would not be a conflict during the lock creation phase:
refs/heads/foo.lock
refs/heads/foo/bar.lockHowever there would be a conflict when the locks are committed, since we cannot have 'refs/heads/foo/bar' and 'refs/heads/foo'. These kinds of conflicts are checked and resolved in `refs_verify_refnames_available()`, so the previous commit ensured that for case-insensitive filesystems, we would lowercase the inputs to that function.
For D/F conflicts, there is a conflict during the lock creation phase itself:
refs/heads/foo/bar.lock
refs/heads/foo.lockAs in `lock_raw_ref()` after creating the lock, we also check for D/F conflicts. To fix this, simply categorize the error as a name conflict. Also remove this reference from the list of valid refnames for availability checks.
By categorizing the error and removing it from the list of valid references, batched updates now knows to reject such reference updates and apply the other reference updates.
Fix a small typo in `ref_transaction_maybe_set_rejected()` while here.
Helped-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Karthik Nayak <karthik.188@gmail.com> --- refs.c | 8 +++++++- refs/files-backend.c | 2 +- t/t5510-fetch.sh | 20 ++++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/refs.c b/refs.c index 4c1c339ed9..ec4f0e9502 100644 --- a/refs.c +++ b/refs.c @@ -1223,7 +1223,7 @@ int ref_transaction_maybe_set_rejected(struct ref_transaction *transaction, return 0; if (!transaction->rejections) - BUG("transaction not inititalized with failure support"); + BUG("transaction not initialized with failure support"); /* * Don't accept generic errors, since these errors are not user @@ -1232,6 +1232,12 @@ int ref_transaction_maybe_set_rejected(struct ref_transaction *transaction, if (err == REF_TRANSACTION_ERROR_GENERIC) return 0; + /* + * Remove this refname from the list of refnames used for validation + */ + string_list_remove(&transaction->refnames, + transaction->updates[update_idx]->refname, 0); + transaction->updates[update_idx]->rejection_err = err; ALLOC_GROW(transaction->rejections->update_indices, transaction->rejections->nr + 1, diff --git a/refs/files-backend.c b/refs/files-backend.c index 85f2e14e93..ceeec272ff 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -852,6 +852,7 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs, goto error_return; } else if (remove_dir_recursively(&ref_file, REMOVE_DIR_EMPTY_ONLY)) { + ret = REF_TRANSACTION_ERROR_NAME_CONFLICT; if (refs_verify_refname_available( &refs->base, refname, extras, NULL, 0, err)) { @@ -859,7 +860,6 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs, * The error message set by * verify_refname_available() is OK. */ - ret = REF_TRANSACTION_ERROR_NAME_CONFLICT; goto error_return; } else { /* diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 08dbea6503..ffaa956cde 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -59,6 +59,12 @@ test_expect_success "clone and setup child repos" ' cd case_sensitive_fd && git branch foo/bar && git branch Foo + ) && + git clone --ref-format=reftable . case_sensitive_df && + ( + cd case_sensitive_df && + git branch Foo/bar && + git branch foo ) ' @@ -1592,6 +1598,20 @@ test_expect_success CASE_INSENSITIVE_FS,REFFILES 'F/D conflict on case insensiti ) ' +test_expect_success CASE_INSENSITIVE_FS,REFFILES 'D/F conflict on case insensitive filesystem' ' + test_when_finished rm -rf case_insensitive && + ( + git init --bare case_insensitive && + cd case_insensitive && + git remote add origin -- ../case_sensitive_df && + test_must_fail git fetch -f origin "refs/heads/*:refs/heads/*" 2>err && + test_grep "failed: refname conflict" err && + git rev-parse refs/heads/main >expect && + git rev-parse refs/heads/Foo/bar >actual && + test_cmp expect actual + ) +' + . "$TEST_DIRECTORY"/lib-httpd.sh start_httpd
-- 2.50.1