Skip to content

Commit 07f0542

Browse files
committed
Merge branch 'mh/packed-ref-transactions'
Implement transactional update to the packed-ref representation of references. * mh/packed-ref-transactions: files_transaction_finish(): delete reflogs before references packed-backend: rip out some now-unused code files_ref_store: use a transaction to update packed refs t1404: demonstrate two problems with reference transactions files_initial_transaction_commit(): use a transaction for packed refs prune_refs(): also free the linked list files_pack_refs(): use a reference transaction to write packed refs packed_delete_refs(): implement method packed_ref_store: implement reference transactions struct ref_transaction: add a place for backends to store data packed-backend: don't adjust the reference count on lock/unlock
2 parents 6701263 + 5e00a6c commit 07f0542

File tree

5 files changed

+551
-216
lines changed

5 files changed

+551
-216
lines changed

refs/files-backend.c

Lines changed: 158 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,11 +1041,17 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)
10411041
strbuf_release(&err);
10421042
}
10431043

1044-
static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)
1044+
/*
1045+
* Prune the loose versions of the references in the linked list
1046+
* `*refs_to_prune`, freeing the entries in the list as we go.
1047+
*/
1048+
static void prune_refs(struct files_ref_store *refs, struct ref_to_prune **refs_to_prune)
10451049
{
1046-
while (r) {
1050+
while (*refs_to_prune) {
1051+
struct ref_to_prune *r = *refs_to_prune;
1052+
*refs_to_prune = r->next;
10471053
prune_ref(refs, r);
1048-
r = r->next;
1054+
free(r);
10491055
}
10501056
}
10511057

@@ -1084,6 +1090,11 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
10841090
int ok;
10851091
struct ref_to_prune *refs_to_prune = NULL;
10861092
struct strbuf err = STRBUF_INIT;
1093+
struct ref_transaction *transaction;
1094+
1095+
transaction = ref_store_transaction_begin(refs->packed_ref_store, &err);
1096+
if (!transaction)
1097+
return -1;
10871098

10881099
packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err);
10891100

@@ -1099,12 +1110,14 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
10991110
continue;
11001111

11011112
/*
1102-
* Create an entry in the packed-refs cache equivalent
1103-
* to the one from the loose ref cache, except that
1104-
* we don't copy the peeled status, because we want it
1105-
* to be re-peeled.
1113+
* Add a reference creation for this reference to the
1114+
* packed-refs transaction:
11061115
*/
1107-
add_packed_ref(refs->packed_ref_store, iter->refname, iter->oid);
1116+
if (ref_transaction_update(transaction, iter->refname,
1117+
iter->oid->hash, NULL,
1118+
REF_NODEREF, NULL, &err))
1119+
die("failure preparing to create packed reference %s: %s",
1120+
iter->refname, err.buf);
11081121

