summaryrefslogtreecommitdiff
path: root/refs/files-backend.c
diff options
context:
space:
mode:
authorKarthik Nayak <karthik.188@gmail.com>2025-04-08 10:51:06 +0200
committerJunio C Hamano <gitster@pobox.com>2025-04-08 07:57:18 -0700
commitc3baddf04f8fb20bec590f492f00189fd6c02a35 (patch)
treed9a70f001a38145b024a87955717e96ac4830136 /refs/files-backend.c
parent05a1834e429c619602a8507d8a2c9b81d467c24d (diff)
refs: move duplicate refname update check to generic layer
Move the tracking of refnames in `affected_refnames` from individual backends into the generic layer in 'refs.c'. This centralizes the duplicate refname detection that was previously handled separately by each backend. Make some changes to accommodate this move: - Add a `string_list` field `refnames` to `ref_transaction` to contain all the references in a transaction. This field is updated whenever a new update is added via `ref_transaction_add_update`, so manual additions in reference backends are dropped. - Modify the backends to use this field internally as needed. The backends need to check if an update for refname already exists when splitting symrefs or adding an update for 'HEAD'. - In the reftable backend, within `reftable_be_transaction_prepare()`, move the `string_list_has_string()` check above `ref_transaction_add_update()`. Since `ref_transaction_add_update()` automatically adds the refname to `transaction->refnames`, performing the check after will always return true, so we perform the check before adding the update. This helps reduce duplication of functionality between the backends and makes it easier to make changes in a more centralized manner. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Acked-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'refs/files-backend.c')
-rw-r--r--refs/files-backend.c67
1 files changed, 14 insertions, 53 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c
index dab3951ccf..ecf2df556d 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -2378,9 +2378,7 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st
*/
static int split_head_update(struct ref_update *update,
struct ref_transaction *transaction,
- const char *head_ref,
- struct string_list *affected_refnames,
- struct strbuf *err)
+ const char *head_ref, struct strbuf *err)
{
struct ref_update *new_update;
@@ -2398,7 +2396,7 @@ static int split_head_update(struct ref_update *update,
* transaction. This check is O(lg N) in the transaction
* size, but it happens at most once per transaction.
*/
- if (string_list_has_string(affected_refnames, "HEAD")) {
+ if (string_list_has_string(&transaction->refnames, "HEAD")) {
/* An entry already existed */
strbuf_addf(err,
"multiple updates for 'HEAD' (including one "
@@ -2420,7 +2418,6 @@ static int split_head_update(struct ref_update *update,
*/
if (strcmp(new_update->refname, "HEAD"))
BUG("%s unexpectedly not 'HEAD'", new_update->refname);
- string_list_insert(affected_refnames, new_update->refname);
return 0;
}
@@ -2436,7 +2433,6 @@ static int split_head_update(struct ref_update *update,
static int split_symref_update(struct ref_update *update,
const char *referent,
struct ref_transaction *transaction,
- struct string_list *affected_refnames,
struct strbuf *err)
{
struct ref_update *new_update;
@@ -2448,7 +2444,7 @@ static int split_symref_update(struct ref_update *update,
* size, but it happens at most once per symref in a
* transaction.
*/
- if (string_list_has_string(affected_refnames, referent)) {
+ if (string_list_has_string(&transaction->refnames, referent)) {
/* An entry already exists */
strbuf_addf(err,
"multiple updates for '%s' (including one "
@@ -2486,15 +2482,6 @@ static int split_symref_update(struct ref_update *update,
update->flags |= REF_LOG_ONLY | REF_NO_DEREF;
update->flags &= ~REF_HAVE_OLD;
- /*
- * Add the referent. This insertion is O(N) in the transaction
- * size, but it happens at most once per symref in a
- * transaction. Make sure to add new_update->refname, which will
- * be valid as long as affected_refnames is in use, and NOT
- * referent, which might soon be freed by our caller.
- */
- string_list_insert(affected_refnames, new_update->refname);
-
return 0;
}
@@ -2558,7 +2545,6 @@ static int lock_ref_for_update(struct files_ref_store *refs,
struct ref_transaction *transaction,
const char *head_ref,
struct string_list *refnames_to_check,
- struct string_list *affected_refnames,
struct strbuf *err)
{
struct strbuf referent = STRBUF_INIT;
@@ -2575,8 +2561,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
update->flags |= REF_DELETING;
if (head_ref) {
- ret = split_head_update(update, transaction, head_ref,
- affected_refnames, err);
+ ret = split_head_update(update, transaction, head_ref, err);
if (ret)
goto out;
}
@@ -2586,9 +2571,8 @@ static int lock_ref_for_update(struct files_ref_store *refs,
lock->count++;
} else {
ret = lock_raw_ref(refs, update->refname, mustexist,
- refnames_to_check, affected_refnames,
- &lock, &referent,
- &update->type, err);
+ refnames_to_check, &transaction->refnames,
+ &lock, &referent, &update->type, err);
if (ret) {
char *reason;
@@ -2642,9 +2626,8 @@ static int lock_ref_for_update(struct files_ref_store *refs,
* of processing the split-off update, so we
* don't have to do it here.
*/
- ret = split_symref_update(update,
- referent.buf, transaction,
- affected_refnames, err);
+ ret = split_symref_update(update, referent.buf,
+ transaction, err);
if (ret)
goto out;
}
@@ -2799,7 +2782,6 @@ static int files_transaction_prepare(struct ref_store *ref_store,
"ref_transaction_prepare");
size_t i;
int ret = 0;
- struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
struct string_list refnames_to_check = STRING_LIST_INIT_NODUP;
char *head_ref = NULL;
int head_type;
@@ -2818,12 +2800,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
transaction->backend_data = backend_data;
/*
- * Fail if a refname appears more than once in the
- * transaction. (If we end up splitting up any updates using
- * split_symref_update() or split_head_update(), those
- * functions will check that the new updates don't have the
- * same refname as any existing ones.) Also fail if any of the
- * updates use REF_IS_PRUNING without REF_NO_DEREF.
+ * Fail if any of the updates use REF_IS_PRUNING without REF_NO_DEREF.
*/
for (i = 0; i < transaction->nr; i++) {
struct ref_update *update = transaction->updates[i];
@@ -2831,16 +2808,6 @@ static int files_transaction_prepare(struct ref_store *ref_store,
if ((update->flags & REF_IS_PRUNING) &&
!(update->flags & REF_NO_DEREF))
BUG("REF_IS_PRUNING set without REF_NO_DEREF");
-
- if (update->flags & REF_LOG_ONLY)
- continue;
-
- string_list_append(&affected_refnames, update->refname);
- }
- string_list_sort(&affected_refnames);
- if (ref_update_reject_duplicates(&affected_refnames, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
- goto cleanup;
}
/*
@@ -2882,7 +2849,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
ret = lock_ref_for_update(refs, update, transaction,
head_ref, &refnames_to_check,
- &affected_refnames, err);
+ err);
if (ret)
goto cleanup;
@@ -2929,7 +2896,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
* So instead, we accept the race for now.
*/
if (refs_verify_refnames_available(refs->packed_ref_store, &refnames_to_check,
- &affected_refnames, NULL, 0, err)) {
+ &transaction->refnames, NULL, 0, err)) {
ret = TRANSACTION_NAME_CONFLICT;
goto cleanup;
}
@@ -2975,7 +2942,6 @@ static int files_transaction_prepare(struct ref_store *ref_store,
cleanup:
free(head_ref);
- string_list_clear(&affected_refnames, 0);
string_list_clear(&refnames_to_check, 0);
if (ret)
@@ -3050,13 +3016,8 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
if (transaction->state != REF_TRANSACTION_PREPARED)
BUG("commit called for transaction that is not prepared");
- /* Fail if a refname appears more than once in the transaction: */
- for (i = 0; i < transaction->nr; i++)
- if (!(transaction->updates[i]->flags & REF_LOG_ONLY))
- string_list_append(&affected_refnames,
- transaction->updates[i]->refname);
- string_list_sort(&affected_refnames);
- if (ref_update_reject_duplicates(&affected_refnames, err)) {
+ string_list_sort(&transaction->refnames);
+ if (ref_update_reject_duplicates(&transaction->refnames, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
@@ -3074,7 +3035,7 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
* that we are creating already exists.
*/
if (refs_for_each_rawref(&refs->base, ref_present,
- &affected_refnames))
+ &transaction->refnames))
BUG("initial ref transaction called with existing refs");
packed_transaction = ref_store_transaction_begin(refs->packed_ref_store,