refs: introduce the `ref_transaction_update_reflog` function

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. The `transaction_refname_valid()` function also modifies the error message selectively based on the type of the update. This change also affects reflog updates which go through `ref_transaction_update()`. A follow up commit will utilize this function to add reflog support to `git refs migrate`. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Karthik Nayak committed Dec 16, 2024 at 17:44 UTC 84675fa2717e08b39bf810eb9a439068ac915dfb
3 files changed +65 -12
refs.c
+35 -4
@@ -1207,14 +1207,14 @@ static int transaction_refname_valid(const char *refname,
1207 return 1;
1208
1209 if (is_pseudo_ref(refname)) {
1210 - strbuf_addf(err, _("refusing to update pseudoref '%s'"),
1211 - refname);
1210 + const char *what = flags & REF_LOG_ONLY ? "reflog for pseudoref" : "pseudoref";
1211 + strbuf_addf(err, _("refusing to update %s '%s'"), what, refname);
1212 return 0;
1213 } else if ((new_oid && !is_null_oid(new_oid)) ?
1214 check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
1215 !refname_is_safe(refname)) {
1216 - strbuf_addf(err, _("refusing to update ref with bad name '%s'"),
1217 - refname);
1216 + const char *what = flags & REF_LOG_ONLY ? "reflog with bad name" : "ref with bad name";
1217 + strbuf_addf(err, _("refusing to update %s '%s'"), what, refname);
1218 return 0;
1219 }
1220
@@ -1257,6 +1257,37 @@ int ref_transaction_update(struct ref_transaction *transaction,
1257 ref_transaction_add_update(transaction, refname, flags,
1258 new_oid, old_oid, new_target,
1259 old_target, NULL, msg);
1260 +
1261 + return 0;
1262 +}
1263 +
1264 +int ref_transaction_update_reflog(struct ref_transaction *transaction,
1265 + const char *refname,
1266 + const struct object_id *new_oid,
1267 + const struct object_id *old_oid,
1268 + const char *committer_info, unsigned int flags,
1269 + const char *msg, unsigned int index,
1270 + struct strbuf *err)
1271 +{
1272 + struct ref_update *update;
1273 +
1274 + assert(err);
1275 +
1276 + flags |= REF_LOG_ONLY | REF_NO_DEREF;
1277 +
1278 + if (!transaction_refname_valid(refname, new_oid, flags, err))
1279 + return -1;
1280 +
1281 + update = ref_transaction_add_update(transaction, refname, flags,
1282 + new_oid, old_oid, NULL, NULL,
1283 + committer_info, msg);
1284 + /*
1285 + * While we do set the old_oid value, we unset the flag to skip
1286 + * old_oid verification which only makes sense for refs.
1287 + */
1288 + update->flags &= ~REF_HAVE_OLD;
1289 + update->index = index;
1290 +
1291 return 0;
1292 }
1293
refs.h
+14
@@ -727,6 +727,20 @@ int ref_transaction_update(struct ref_transaction *transaction,
727 unsigned int flags, const char *msg,
728 struct strbuf *err);
729
730 +/*
731 + * Similar to`ref_transaction_update`, but this function is only for adding
732 + * a reflog update. Supports providing custom committer information. The index
733 + * field can be utiltized to order updates as desired. When not used, the
734 + * updates default to being ordered by refname.
735 + */
736 +int ref_transaction_update_reflog(struct ref_transaction *transaction,
737 + const char *refname,
738 + const struct object_id *new_oid,
739 + const struct object_id *old_oid,
740 + const char *committer_info, unsigned int flags,
741 + const char *msg, unsigned int index,
742 + struct strbuf *err);
743 +
744 /*
745 * Add a reference creation to transaction. new_oid is the value that
746 * the reference should have after the update; it must not be
refs/files-backend.c
+16 -8
@@ -3080,10 +3080,12 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
3080 }
3081
3082 /*
3083 - * packed-refs don't support symbolic refs and root refs, so we
3084 - * have to queue these references via the loose transaction.
3083 + * packed-refs don't support symbolic refs, root refs and reflogs,
3084 + * so we have to queue these references via the loose transaction.
3085 */
3086 - if (update->new_target || is_root_ref(update->refname)) {
3086 + if (update->new_target ||
3087 + is_root_ref(update->refname) ||
3088 + (update->flags & REF_LOG_ONLY)) {
3089 if (!loose_transaction) {
3090 loose_transaction = ref_store_transaction_begin(&refs->base, 0, err);
3091 if (!loose_transaction) {
@@ -3092,11 +3094,17 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
3094 }
3095 }
3096
3095 - ref_transaction_add_update(loose_transaction, update->refname,
3096 - update->flags & ~REF_HAVE_OLD,
3097 - update->new_target ? NULL : &update->new_oid, NULL,
3098 - update->new_target, NULL, update->committer_info,
3099 - NULL);
3097 + if (update->flags & REF_LOG_ONLY)
3098 + ref_transaction_add_update(loose_transaction, update->refname,
3099 + update->flags, &update->new_oid,
3100 + &update->old_oid, NULL, NULL,
3101 + update->committer_info, update->msg);
3102 + else
3103 + ref_transaction_add_update(loose_transaction, update->refname,
3104 + update->flags & ~REF_HAVE_OLD,
3105 + update->new_target ? NULL : &update->new_oid, NULL,
3106 + update->new_target, NULL, update->committer_info,
3107 + NULL);
3108 } else {
3109 ref_transaction_add_update(packed_transaction, update->refname,
3110 update->flags & ~REF_HAVE_OLD,