11091122
/* Schedule the loose reference for pruning if requested. */
11101123
if ((flags & PACK_REFS_PRUNE)) {
@@ -1118,11 +1131,14 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
11181131
if (ok != ITER_DONE)
11191132
die("error while iterating over references");
11201133

1121-
if (commit_packed_refs(refs->packed_ref_store, &err))
1122-
die("unable to overwrite old ref-pack file: %s", err.buf);
1134+
if (ref_transaction_commit(transaction, &err))
1135+
die("unable to write new packed-refs: %s", err.buf);
1136+
1137+
ref_transaction_free(transaction);
1138+
11231139
packed_refs_unlock(refs->packed_ref_store);
11241140

1125-
prune_refs(refs, refs_to_prune);
1141+
prune_refs(refs, &refs_to_prune);
11261142
strbuf_release(&err);
11271143
return 0;
11281144
}
@@ -1141,7 +1157,7 @@ static int files_delete_refs(struct ref_store *ref_store, const char *msg,
11411157
if (packed_refs_lock(refs->packed_ref_store, 0, &err))
11421158
goto error;
11431159

1144-
if (repack_without_refs(refs->packed_ref_store, refnames, &err)) {
1160+
if (refs_delete_refs(refs->packed_ref_store, msg, refnames, flags)) {
11451161
packed_refs_unlock(refs->packed_ref_store);
11461162
goto error;
11471163
}
@@ -2431,13 +2447,22 @@ static int lock_ref_for_update(struct files_ref_store *refs,
24312447
return ret;
24322448
}
24332449

2450+
struct files_transaction_backend_data {
2451+
struct ref_transaction *packed_transaction;
2452+
int packed_refs_locked;
2453+
};
2454+
24342455
/*
24352456
* Unlock any references in `transaction` that are still locked, and
24362457
* mark the transaction closed.
24372458
*/
2438-
static void files_transaction_cleanup(struct ref_transaction *transaction)
2459+
static void files_transaction_cleanup(struct files_ref_store *refs,
2460+
struct ref_transaction *transaction)
24392461
{
24402462
size_t i;
2463+
struct files_transaction_backend_data *backend_data =
2464+
transaction->backend_data;
2465+
struct strbuf err = STRBUF_INIT;
24412466

24422467
for (i = 0; i < transaction->nr; i++) {
24432468
struct ref_update *update = transaction->updates[i];
@@ -2449,6 +2474,17 @@ static void files_transaction_cleanup(struct ref_transaction *transaction)
24492474
}
24502475
}
24512476

2477+
if (backend_data->packed_transaction &&
2478+
ref_transaction_abort(backend_data->packed_transaction, &err)) {
2479+
error("error aborting transaction: %s", err.buf);
2480+
strbuf_release(&err);
2481+
}
2482+
2483+
if (backend_data->packed_refs_locked)
2484+
packed_refs_unlock(refs->packed_ref_store);
2485+
2486+
free(backend_data);
2487+
24522488
transaction->state = REF_TRANSACTION_CLOSED;
24532489
}
24542490

@@ -2465,12 +2501,17 @@ static int files_transaction_prepare(struct ref_store *ref_store,
24652501
char *head_ref = NULL;
24662502
int head_type;
24672503
struct object_id head_oid;
2504+
struct files_transaction_backend_data *backend_data;
2505+
struct ref_transaction *packed_transaction = NULL;
24682506

24692507
assert(err);
24702508

24712509
if (!transaction->nr)
24722510
goto cleanup;
24732511

2512+
backend_data = xcalloc(1, sizeof(*backend_data));
2513+
transaction->backend_data = backend_data;
2514+
24742515
/*
24752516
* Fail if a refname appears more than once in the
24762517
* transaction. (If we end up splitting up any updates using
@@ -2537,14 +2578,49 @@ static int files_transaction_prepare(struct ref_store *ref_store,
25372578
head_ref, &affected_refnames, err);
25382579
if (ret)
25392580
break;
2581+
2582+
if (update->flags & REF_DELETING &&
2583+
!(update->flags & REF_LOG_ONLY) &&
2584+
!(update->flags & REF_ISPRUNING)) {
2585+
/*
2586+
* This reference has to be deleted from
2587+
* packed-refs if it exists there.
2588+
*/
2589+
if (!packed_transaction) {
2590+
packed_transaction = ref_store_transaction_begin(
2591+
refs->packed_ref_store, err);
2592+
if (!packed_transaction) {
2593+
ret = TRANSACTION_GENERIC_ERROR;
2594+
goto cleanup;
2595+
}
2596+
2597+
backend_data->packed_transaction =
2598+
packed_transaction;
2599+
}
2600+
2601+
ref_transaction_add_update(
2602+
packed_transaction, update->refname,
2603+
update->flags & ~REF_HAVE_OLD,
2604+
update->new_oid.hash, update->old_oid.hash,
2605+
NULL);
2606+
}
2607+
}
2608+
2609+
if (packed_transaction) {
2610+
if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
2611+
ret = TRANSACTION_GENERIC_ERROR;
2612+
goto cleanup;
2613+
}
2614+
backend_data->packed_refs_locked = 1;
2615+
ret = ref_transaction_prepare(packed_transaction, err);
25402616
}
25412617

25422618
cleanup:
25432619
free(head_ref);
25442620
string_list_clear(&affected_refnames, 0);
25452621

