Skip to content

Commit c3baddf

Browse files
KarthikNayakgitster
authored andcommitted
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 <[email protected]> Acked-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 05a1834 commit c3baddf

File tree

5 files changed

+51
-114
lines changed

5 files changed

+51
-114
lines changed

refs.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,6 +1175,7 @@ struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
11751175
CALLOC_ARRAY(tr, 1);
11761176
tr->ref_store = refs;
11771177
tr->flags = flags;
1178+
string_list_init_dup(&tr->refnames);
11781179
return tr;
11791180
}
11801181

@@ -1205,6 +1206,7 @@ void ref_transaction_free(struct ref_transaction *transaction)
12051206
free((char *)transaction->updates[i]->old_target);
12061207
free(transaction->updates[i]);
12071208
}
1209+
string_list_clear(&transaction->refnames, 0);
12081210
free(transaction->updates);
12091211
free(transaction);
12101212
}
@@ -1218,6 +1220,7 @@ struct ref_update *ref_transaction_add_update(
12181220
const char *committer_info,
12191221
const char *msg)
12201222
{
1223+
struct string_list_item *item;
12211224
struct ref_update *update;
12221225

12231226
if (transaction->state != REF_TRANSACTION_OPEN)
@@ -1245,6 +1248,16 @@ struct ref_update *ref_transaction_add_update(
12451248
update->msg = normalize_reflog_message(msg);
12461249
}
12471250

1251+
/*
1252+
* This list is generally used by the backends to avoid duplicates.
1253+
* But we do support multiple log updates for a given refname within
1254+
* a single transaction.
1255+
*/
1256+
if (!(update->flags & REF_LOG_ONLY)) {
1257+
item = string_list_append(&transaction->refnames, refname);
1258+
item->util = update;
1259+
}
1260+
12481261
return update;
12491262
}
12501263

@@ -2405,6 +2418,10 @@ int ref_transaction_prepare(struct ref_transaction *transaction,
24052418
return -1;
24062419
}
24072420

2421+
string_list_sort(&transaction->refnames);
2422+
if (ref_update_reject_duplicates(&transaction->refnames, err))
2423+
return TRANSACTION_GENERIC_ERROR;
2424+
24082425
ret = refs->be->transaction_prepare(refs, transaction, err);
24092426
if (ret)
24102427
return ret;

refs/files-backend.c

