Skip to content

Commit 948b2ab

Browse files
KarthikNayakgitster
authored andcommitted
refs/files: handle D/F conflicts during locking
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.lock However 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.lock As in `lock_raw_ref()` after creating the lock, we also check for D/F conflicts. This can occur in case-insensitive filesystems when trying to fetch case-conflicted references like: refs/heads/Foo/new refs/heads/foo D/F conflicts can also occur in case-sensitive filesystems, when the repository already contains a directory with a lock file 'refs/heads/foo/bar.lock' and trying to fetch 'refs/heads/foo'. This doesn't concern directories containing garbage files as those are handled on a higher level. 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 <[email protected]> Signed-off-by: Karthik Nayak <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 770f389 commit 948b2ab

File tree

3 files changed

+60
-6
lines changed

3 files changed

+60
-6
lines changed

refs.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1223,7 +1223,7 @@ int ref_transaction_maybe_set_rejected(struct ref_transaction *transaction,
12231223
return 0;
12241224

12251225
if (!transaction->rejections)
1226-
BUG("transaction not inititalized with failure support");
1226+
BUG("transaction not initialized with failure support");
12271227

12281228
/*
12291229
* Don't accept generic errors, since these errors are not user
@@ -1232,6 +1232,13 @@ int ref_transaction_maybe_set_rejected(struct ref_transaction *transaction,
12321232
if (err == REF_TRANSACTION_ERROR_GENERIC)
12331233
return 0;
12341234

1235+
/*
1236+
* Rejected refnames shouldn't be considered in the availability
1237+
* checks, so remove them from the list.
1238+
*/
1239+
string_list_remove(&transaction->refnames,
1240+
transaction->updates[update_idx]->refname, 0);
1241+
12351242
transaction->updates[update_idx]->rejection_err = err;
12361243
ALLOC_GROW(transaction->rejections->update_indices,
12371244
transaction->rejections->nr + 1,

refs/files-backend.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -870,21 +870,22 @@ static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,
870870
goto error_return;
871871
} else if (remove_dir_recursively(&ref_file,
872872
REMOVE_DIR_EMPTY_ONLY)) {
873+
ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
873874
if (refs_verify_refname_available(
874875
&refs->base, refname,
875876
extras, NULL, 0, err)) {
876877
/*
877878
* The error message set by
878879
* verify_refname_available() is OK.
879880
*/
880-
ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
881881
goto error_return;
882882
} else {
883883
/*
884-
* We can't delete the directory,
885-
* but we also don't know of any
886-
* references that it should
887-
* contain.
884+
* Directory conflicts can occur if there
885+
* is an existing lock file in the directory
886+
* or if the filesystem is case-insensitive
887+
* and the directory contains a valid reference
888+
* but conflicts with the update.
888889
*/
889890
strbuf_addf(err, "there is a non-empty directory '%s' "
890891
"blocking reference '%s'",

t/t5510-fetch.sh

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ test_expect_success "clone and setup child repos" '
5959
cd case_sensitive_fd &&
6060
git branch foo/bar &&
6161
git branch Foo
62+
) &&
63+
git clone --ref-format=reftable . case_sensitive_df &&
64+
(
65+
cd case_sensitive_df &&
66+
git branch Foo/bar &&
67+
git branch foo
6268
)
6369
'
6470

@@ -1592,6 +1598,46 @@ test_expect_success CASE_INSENSITIVE_FS,REFFILES 'F/D conflict on case insensiti
15921598
)
15931599
'
15941600

1601+
test_expect_success CASE_INSENSITIVE_FS,REFFILES 'D/F conflict on case insensitive filesystem' '
1602+
test_when_finished rm -rf case_insensitive &&
1603+
(
1604+
git init --bare case_insensitive &&
1605+
cd case_insensitive &&
1606+
git remote add origin -- ../case_sensitive_df &&
1607+
test_must_fail git fetch -f origin "refs/heads/*:refs/heads/*" 2>err &&
1608+
test_grep "failed: refname conflict" err &&
1609+
git rev-parse refs/heads/main >expect &&
1610+
git rev-parse refs/heads/Foo/bar >actual &&
1611+
test_cmp expect actual
1612+
)
1613+
'
1614+
1615+
test_expect_success REFFILES 'D/F conflict on case sensitive filesystem with lock' '
1616+
(
1617+
git init --ref-format=reftable base &&
1618+
cd base &&
1619+
echo >file update &&
1620+
git add . &&
1621+
git commit -m "updated" &&
1622+
git branch -M main &&
1623+
1624+
git update-ref refs/heads/foo @ &&
1625+
git update-ref refs/heads/branch @ &&
1626+
cd .. &&
1627+
1628+
git init --ref-format=files --bare repo &&
1629+
cd repo &&
1630+
git remote add origin ../base &&
1631+
mkdir refs/heads/foo &&
1632+
touch refs/heads/foo/random.lock &&
1633+
test_must_fail git fetch origin "refs/heads/*:refs/heads/*" 2>err &&
1634+
test_grep "some local refs could not be updated; try running" err &&
1635+
git rev-parse refs/heads/main >expect &&
1636+
git rev-parse refs/heads/branch >actual &&
1637+
test_cmp expect actual
1638+
)
1639+
'
1640+
15951641
. "$TEST_DIRECTORY"/lib-httpd.sh
15961642
start_httpd
15971643

0 commit comments

Comments
 (0)