25462622
if (ret)
2547-
files_transaction_cleanup(transaction);
2623+
files_transaction_cleanup(refs, transaction);
25482624
else
25492625
transaction->state = REF_TRANSACTION_PREPARED;
25502626

@@ -2559,9 +2635,10 @@ static int files_transaction_finish(struct ref_store *ref_store,
25592635
files_downcast(ref_store, 0, "ref_transaction_finish");
25602636
size_t i;
25612637
int ret = 0;
2562-
struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
2563-
struct string_list_item *ref_to_delete;
25642638
struct strbuf sb = STRBUF_INIT;
2639+
struct files_transaction_backend_data *backend_data;
2640+
struct ref_transaction *packed_transaction;
2641+
25652642

25662643
assert(err);
25672644

@@ -2570,6 +2647,9 @@ static int files_transaction_finish(struct ref_store *ref_store,
25702647
return 0;
25712648
}
25722649

2650+
backend_data = transaction->backend_data;
2651+
packed_transaction = backend_data->packed_transaction;
2652+
25732653
/* Perform updates first so live commits remain referenced */
25742654
for (i = 0; i < transaction->nr; i++) {
25752655
struct ref_update *update = transaction->updates[i];
@@ -2605,7 +2685,44 @@ static int files_transaction_finish(struct ref_store *ref_store,
26052685
}
26062686
}
26072687
}
2608-
/* Perform deletes now that updates are safely completed */
2688+
2689+
/*
2690+
* Now that updates are safely completed, we can perform
2691+
* deletes. First delete the reflogs of any references that
2692+
* will be deleted, since (in the unexpected event of an
2693+
* error) leaving a reference without a reflog is less bad
2694+
* than leaving a reflog without a reference (the latter is a
2695+
* mildly invalid repository state):
2696+
*/
2697+
for (i = 0; i < transaction->nr; i++) {
2698+
struct ref_update *update = transaction->updates[i];
2699+
if (update->flags & REF_DELETING &&
2700+
!(update->flags & REF_LOG_ONLY) &&
2701+
!(update->flags & REF_ISPRUNING)) {
2702+
strbuf_reset(&sb);
2703+
files_reflog_path(refs, &sb, update->refname);
2704+
if (!unlink_or_warn(sb.buf))
2705+
try_remove_empty_parents(refs, update->refname,
2706+
REMOVE_EMPTY_PARENTS_REFLOG);
2707+
}
2708+
}
2709+
2710+
/*
2711+
* Perform deletes now that updates are safely completed.
2712+
*
2713+
* First delete any packed versions of the references, while
2714+
* retaining the packed-refs lock:
2715+
*/
2716+
if (packed_transaction) {
2717+
ret = ref_transaction_commit(packed_transaction, err);
2718+
ref_transaction_free(packed_transaction);
2719+
packed_transaction = NULL;
2720+
backend_data->packed_transaction = NULL;
2721+
if (ret)
2722+
goto cleanup;
2723+
}
2724+
2725+
/* Now delete the loose versions of the references: */
26092726
for (i = 0; i < transaction->nr; i++) {
26102727
struct ref_update *update = transaction->updates[i];
26112728
struct ref_lock *lock = update->backend_data;
@@ -2623,39 +2740,13 @@ static int files_transaction_finish(struct ref_store *ref_store,
26232740
}
26242741
update->flags |= REF_DELETED_LOOSE;
26252742
}
2626-
2627-
if (!(update->flags & REF_ISPRUNING))
2628-
string_list_append(&refs_to_delete,
2629-
lock->ref_name);
26302743
}
26312744
}
26322745

