diff options
| author | Junio C Hamano <gitster@pobox.com> | 2025-10-15 10:29:27 -0700 |
|---|---|---|
| committer | Junio C Hamano <gitster@pobox.com> | 2025-10-15 10:29:28 -0700 |
| commit | e04c0aded3ce7dc02feab55770a98e5ec1aa65fb (patch) | |
| tree | e7249b9c20b3f787e2f91c60cc901260aa07bc52 /refs/files-backend.c | |
| parent | d549c188be4f041a117d3ca942a5ac1e5f9fc704 (diff) | |
| parent | 465eff81de44763832e96fbe609ec269f1c23ade (diff) | |
Merge branch 'ps/reflog-migrate-fixes' into maint-2.51
"git refs migrate" to migrate the reflog entries from a refs
backend to another had a handful of bugs squashed.
* ps/reflog-migrate-fixes:
refs: fix invalid old object IDs when migrating reflogs
refs: stop unsetting REF_HAVE_OLD for log-only updates
refs/files: detect race when generating reflog entry for HEAD
refs: fix identity for migrated reflogs
ident: fix type of string length parameter
builtin/reflog: implement subcommand to write new entries
refs: export `ref_transaction_update_reflog()`
builtin/reflog: improve grouping of subcommands
Documentation/git-reflog: convert to use synopsis type
Diffstat (limited to 'refs/files-backend.c')
| -rw-r--r-- | refs/files-backend.c | 65 |
1 files changed, 58 insertions, 7 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c index 088b52c740..dea46d07ae 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -68,6 +68,12 @@ */ #define REF_DELETED_RMDIR (1 << 9) +/* + * Used to indicate that the reflog-only update has been created via + * `split_head_update()`. + */ +#define REF_LOG_VIA_SPLIT (1 << 14) + struct ref_lock { char *ref_name; struct lock_file lk; @@ -2421,9 +2427,10 @@ static enum ref_transaction_error split_head_update(struct ref_update *update, new_update = ref_transaction_add_update( transaction, "HEAD", - update->flags | REF_LOG_ONLY | REF_NO_DEREF, + update->flags | REF_LOG_ONLY | REF_NO_DEREF | REF_LOG_VIA_SPLIT, &update->new_oid, &update->old_oid, NULL, NULL, update->committer_info, update->msg); + new_update->parent_update = update; /* * Add "HEAD". This insertion is O(N) in the transaction @@ -2494,7 +2501,6 @@ static enum ref_transaction_error split_symref_update(struct ref_update *update, * done when new_update is processed. */ update->flags |= REF_LOG_ONLY | REF_NO_DEREF; - update->flags &= ~REF_HAVE_OLD; return 0; } @@ -2509,8 +2515,9 @@ static enum ref_transaction_error check_old_oid(struct ref_update *update, struct object_id *oid, struct strbuf *err) { - if (!(update->flags & REF_HAVE_OLD) || - oideq(oid, &update->old_oid)) + if (update->flags & REF_LOG_ONLY || + !(update->flags & REF_HAVE_OLD) || + oideq(oid, &update->old_oid)) return 0; if (is_null_oid(&update->old_oid)) { @@ -2601,7 +2608,36 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re update->backend_data = lock; - if (update->type & REF_ISSYMREF) { + if (update->flags & REF_LOG_VIA_SPLIT) { + struct ref_lock *parent_lock; + + if (!update->parent_update) + BUG("split update without a parent"); + + parent_lock = update->parent_update->backend_data; + + /* + * Check that "HEAD" didn't racily change since we have looked + * it up. If it did we must refuse to write the reflog entry. + * + * Note that this does not catch all races: if "HEAD" was + * racily changed to point to one of the refs part of the + * transaction then we would miss writing the split reflog + * entry for "HEAD". + */ + if (!(update->type & REF_ISSYMREF) || + strcmp(update->parent_update->refname, referent.buf)) { + strbuf_addstr(err, "HEAD has been racily updated"); + ret = REF_TRANSACTION_ERROR_GENERIC; + goto out; + } + + if (update->flags & REF_HAVE_OLD) { + oidcpy(&lock->old_oid, &update->old_oid); + } else { + oidcpy(&lock->old_oid, &parent_lock->old_oid); + } + } else if (update->type & REF_ISSYMREF) { if (update->flags & REF_NO_DEREF) { /* * We won't be reading the referent as part of @@ -2977,6 +3013,20 @@ static int parse_and_write_reflog(struct files_ref_store *refs, struct ref_lock *lock, struct strbuf *err) { + struct object_id *old_oid = &lock->old_oid; + + if (update->flags & REF_LOG_USE_PROVIDED_OIDS) { + if (!(update->flags & REF_HAVE_OLD) || + !(update->flags & REF_HAVE_NEW) || + !(update->flags & REF_LOG_ONLY)) { + strbuf_addf(err, _("trying to write reflog for '%s'" + "with incomplete values"), update->refname); + return REF_TRANSACTION_ERROR_GENERIC; + } + + old_oid = &update->old_oid; + } + if (update->new_target) { /* * We want to get the resolved OID for the target, to ensure @@ -2994,7 +3044,7 @@ static int parse_and_write_reflog(struct files_ref_store *refs, } } - if (files_log_ref_write(refs, lock->ref_name, &lock->old_oid, + if (files_log_ref_write(refs, lock->ref_name, old_oid, &update->new_oid, update->committer_info, update->msg, update->flags, err)) { char *old_msg = strbuf_detach(err, NULL); @@ -3062,7 +3112,8 @@ static int files_transaction_finish_initial(struct files_ref_store *refs, for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; - if ((update->flags & REF_HAVE_OLD) && + if (!(update->flags & REF_LOG_ONLY) && + (update->flags & REF_HAVE_OLD) && !is_null_oid(&update->old_oid)) BUG("initial ref transaction with old_sha1 set"); |
