|
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;
|
@@ -2420,9 +2426,10 @@ static enum ref_transaction_error split_head_update(struct ref_update *update,
|
2420 | 2426 |
|
2421 | 2427 | new_update = ref_transaction_add_update(
|
2422 | 2428 | transaction, "HEAD",
|
2423 |
| - update->flags | REF_LOG_ONLY | REF_NO_DEREF, |
| 2429 | + update->flags | REF_LOG_ONLY | REF_NO_DEREF | REF_LOG_VIA_SPLIT, |
2424 | 2430 | &update->new_oid, &update->old_oid,
|
2425 | 2431 | NULL, NULL, update->committer_info, update->msg);
|
| 2432 | + new_update->parent_update = update; |
2426 | 2433 |
|
2427 | 2434 | /*
|
2428 | 2435 | * Add "HEAD". This insertion is O(N) in the transaction
|
@@ -2600,7 +2607,36 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re
|
2600 | 2607 |
|
2601 | 2608 | update->backend_data = lock;
|
2602 | 2609 |
|
2603 |
| - if (update->type & REF_ISSYMREF) { |
| 2610 | + if (update->flags & REF_LOG_VIA_SPLIT) { |
| 2611 | + struct ref_lock *parent_lock; |
| 2612 | + |
| 2613 | + if (!update->parent_update) |
| 2614 | + BUG("split update without a parent"); |
| 2615 | + |
| 2616 | + parent_lock = update->parent_update->backend_data; |
| 2617 | + |
| 2618 | + /* |
| 2619 | + * Check that "HEAD" didn't racily change since we have looked |
| 2620 | + * it up. If it did we must refuse to write the reflog entry. |
| 2621 | + * |
| 2622 | + * Note that this does not catch all races: if "HEAD" was |
| 2623 | + * racily changed to point to one of the refs part of the |
| 2624 | + * transaction then we would miss writing the split reflog |
| 2625 | + * entry for "HEAD". |
| 2626 | + */ |
| 2627 | + if (!(update->type & REF_ISSYMREF) || |
| 2628 | + strcmp(update->parent_update->refname, referent.buf)) { |
| 2629 | + strbuf_addstr(err, "HEAD has been racily updated"); |
| 2630 | + ret = REF_TRANSACTION_ERROR_GENERIC; |
| 2631 | + goto out; |
| 2632 | + } |
| 2633 | + |
| 2634 | + if (update->flags & REF_HAVE_OLD) { |
| 2635 | + oidcpy(&lock->old_oid, &update->old_oid); |
| 2636 | + } else { |
| 2637 | + oidcpy(&lock->old_oid, &parent_lock->old_oid); |
| 2638 | + } |
| 2639 | + } else if (update->type & REF_ISSYMREF) { |
2604 | 2640 | if (update->flags & REF_NO_DEREF) {
|
2605 | 2641 | /*
|
2606 | 2642 | * We won't be reading the referent as part of
|
|
0 commit comments