2633-
if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
2634-
ret = TRANSACTION_GENERIC_ERROR;
2635-
goto cleanup;
2636-
}
2637-
2638-
if (repack_without_refs(refs->packed_ref_store, &refs_to_delete, err)) {
2639-
ret = TRANSACTION_GENERIC_ERROR;
2640-
packed_refs_unlock(refs->packed_ref_store);
2641-
goto cleanup;
2642-
}
2643-
2644-
packed_refs_unlock(refs->packed_ref_store);
2645-
2646-
/* Delete the reflogs of any references that were deleted: */
2647-
for_each_string_list_item(ref_to_delete, &refs_to_delete) {
2648-
strbuf_reset(&sb);
2649-
files_reflog_path(refs, &sb, ref_to_delete->string);
2650-
if (!unlink_or_warn(sb.buf))
2651-
try_remove_empty_parents(refs, ref_to_delete->string,
2652-
REMOVE_EMPTY_PARENTS_REFLOG);
2653-
}
2654-
26552746
clear_loose_ref_cache(refs);
26562747

26572748
cleanup:
2658-
files_transaction_cleanup(transaction);
2749+
files_transaction_cleanup(refs, transaction);
26592750

26602751
for (i = 0; i < transaction->nr; i++) {
26612752
struct ref_update *update = transaction->updates[i];
@@ -2673,15 +2764,17 @@ static int files_transaction_finish(struct ref_store *ref_store,
26732764
}
26742765

26752766
strbuf_release(&sb);
2676-
string_list_clear(&refs_to_delete, 0);
26772767
return ret;
26782768
}
26792769

26802770
static int files_transaction_abort(struct ref_store *ref_store,
26812771
struct ref_transaction *transaction,
26822772
struct strbuf *err)
26832773
{
2684-
files_transaction_cleanup(transaction);
2774+
struct files_ref_store *refs =
2775+
files_downcast(ref_store, 0, "ref_transaction_abort");
2776+
2777+
files_transaction_cleanup(refs, transaction);
26852778
return 0;
26862779
}
26872780

@@ -2703,6 +2796,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
27032796
size_t i;
27042797
int ret = 0;
27052798
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
2799+
struct ref_transaction *packed_transaction = NULL;
27062800

27072801
assert(err);
27082802

@@ -2735,6 +2829,12 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
27352829
&affected_refnames))
27362830
die("BUG: initial ref transaction called with existing refs");
27372831

2832+
packed_transaction = ref_store_transaction_begin(refs->packed_ref_store, err);
2833+
if (!packed_transaction) {
2834+
ret = TRANSACTION_GENERIC_ERROR;
2835+
goto cleanup;
2836+
}
2837+
27382838
for (i = 0; i < transaction->nr; i++) {
27392839
struct ref_update *update = transaction->updates[i];
27402840

@@ -2747,28 +2847,30 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
27472847
ret = TRANSACTION_NAME_CONFLICT;
27482848
goto cleanup;
27492849
}
2850+
2851+
/*
2852+
* Add a reference creation for this reference to the
2853+
* packed-refs transaction:
2854+
*/
2855+
ref_transaction_add_update(packed_transaction, update->refname,
2856+
update->flags & ~REF_HAVE_OLD,
2857+
update->new_oid.hash, update->old_oid.hash,
2858+
NULL);
27502859
}
27512860

27522861
if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
27532862
ret = TRANSACTION_GENERIC_ERROR;
27542863
goto cleanup;
27552864
}
27562865

2757-
for (i = 0; i < transaction->nr; i++) {
2758-
struct ref_update *update = transaction->updates[i];
2759-
2760-
if ((update->flags & REF_HAVE_NEW) &&
2761-
!is_null_oid(&update->new_oid))
2762-
add_packed_ref(refs->packed_ref_store, update->refname,
2763-
&update->new_oid);
2764-
}
2765-
2766-
if (commit_packed_refs(refs->packed_ref_store, err)) {
2866+
if (initial_ref_transaction_commit(packed_transaction, err)) {
27672867
ret = TRANSACTION_GENERIC_ERROR;
27682868
goto cleanup;
27692869
}
27702870

27712871
cleanup:
2872+
if (packed_transaction)
2873+
ref_transaction_free(packed_transaction);
27722874
packed_refs_unlock(refs->packed_ref_store);
27732875
transaction->state = REF_TRANSACTION_CLOSED;
27742876
string_list_clear(&affected_refnames, 0);

0 commit comments

Comments
 (0)