Introduce a new function `ref_transaction_update_reflog`, for clients to add a reflog update to a transaction. While the existing function `ref_transaction_update` also allows clients to add a reflog entry, this function does a few things more, It: - Enforces that only a reflog entry is added and does not update the ref itself. - Allows the users to also provide the committer information. This means clients can add reflog entries with custom committer information. A follow up commit will utilize this function to add reflog support to `git refs migrate`. Signed-off-by: Karthik Nayak <karthik.188@xxxxxxxxx> --- refs.c | 89 +++++++++++++++++++++++++++++++++++++------------ refs.h | 12 +++++++ refs/files-backend.c | 48 +++++++++++++++----------- refs/refs-internal.h | 16 +++++---- refs/reftable-backend.c | 6 ++-- 5 files changed, 122 insertions(+), 49 deletions(-) diff --git a/refs.c b/refs.c index 732c236a3fd0cf324cc172b48d3d54f6dbadf4a4..602a65873181a90751def525608a7fa7bea59562 100644 --- a/refs.c +++ b/refs.c @@ -1160,13 +1160,15 @@ void ref_transaction_free(struct ref_transaction *transaction) free(transaction); } -struct ref_update *ref_transaction_add_update( - struct ref_transaction *transaction, - const char *refname, unsigned int flags, - const struct object_id *new_oid, - const struct object_id *old_oid, - const char *new_target, const char *old_target, - const char *msg) +struct ref_update *ref_transaction_add_update(struct ref_transaction *transaction, + const char *refname, + unsigned int flags, + const struct object_id *new_oid, + const struct object_id *old_oid, + const char *new_target, + const char *old_target, + const char *committer_info, + const char *msg) { struct ref_update *update; @@ -1190,8 +1192,15 @@ struct ref_update *ref_transaction_add_update( oidcpy(&update->new_oid, new_oid); if ((flags & REF_HAVE_OLD) && old_oid) oidcpy(&update->old_oid, old_oid); - if (!(flags & REF_SKIP_CREATE_REFLOG)) + if (!(flags & REF_SKIP_CREATE_REFLOG)) { + if (committer_info) { + struct strbuf sb = STRBUF_INIT; + strbuf_addstr(&sb, committer_info); + update->committer_info = strbuf_detach(&sb, NULL); + } + update->msg = normalize_reflog_message(msg); + } return update; } @@ -1199,20 +1208,29 @@ struct ref_update *ref_transaction_add_update( static int transaction_refname_verification(const char *refname, const struct object_id *new_oid, unsigned int flags, + unsigned int reflog, struct strbuf *err) { if (flags & REF_SKIP_REFNAME_VERIFICATION) return 0; if (is_pseudo_ref(refname)) { - strbuf_addf(err, _("refusing to update pseudoref '%s'"), - refname); + if (reflog) + strbuf_addf(err, _("refusing to update reflog for pseudoref '%s'"), + refname); + else + strbuf_addf(err, _("refusing to update pseudoref '%s'"), + refname); return -1; } else if ((new_oid && !is_null_oid(new_oid)) ? check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) : !refname_is_safe(refname)) { - strbuf_addf(err, _("refusing to update ref with bad name '%s'"), - refname); + if (reflog) + strbuf_addf(err, _("refusing to update reflog with bad name '%s'"), + refname); + else + strbuf_addf(err, _("refusing to update ref with bad name '%s'"), + refname); return -1; } @@ -1238,7 +1256,7 @@ int ref_transaction_update(struct ref_transaction *transaction, return -1; } - ret = transaction_refname_verification(refname, new_oid, flags, err); + ret = transaction_refname_verification(refname, new_oid, flags, 0, err); if (ret) return ret; @@ -1255,18 +1273,47 @@ int ref_transaction_update(struct ref_transaction *transaction, flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0); flags |= (new_target ? REF_HAVE_NEW : 0) | (old_target ? REF_HAVE_OLD : 0); - ref_transaction_add_update(transaction, refname, flags, - new_oid, old_oid, new_target, - old_target, msg); + ref_transaction_add_update(transaction, refname, flags, new_oid, + old_oid, new_target, old_target, NULL, msg); + return 0; +} + +int ref_transaction_update_reflog(struct ref_transaction *transaction, + const char *refname, + const struct object_id *new_oid, + const struct object_id *old_oid, + const char *committer_info, unsigned int flags, + const char *msg, unsigned int index, + struct strbuf *err) +{ + struct ref_update *update; + int ret; + + assert(err); + + ret = transaction_refname_verification(refname, new_oid, flags, 1, err); + if (ret) + return ret; + + flags |= REF_LOG_ONLY | REF_NO_DEREF; + + update = ref_transaction_add_update(transaction, refname, flags, + new_oid, old_oid, NULL, NULL, + committer_info, msg); + /* + * While we do set the old_oid value, we unset the flag to skip + * old_oid verification which only makes sense for refs. + */ + update->flags &= ~REF_HAVE_OLD; + update->index = index; + return 0; } int ref_transaction_create(struct ref_transaction *transaction, - const char *refname, - const struct object_id *new_oid, - const char *new_target, - unsigned int flags, const char *msg, - struct strbuf *err) + const char *refname, const struct object_id *new_oid, + const char *new_target, unsigned int flags, + const char *msg, struct strbuf *err) { if (new_oid && new_target) BUG("create called with both new_oid and new_target set"); diff --git a/refs.h b/refs.h index a5bedf48cf6de91005a7e8d0bf58ca98350397a6..b86d2cd87be33f7bb1b31fce711d6c7c8d9491c9 100644 --- a/refs.h +++ b/refs.h @@ -727,6 +727,18 @@ int ref_transaction_update(struct ref_transaction *transaction, unsigned int flags, const char *msg, struct strbuf *err); +/* + * Similar to `ref_transaction_update`, but this function is only for adding + * a reflog updates. Supports providing custom committer information. + */ +int ref_transaction_update_reflog(struct ref_transaction *transaction, + const char *refname, + const struct object_id *new_oid, + const struct object_id *old_oid, + const char *committer_info, unsigned int flags, + const char *msg, unsigned int index, + struct strbuf *err); + /* * Add a reference creation to transaction. new_oid is the value that * the reference should have after the update; it must not be diff --git a/refs/files-backend.c b/refs/files-backend.c index 9c929c1ac33bc62a75620e684a809d46b574f1c6..32975e0fd7a03ab8ddf99c0a68af99921d3f5090 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1268,10 +1268,10 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r) transaction = ref_store_transaction_begin(&refs->base, 0, &err); if (!transaction) goto cleanup; - ref_transaction_add_update( - transaction, r->name, - REF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | REF_IS_PRUNING, - null_oid(), &r->oid, NULL, NULL, NULL); + ref_transaction_add_update(transaction, r->name, + REF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | + REF_IS_PRUNING, null_oid(), &r->oid, NULL, + NULL, NULL, NULL); if (ref_transaction_commit(transaction, &err)) goto cleanup; @@ -2418,7 +2418,7 @@ static int split_head_update(struct ref_update *update, transaction, "HEAD", update->flags | REF_LOG_ONLY | REF_NO_DEREF, &update->new_oid, &update->old_oid, - NULL, NULL, update->msg); + NULL, NULL, update->committer_info, update->msg); /* * Add "HEAD". This insertion is O(N) in the transaction @@ -2482,7 +2482,8 @@ static int split_symref_update(struct ref_update *update, transaction, referent, new_flags, update->new_target ? NULL : &update->new_oid, update->old_target ? NULL : &update->old_oid, - update->new_target, update->old_target, update->msg); + update->new_target, update->old_target, NULL, + update->msg); new_update->parent_update = update; @@ -2911,11 +2912,11 @@ static int files_transaction_prepare(struct ref_store *ref_store, packed_transaction; } - ref_transaction_add_update( - packed_transaction, update->refname, - REF_HAVE_NEW | REF_NO_DEREF, - &update->new_oid, NULL, - NULL, NULL, NULL); + ref_transaction_add_update(packed_transaction, + update->refname, + REF_HAVE_NEW | REF_NO_DEREF, + &update->new_oid, NULL, NULL, + NULL, NULL, NULL); } } @@ -3080,10 +3081,12 @@ static int files_transaction_finish_initial(struct files_ref_store *refs, } /* - * packed-refs don't support symbolic refs and root refs, so we - * have to queue these references via the loose transaction. + * packed-refs don't support symbolic refs, root refs and reflogs, + * so we have to queue these references via the loose transaction. */ - if (update->new_target || is_root_ref(update->refname)) { + if (update->new_target || + is_root_ref(update->refname) || + (update->flags & REF_LOG_ONLY)) { if (!loose_transaction) { loose_transaction = ref_store_transaction_begin(&refs->base, 0, err); if (!loose_transaction) { @@ -3092,15 +3095,22 @@ static int files_transaction_finish_initial(struct files_ref_store *refs, } } - ref_transaction_add_update(loose_transaction, update->refname, - update->flags & ~REF_HAVE_OLD, - update->new_target ? NULL : &update->new_oid, NULL, - update->new_target, NULL, NULL); + if (update->flags & REF_LOG_ONLY) + ref_transaction_add_update(loose_transaction, update->refname, + update->flags, &update->new_oid, + &update->old_oid, NULL, NULL, + update->committer_info, update->msg); + else + ref_transaction_add_update(loose_transaction, update->refname, + update->flags & ~REF_HAVE_OLD, + update->new_target ? NULL : &update->new_oid, NULL, + update->new_target, NULL, update->committer_info, + NULL); } else { ref_transaction_add_update(packed_transaction, update->refname, update->flags & ~REF_HAVE_OLD, &update->new_oid, &update->old_oid, - NULL, NULL, NULL); + NULL, NULL, update->committer_info, NULL); } } diff --git a/refs/refs-internal.h b/refs/refs-internal.h index f5c733d099f0c6f1076a25f4f77d9d5eb345ec87..82c1387d1e6ab3658b31fe99c95f98645ff1ebf1 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -156,13 +156,15 @@ int ref_update_reject_duplicates(struct string_list *refnames, * dereferenced if the REF_HAVE_NEW and REF_HAVE_OLD bits, * respectively, are set in flags. */ -struct ref_update *ref_transaction_add_update( - struct ref_transaction *transaction, - const char *refname, unsigned int flags, - const struct object_id *new_oid, - const struct object_id *old_oid, - const char *new_target, const char *old_target, - const char *msg); +struct ref_update *ref_transaction_add_update(struct ref_transaction *transaction, + const char *refname, + unsigned int flags, + const struct object_id *new_oid, + const struct object_id *old_oid, + const char *new_target, + const char *old_target, + const char *committer_info, + const char *msg); /* * Transaction states. diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c index c008f20be719fec3af6a8f81c821cb9c263764d7..b2e3ba877de9e59fea5a4d066eb13e60ef22a32b 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c @@ -1078,7 +1078,8 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store, new_update = ref_transaction_add_update( transaction, "HEAD", u->flags | REF_LOG_ONLY | REF_NO_DEREF, - &u->new_oid, &u->old_oid, NULL, NULL, u->msg); + &u->new_oid, &u->old_oid, NULL, NULL, NULL, + u->msg); string_list_insert(&affected_refnames, new_update->refname); } @@ -1161,7 +1162,8 @@ static int reftable_be_transaction_prepare(struct ref_store *ref_store, transaction, referent.buf, new_flags, u->new_target ? NULL : &u->new_oid, u->old_target ? NULL : &u->old_oid, - u->new_target, u->old_target, u->msg); + u->new_target, u->old_target, + u->committer_info, u->msg); new_update->parent_update = u; -- 2.47.1