mirror of
https://github.com/git/git.git
synced 2025-12-12 20:36:24 +01:00
Merge branch 'kn/refs-files-case-insensitive' into maint-2.51
Deal more gracefully with directory / file conflicts when the files backend is used for ref storage, by failing only the ones that are involved in the conflict while allowing others. * kn/refs-files-case-insensitive: refs/files: handle D/F conflicts during locking refs/files: handle F/D conflicts in case-insensitive FS refs/files: use correct error type when lock exists refs/files: catch conflicts on case-insensitive file-systems
This commit is contained in:
@@ -653,6 +653,26 @@ static void unlock_ref(struct ref_lock *lock)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the transaction has another update with a case-insensitive refname
|
||||
* match.
|
||||
*
|
||||
* If the update is part of the transaction, we only check up to that index.
|
||||
* Further updates are expected to call this function to match previous indices.
|
||||
*/
|
||||
static bool transaction_has_case_conflicting_update(struct ref_transaction *transaction,
|
||||
struct ref_update *update)
|
||||
{
|
||||
for (size_t i = 0; i < transaction->nr; i++) {
|
||||
if (transaction->updates[i] == update)
|
||||
break;
|
||||
|
||||
if (!strcasecmp(transaction->updates[i]->refname, update->refname))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock refname, without following symrefs, and set *lock_p to point
|
||||
* at a newly-allocated lock object. Fill in lock->old_oid, referent,
|
||||
@@ -683,16 +703,17 @@ static void unlock_ref(struct ref_lock *lock)
|
||||
* - Generate informative error messages in the case of failure
|
||||
*/
|
||||
static enum ref_transaction_error lock_raw_ref(struct files_ref_store *refs,
|
||||
struct ref_update *update,
|
||||
struct ref_transaction *transaction,
|
||||
size_t update_idx,
|
||||
int mustexist,
|
||||
struct string_list *refnames_to_check,
|
||||
const struct string_list *extras,
|
||||
struct ref_lock **lock_p,
|
||||
struct strbuf *referent,
|
||||
struct strbuf *err)
|
||||
{
|
||||
enum ref_transaction_error ret = REF_TRANSACTION_ERROR_GENERIC;
|
||||
struct ref_update *update = transaction->updates[update_idx];
|
||||
const struct string_list *extras = &transaction->refnames;
|
||||
const char *refname = update->refname;
|
||||
unsigned int *type = &update->type;
|
||||
struct ref_lock *lock;
|
||||
@@ -782,6 +803,24 @@ retry:
|
||||
goto retry;
|
||||
} else {
|
||||
unable_to_lock_message(ref_file.buf, myerr, err);
|
||||
if (myerr == EEXIST) {
|
||||
if (ignore_case &&
|
||||
transaction_has_case_conflicting_update(transaction, update)) {
|
||||
/*
|
||||
* In case-insensitive filesystems, ensure that conflicts within a
|
||||
* given transaction are handled. Pre-existing refs on a
|
||||
* case-insensitive system will be overridden without any issue.
|
||||
*/
|
||||
ret = REF_TRANSACTION_ERROR_CASE_CONFLICT;
|
||||
} else {
|
||||
/*
|
||||
* Pre-existing case-conflicting reference locks should also be
|
||||
* specially categorized to avoid failing all batched updates.
|
||||
*/
|
||||
ret = REF_TRANSACTION_ERROR_CREATE_EXISTS;
|
||||
}
|
||||
}
|
||||
|
||||
goto error_return;
|
||||
}
|
||||
}
|
||||
@@ -837,6 +876,7 @@ retry:
|
||||
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)) {
|
||||
@@ -844,14 +884,14 @@ retry:
|
||||
* The error message set by
|
||||
* verify_refname_available() is OK.
|
||||
*/
|
||||
ret = REF_TRANSACTION_ERROR_NAME_CONFLICT;
|
||||
goto error_return;
|
||||
} else {
|
||||
/*
|
||||
* We can't delete the directory,
|
||||
* but we also don't know of any
|
||||
* references that it should
|
||||
* contain.
|
||||
* Directory conflicts can occur if there
|
||||
* is an existing lock file in the directory
|
||||
* or if the filesystem is case-insensitive
|
||||
* and the directory contains a valid reference
|
||||
* but conflicts with the update.
|
||||
*/
|
||||
strbuf_addf(err, "there is a non-empty directory '%s' "
|
||||
"blocking reference '%s'",
|
||||
@@ -873,8 +913,23 @@ retry:
|
||||
* If the ref did not exist and we are creating it, we have to
|
||||
* make sure there is no existing packed ref that conflicts
|
||||
* with refname. This check is deferred so that we can batch it.
|
||||
*
|
||||
* For case-insensitive filesystems, we should also check for F/D
|
||||
* conflicts between 'foo' and 'Foo/bar'. So let's lowercase
|
||||
* the refname.
|
||||
*/
|
||||
item = string_list_append(refnames_to_check, refname);
|
||||
if (ignore_case) {
|
||||
struct strbuf lower = STRBUF_INIT;
|
||||
|
||||
strbuf_addstr(&lower, refname);
|
||||
strbuf_tolower(&lower);
|
||||
|
||||
item = string_list_append_nodup(refnames_to_check,
|
||||
strbuf_detach(&lower, NULL));
|
||||
} else {
|
||||
item = string_list_append(refnames_to_check, refname);
|
||||
}
|
||||
|
||||
item->util = xmalloc(sizeof(update_idx));
|
||||
memcpy(item->util, &update_idx, sizeof(update_idx));
|
||||
}
|
||||
@@ -2590,9 +2645,8 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re
|
||||
if (lock) {
|
||||
lock->count++;
|
||||
} else {
|
||||
ret = lock_raw_ref(refs, update, update_idx, mustexist,
|
||||
refnames_to_check, &transaction->refnames,
|
||||
&lock, &referent, err);
|
||||
ret = lock_raw_ref(refs, transaction, update_idx, mustexist,
|
||||
refnames_to_check, &lock, &referent, err);
|
||||
if (ret) {
|
||||
char *reason;
|
||||
|
||||
@@ -2830,7 +2884,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
|
||||
"ref_transaction_prepare");
|
||||
size_t i;
|
||||
int ret = 0;
|
||||
struct string_list refnames_to_check = STRING_LIST_INIT_NODUP;
|
||||
struct string_list refnames_to_check = STRING_LIST_INIT_DUP;
|
||||
char *head_ref = NULL;
|
||||
int head_type;
|
||||
struct files_transaction_backend_data *backend_data;
|
||||
|
||||
Reference in New Issue
Block a user