Lines changed: 14 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2378,9 +2378,7 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st
23782378
*/
23792379
static int split_head_update(struct ref_update *update,
23802380
struct ref_transaction *transaction,
2381-
const char *head_ref,
2382-
struct string_list *affected_refnames,
2383-
struct strbuf *err)
2381+
const char *head_ref, struct strbuf *err)
23842382
{
23852383
struct ref_update *new_update;
23862384

@@ -2398,7 +2396,7 @@ static int split_head_update(struct ref_update *update,
23982396
* transaction. This check is O(lg N) in the transaction
23992397
* size, but it happens at most once per transaction.
24002398
*/
2401-
if (string_list_has_string(affected_refnames, "HEAD")) {
2399+
if (string_list_has_string(&transaction->refnames, "HEAD")) {
24022400
/* An entry already existed */
24032401
strbuf_addf(err,
24042402
"multiple updates for 'HEAD' (including one "
@@ -2420,7 +2418,6 @@ static int split_head_update(struct ref_update *update,
24202418
*/
24212419
if (strcmp(new_update->refname, "HEAD"))
24222420
BUG("%s unexpectedly not 'HEAD'", new_update->refname);
2423-
string_list_insert(affected_refnames, new_update->refname);
24242421

24252422
return 0;
24262423
}
@@ -2436,7 +2433,6 @@ static int split_head_update(struct ref_update *update,
24362433
static int split_symref_update(struct ref_update *update,
24372434
const char *referent,
24382435
struct ref_transaction *transaction,
2439-
struct string_list *affected_refnames,
24402436
struct strbuf *err)
24412437
{
24422438
struct ref_update *new_update;
@@ -2448,7 +2444,7 @@ static int split_symref_update(struct ref_update *update,
24482444
* size, but it happens at most once per symref in a
24492445
* transaction.
24502446
*/
2451-
if (string_list_has_string(affected_refnames, referent)) {
2447+
if (string_list_has_string(&transaction->refnames, referent)) {
24522448
/* An entry already exists */
24532449
strbuf_addf(err,
24542450
"multiple updates for '%s' (including one "
@@ -2486,15 +2482,6 @@ static int split_symref_update(struct ref_update *update,
24862482
update->flags |= REF_LOG_ONLY | REF_NO_DEREF;
24872483
update->flags &= ~REF_HAVE_OLD;
24882484

2489-
/*
2490-
* Add the referent. This insertion is O(N) in the transaction
2491-
* size, but it happens at most once per symref in a
2492-
* transaction. Make sure to add new_update->refname, which will
2493-
* be valid as long as affected_refnames is in use, and NOT
2494-
* referent, which might soon be freed by our caller.
2495-
*/
2496-
string_list_insert(affected_refnames, new_update->refname);
2497-
24982485
return 0;
24992486
}
25002487

@@ -2558,7 +2545,6 @@ static int lock_ref_for_update(struct files_ref_store *refs,
25582545
struct ref_transaction *transaction,
25592546
const char *head_ref,
25602547
struct string_list *refnames_to_check,
2561-
struct string_list *affected_refnames,
25622548
struct strbuf *err)
25632549
{
25642550
struct strbuf referent = STRBUF_INIT;
@@ -2575,8 +2561,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
25752561
update->flags |= REF_DELETING;
25762562

25772563
if (head_ref) {
2578-
ret = split_head_update(update, transaction, head_ref,
2579-
affected_refnames, err);
2564+
ret = split_head_update(update, transaction, head_ref, err);
25802565
if (ret)
25812566
goto out;
25822567
}
@@ -2586,9 +2571,8 @@ static int lock_ref_for_update(struct files_ref_store *refs,
25862571
lock->count++;
25872572
} else {
25882573
ret = lock_raw_ref(refs, update->refname, mustexist,
2589-
refnames_to_check, affected_refnames,
2590-
&lock, &referent,
2591-
&update->type, err);
2574+
refnames_to_check, &transaction->refnames,
2575+
&lock, &referent, &update->type, err);
25922576
if (ret) {
25932577
char *reason;
25942578

@@ -2642,9 +2626,8 @@ static int lock_ref_for_update(struct files_ref_store *refs,
26422626
* of processing the split-off update, so we
26432627
* don't have to do it here.
26442628
*/
2645-
ret = split_symref_update(update,
2646-
referent.buf, transaction,
2647-
affected_refnames, err);
2629+
ret = split_symref_update(update, referent.buf,
2630+
transaction, err);
26482631
if (ret)
26492632
goto out;
26502633
}
@@ -2799,7 +2782,6 @@ static int files_transaction_prepare(struct ref_store *ref_store,
27992782
"ref_transaction_prepare");
28002783
size_t i;
28012784
int ret = 0;
2802-
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
28032785
struct string_list refnames_to_check = STRING_LIST_INIT_NODUP;
28042786
char *head_ref = NULL;
28052787
int head_type;
@@ -2818,29 +2800,14 @@ static int files_transaction_prepare(struct ref_store *ref_store,
28182800
transaction->backend_data = backend_data;
28192801

28202802
/*
2821-
* Fail if a refname appears more than once in the
2822-
* transaction. (If we end up splitting up any updates using
2823-
* split_symref_update() or split_head_update(), those
2824-
* functions will check that the new updates don't have the
2825-
* same refname as any existing ones.) Also fail if any of the
2826-
* updates use REF_IS_PRUNING without REF_NO_DEREF.
2803+
* Fail if any of the updates use REF_IS_PRUNING without REF_NO_DEREF.
28272804
*/
28282805
for (i = 0; i < transaction->nr; i++) {
28292806
struct ref_update *update = transaction->updates[i];
28302807

28312808
if ((update->flags & REF_IS_PRUNING) &&
28322809
!(update->flags & REF_NO_DEREF))
28332810
BUG("REF_IS_PRUNING set without REF_NO_DEREF");
2834-
2835-
if (update->flags & REF_LOG_ONLY)
2836-
continue;
2837-
2838-
string_list_append(&affected_refnames, update->refname);
2839-
}
2840-
string_list_sort(&affected_refnames);
2841-
if (ref_update_reject_duplicates(&affected_refnames, err)) {
2842-
ret = TRANSACTION_GENERIC_ERROR;
2843-
goto cleanup;
28442811
}
28452812

28462813
/*
@@ -2882,7 +2849,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
28822849

28832850
ret = lock_ref_for_update(refs, update, transaction,
28842851
head_ref, &refnames_to_check,
2885-
&affected_refnames, err);
2852+
err);
28862853
if (ret)
28872854
goto cleanup;
28882855

@@ -2929,7 +2896,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
29292896
* So instead, we accept the race for now.
29302897
*/
29312898
if (refs_verify_refnames_available(refs->packed_ref_store, &refnames_to_check,
2932-
&affected_refnames, NULL, 0, err)) {
2899+
&transaction->refnames, NULL, 0, err)) {
29332900
ret = TRANSACTION_NAME_CONFLICT;
29342901
goto cleanup;
29352902
}
@@ -2975,7 +2942,6 @@ static int files_transaction_prepare(struct ref_store *ref_store,
29752942

29762943
cleanup:
29772944
free(head_ref);
2978-
string_list_clear(&affected_refnames, 0);
29792945
string_list_clear(&refnames_to_check, 0);
29802946

29812947
if (ret)
@@ -3050,13 +3016,8 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
30503016
if (transaction->state != REF_TRANSACTION_PREPARED)
30513017
BUG("commit called for transaction that is not prepared");
30523018

3053-
/* Fail if a refname appears more than once in the transaction: */
3054-
for (i = 0; i < transaction->nr; i++)
3055-
if (!(transaction->updates[i]->flags & REF_LOG_ONLY))
3056-
string_list_append(&affected_refnames,
3057-
transaction->updates[i]->refname);
3058-
string_list_sort(&affected_refnames);
3059-
if (ref_update_reject_duplicates(&affected_refnames, err)) {
3019+
string_list_sort(&transaction->refnames);
3020+
if (ref_update_reject_duplicates(&transaction->refnames, err)) {
30603021
ret = TRANSACTION_GENERIC_ERROR;
30613022
goto cleanup;
30623023
}
@@ -3074,7 +3035,7 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
30743035
* that we are creating already exists.
30753036
*/
30763037
if (refs_for_each_rawref(&refs->base, ref_present,
3077-
&affected_refnames))
3038+
&transaction->refnames))
30783039
BUG("initial ref transaction called with existing refs");
30793040

30803041
packed_transaction = ref_store_transaction_begin(refs->packed_ref_store,

refs/packed-backend.c

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,8 +1622,6 @@ int is_packed_transaction_needed(struct ref_store *ref_store,
16221622
struct packed_transaction_backend_data {
16231623
/* True iff the transaction owns the packed-refs lock. */
16241624
int own_lock;
1625-
1626-
struct string_list updates;
16271625
};
16281626

16291627
static void packed_transaction_cleanup(struct packed_ref_store *refs,
@@ -1632,8 +1630,6 @@ static void packed_transaction_cleanup(struct packed_ref_store *refs,
16321630
struct packed_transaction_backend_data *data = transaction->backend_data;
16331631

16341632
if (data) {
1635-
string_list_clear(&data->updates, 0);
1636-
16371633
if (is_tempfile_active(refs->tempfile))
16381634
delete_tempfile(&refs->tempfile);
16391635

@@ -1658,7 +1654,6 @@ static int packed_transaction_prepare(struct ref_store *ref_store,
16581654
REF_STORE_READ | REF_STORE_WRITE | REF_STORE_ODB,
16591655
"ref_transaction_prepare");
16601656
struct packed_transaction_backend_data *data;
1661-
size_t i;
16621657
int ret = TRANSACTION_GENERIC_ERROR;
16631658

16641659
/*
@@ -1671,34 +1666,16 @@ static int packed_transaction_prepare(struct ref_store *ref_store,
16711666
*/
16721667

16731668
CALLOC_ARRAY(data, 1);
1674-
string_list_init_nodup(&data->updates);
16751669

16761670
transaction->backend_data = data;
16771671

1678-
/*
1679-
* Stick the updates in a string list by refname so that we
1680-
* can sort them:
1681-
*/
1682-
for (i = 0; i < transaction->nr; i++) {
1683-
struct ref_update *update = transaction->updates[i];
1684-
struct string_list_item *item =
1685-
string_list_append(&data->updates, update->refname);
1686-
1687-
/* Store a pointer to update in item->util: */
1688-
item->util = update;
1689-
}
1690-
string_list_sort(&data->updates);
1691-
1692-
if (ref_update_reject_duplicates(&data->updates, err))
1693-
goto failure;
1694-
16951672
if (!is_lock_file_locked(&refs->lock)) {
16961673
if (packed_refs_lock(ref_store, 0, err))
16971674
goto failure;
16981675
data->own_lock = 1;
16991676
}
17001677

1701-
if (write_with_updates(refs, &data->updates, err))
1678+
if (write_with_updates(refs, &transaction->refnames, err))
17021679
goto failure;
17031680

17041681
transaction->state = REF_TRANSACTION_PREPARED;

refs/refs-internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "refs.h"
55
#include "iterator.h"
6+
#include "string-list.h"
67

78
struct fsck_options;
89
struct ref_transaction;
@@ -198,6 +199,7 @@ enum ref_transaction_state {
198199
struct ref_transaction {
199200
struct ref_store *ref_store;
200201
struct ref_update **updates;
202+
struct string_list refnames;
201203
size_t alloc;
202204
size_t nr;
203205
enum ref_transaction_state state;

0 commit comments

Comments
 (0)