|
68 | 68 | */
|
69 | 69 | #define REF_DELETED_RMDIR (1 << 9)
|
70 | 70 |
|
| 71 | +/* |
| 72 | + * Used to indicate that the reflog-only update has been created via |
| 73 | + * `split_head_update()`. |
| 74 | + */ |
| 75 | +#define REF_LOG_VIA_SPLIT (1 << 14) |
| 76 | + |
71 | 77 | struct ref_lock {
|
72 | 78 | char *ref_name;
|
73 | 79 | struct lock_file lk;
|
@@ -637,14 +643,16 @@ int parse_loose_ref_contents(const struct git_hash_algo *algop,
|
637 | 643 | return 0;
|
638 | 644 | }
|
639 | 645 |
|
640 |
| -static void unlock_ref(struct ref_lock *lock) |
| 646 | +static int unlock_ref(struct ref_lock *lock) |
641 | 647 | {
|
642 | 648 | lock->count--;
|
643 | 649 | if (!lock->count) {
|
644 | 650 | rollback_lock_file(&lock->lk);
|
645 | 651 | free(lock->ref_name);
|
646 | 652 | free(lock);
|
| 653 | + return 1; |
647 | 654 | }
|
| 655 | + return 0; |
648 | 656 | }
|
649 | 657 |
|
650 | 658 | /*
|
@@ -2420,9 +2428,10 @@ static enum ref_transaction_error split_head_update(struct ref_update *update,
|
2420 | 2428 |
|
2421 | 2429 | new_update = ref_transaction_add_update(
|
2422 | 2430 | transaction, "HEAD",
|
2423 |
| - update->flags | REF_LOG_ONLY | REF_NO_DEREF, |
| 2431 | + update->flags | REF_LOG_ONLY | REF_NO_DEREF | REF_LOG_VIA_SPLIT, |
2424 | 2432 | &update->new_oid, &update->old_oid,
|
2425 | 2433 | NULL, NULL, update->committer_info, update->msg);
|
| 2434 | + new_update->parent_update = update; |
2426 | 2435 |
|
2427 | 2436 | /*
|
2428 | 2437 | * Add "HEAD". This insertion is O(N) in the transaction
|
@@ -2550,6 +2559,9 @@ struct files_transaction_backend_data {
|
2550 | 2559 | * the referent to transaction.
|
2551 | 2560 | * - If it is an update of head_ref, add a corresponding REF_LOG_ONLY
|
2552 | 2561 | * update of HEAD.
|
| 2562 | + * |
| 2563 | + * Returns 0 on success, 1 in case the update needs to be dropped or a negative |
| 2564 | + * error code otherwise. |
2553 | 2565 | */
|
2554 | 2566 | static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *refs,
|
2555 | 2567 | struct ref_update *update,
|
@@ -2600,7 +2612,45 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re
|
2600 | 2612 |
|
2601 | 2613 | update->backend_data = lock;
|
2602 | 2614 |
|
2603 |
| - if (update->type & REF_ISSYMREF) { |
| 2615 | + if (update->flags & REF_LOG_VIA_SPLIT) { |
| 2616 | + struct ref_lock *parent_lock; |
| 2617 | + |
| 2618 | + if (!update->parent_update) |
| 2619 | + BUG("split update without a parent"); |
| 2620 | + |
| 2621 | + parent_lock = update->parent_update->backend_data; |
| 2622 | + |
| 2623 | + /* |
| 2624 | + * Check that "HEAD" didn't racily change since we have looked |
| 2625 | + * it up. If it did we remove the reflog-only updateg from the |
| 2626 | + * transaction again. |
| 2627 | + * |
| 2628 | + * Note that this does not catch all races: if "HEAD" was |
| 2629 | + * racily changed to point to one of the refs part of the |
| 2630 | + * transaction then we would miss writing the split reflog |
| 2631 | + * entry for "HEAD". |
| 2632 | + */ |
| 2633 | + if (!(update->type & REF_ISSYMREF) || |
| 2634 | + strcmp(update->parent_update->refname, referent.buf)) { |
| 2635 | + if (unlock_ref(lock)) |
| 2636 | + strmap_remove(&backend_data->ref_locks, |
| 2637 | + update->refname, 0); |
| 2638 | + |
| 2639 | + memmove(transaction->updates + update_idx, |
| 2640 | + transaction->updates + update_idx + 1, |
| 2641 | + (transaction->nr - update_idx - 1) * sizeof(*transaction->updates)); |
| 2642 | + transaction->nr--; |
| 2643 | + |
| 2644 | + ret = 1; |
| 2645 | + goto out; |
| 2646 | + } |
| 2647 | + |
| 2648 | + if (update->flags & REF_HAVE_OLD) { |
| 2649 | + oidcpy(&lock->old_oid, &update->old_oid); |
| 2650 | + } else { |
| 2651 | + oidcpy(&lock->old_oid, &parent_lock->old_oid); |
| 2652 | + } |
| 2653 | + } else if (update->type & REF_ISSYMREF) { |
2604 | 2654 | if (update->flags & REF_NO_DEREF) {
|
2605 | 2655 | /*
|
2606 | 2656 | * We won't be reading the referent as part of
|
@@ -2860,6 +2910,8 @@ static int files_transaction_prepare(struct ref_store *ref_store,
|
2860 | 2910 | head_ref, &refnames_to_check,
|
2861 | 2911 | err);
|
2862 | 2912 | if (ret) {
|
| 2913 | + if (ret > 0) |
| 2914 | + continue; |
2863 | 2915 | if (ref_transaction_maybe_set_rejected(transaction, i, ret)) {
|
2864 | 2916 | strbuf_reset(err);
|
2865 | 2917 | ret = 0;
|
|
0 commit comments