refs: add peeled object ID to the `ref_update` struct

Certain reference backends {packed, reftable}, have the ability to also store the peeled object ID for a reference pointing to a tag object. This has the added benefit that during retrieval of such references, we also obtain the peeled object ID without having to use the ODB. To provide this functionality, each backend independently calls the ODB to obtain the peeled OID. To move this functionality to the generic layer, there must be support infrastructure to pass in a peeled OID for reference updates. Add a `peeled` field to the `ref_update` structure and modify `ref_transaction_add_update()` to receive and copy this object ID to the `ref_update` structure. Finally, modify `ref_transaction_update()` to peel tag objects and pass the peeled OID to `ref_transaction_add_update()`. Update all callers of these functions with the new function parameters. Callers which only add reflog updates, need to only pass in NULL, since for reflogs, we don't store peeled OIDs. Reference deletions also only need to pass in NULL. For others, pass along the peeled OID if available. In a following commit, we'll modify the backends to use this peeled OID instead of parsing it themselves. Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Karthik Nayak committed May 4, 2026 at 19:44 UTC ad0f76d7824558e08cc35f77df2fc0e48ee1b28b
4 files changed +42 -13
refs.c
+13 -2
@@ -1307,6 +1307,7 @@ struct ref_update *ref_transaction_add_update(
1307 const char *refname, unsigned int flags,
1308 const struct object_id *new_oid,
1309 const struct object_id *old_oid,
1310 + const struct object_id *peeled,
1311 const char *new_target, const char *old_target,
1312 const char *committer_info,
1313 const char *msg)
@@ -1339,6 +1340,8 @@ struct ref_update *ref_transaction_add_update(
1340 update->committer_info = xstrdup_or_null(committer_info);
1341 update->msg = normalize_reflog_message(msg);
1342 }
1343 + if (flags & REF_HAVE_PEELED)
1344 + oidcpy(&update->peeled, peeled);
1345
1346 /*
1347 * This list is generally used by the backends to avoid duplicates.
@@ -1392,6 +1395,8 @@ enum ref_transaction_error ref_transaction_update(struct ref_transaction *transa
1395 unsigned int flags, const char *msg,
1396 struct strbuf *err)
1397 {
1398 + struct object_id peeled;
1399 +
1400 assert(err);
1401
1402 if ((flags & REF_FORCE_CREATE_REFLOG) &&
@@ -1432,10 +1437,16 @@ enum ref_transaction_error ref_transaction_update(struct ref_transaction *transa
1437 oid_to_hex(new_oid), refname);
1438 return REF_TRANSACTION_ERROR_INVALID_NEW_VALUE;
1439 }
1440 +
1441 + if (o->type == OBJ_TAG) {
1442 + if (!peel_object(transaction->ref_store->repo, new_oid, &peeled,
1443 + PEEL_OBJECT_VERIFY_TAGGED_OBJECT_TYPE))
1444 + flags |= REF_HAVE_PEELED;
1445 + }
1446 }
1447
1448 ref_transaction_add_update(transaction, refname, flags,
1438 - new_oid, old_oid, new_target,
1449 + new_oid, old_oid, &peeled, new_target,
1450 old_target, NULL, msg);
1451
1452 return 0;
@@ -1462,7 +1473,7 @@ int ref_transaction_update_reflog(struct ref_transaction *transaction,
1473 return -1;
1474
1475 update = ref_transaction_add_update(transaction, refname, flags,
1465 - new_oid, old_oid, NULL, NULL,
1476 + new_oid, old_oid, NULL, NULL, NULL,
1477 committer_info, msg);
1478 update->index = index;
1479
refs/files-backend.c
+12 -8
@@ -1325,7 +1325,8 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)
1325 ref_transaction_add_update(
1326 transaction, r->name,
1327 REF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | REF_IS_PRUNING,
1328 - null_oid(the_hash_algo), &r->oid, NULL, NULL, NULL, NULL);
1328 + null_oid(the_hash_algo), &r->oid, NULL, NULL, NULL,
1329 + NULL, NULL);
1330 if (ref_transaction_commit(transaction, &err))
1331 goto cleanup;
1332
@@ -2468,7 +2469,7 @@ static enum ref_transaction_error split_head_update(struct ref_update *update,
2469 new_update = ref_transaction_add_update(
2470 transaction, "HEAD",
2471 update->flags | REF_LOG_ONLY | REF_NO_DEREF | REF_LOG_VIA_SPLIT,
2471 - &update->new_oid, &update->old_oid,
2472 + &update->new_oid, &update->old_oid, &update->peeled,
2473 NULL, NULL, update->committer_info, update->msg);
2474 new_update->parent_update = update;
2475
@@ -2530,8 +2531,8 @@ static enum ref_transaction_error split_symref_update(struct ref_update *update,
2531 transaction, referent, new_flags,
2532 update->new_target ? NULL : &update->new_oid,
2533 update->old_target ? NULL : &update->old_oid,
2533 - update->new_target, update->old_target, NULL,
2534 - update->msg);
2534 + &update->peeled, update->new_target, update->old_target,
2535 + NULL, update->msg);
2536
2537 new_update->parent_update = update;
2538
@@ -2994,7 +2995,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
2995 ref_transaction_add_update(
2996 packed_transaction, update->refname,
2997 REF_HAVE_NEW | REF_NO_DEREF,
2997 - &update->new_oid, NULL,
2998 + &update->new_oid, NULL, NULL,
2999 NULL, NULL, NULL, NULL);
3000 }
3001 }
@@ -3200,19 +3201,22 @@ static int files_transaction_finish_initial(struct files_ref_store *refs,
3201 if (update->flags & REF_LOG_ONLY)
3202 ref_transaction_add_update(loose_transaction, update->refname,
3203 update->flags, &update->new_oid,
3203 - &update->old_oid, NULL, NULL,
3204 + &update->old_oid, &update->peeled,
3205 + NULL, NULL,
3206 update->committer_info, update->msg);
3207 else
3208 ref_transaction_add_update(loose_transaction, update->refname,
3209 update->flags & ~REF_HAVE_OLD,
3210 update->new_target ? NULL : &update->new_oid, NULL,
3209 - update->new_target, NULL, update->committer_info,
3211 + &update->peeled, update->new_target,
3212 + NULL, update->committer_info,
3213 NULL);
3214 } else {
3215 ref_transaction_add_update(packed_transaction, update->refname,
3216 update->flags & ~REF_HAVE_OLD,
3217 &update->new_oid, &update->old_oid,
3215 - NULL, NULL, update->committer_info, NULL);
3218 + &update->peeled, NULL, NULL,
3219 + update->committer_info, NULL);
3220 }
3221 }
3222
refs/refs-internal.h
+14
@@ -39,6 +39,13 @@ struct ref_transaction;
39 */
40 #define REF_LOG_ONLY (1 << 7)
41
42 +/*
43 + * The reference contains a peeled object ID. This is used when the
44 + * new_oid is pointing to a tag object and the reference backend
45 + * wants to also store the peeled value for optimized retrieval.
46 + */
47 +#define REF_HAVE_PEELED (1 << 15)
48 +
49 /*
50 * Return the length of time to retry acquiring a loose reference lock
51 * before giving up, in milliseconds:
@@ -92,6 +99,12 @@ struct ref_update {
99 */
100 struct object_id old_oid;
101
102 + /*
103 + * If the new_oid points to a tag object, set this to the peeled
104 + * object ID for optimized retrieval without needed to hit the odb.
105 + */
106 + struct object_id peeled;
107 +
108 /*
109 * If set, point the reference to this value. This can also be
110 * used to convert regular references to become symbolic refs.
@@ -169,6 +182,7 @@ struct ref_update *ref_transaction_add_update(
182 const char *refname, unsigned int flags,
183 const struct object_id *new_oid,
184 const struct object_id *old_oid,
185 + const struct object_id *peeled,
186 const char *new_target, const char *old_target,
187 const char *committer_info,
188 const char *msg);
refs/reftable-backend.c
+3 -3
@@ -1107,8 +1107,8 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
1107 ref_transaction_add_update(
1108 transaction, "HEAD",
1109 u->flags | REF_LOG_ONLY | REF_NO_DEREF,
1110 - &u->new_oid, &u->old_oid, NULL, NULL, NULL,
1111 - u->msg);
1110 + &u->new_oid, &u->old_oid, &u->peeled, NULL, NULL,
1111 + NULL, u->msg);
1112 }
1113
1114 ret = reftable_backend_read_ref(be, rewritten_ref,
@@ -1194,7 +1194,7 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
1194 transaction, referent->buf, new_flags,
1195 u->new_target ? NULL : &u->new_oid,
1196 u->old_target ? NULL : &u->old_oid,
1197 - u->new_target, u->old_target,
1197 + &u->peeled, u->new_target, u->old_target,
1198 u->committer_info, u->msg);
1199
1200 new_update->parent_update = u;