refs: fully reset `struct ref_iterator::ref` on iteration

With the introduction of the `struct ref_iterator::ref` field it now is a whole lot easier to introduce new fields that become accessible to the caller without having to adapt every single callsite. But there's a downside: when a new field is introduced we always have to adapt all backends to set that field. This isn't something we can avoid in the general case: when the new field is expected to be populated by all backends we of course cannot avoid doing so. But new fields may be entirely optional, in which case we'd still have such churn. And furthermore, it is very easy right now to leak state from a previous iteration into the next iteration. Address this issue by ensuring that the reference backends all fully reset the field on every single iteration. This ensures that no state from previous iterations can leak into the next one. And it ensures that any newly introduced fields will be zeroed out by default. Note that we don't have to explicitly adapt the "files" backend, as it uses the `cache_ref_iterator` internally. Furthermore, other "wrapping" iterators like for example the `prefix_ref_iterator` copy around the whole reference, so these don't need to be adapted either. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Oct 23, 2025 at 09:16 UTC 4cea0422879f6a64c0f7ad0ddac6d43897a53e94
3 files changed +4 -1
refs/packed-backend.c
+2 -1
@@ -882,6 +882,7 @@ static int next_record(struct packed_ref_iterator *iter)
882 {
883 const char *p, *eol;
884
885 + memset(&iter->base.ref, 0, sizeof(iter->base.ref));
886 strbuf_reset(&iter->refname_buf);
887
888 /*
@@ -916,6 +917,7 @@ static int next_record(struct packed_ref_iterator *iter)
917 !isspace(*p++))
918 die_invalid_line(iter->snapshot->refs->path,
919 iter->pos, iter->eof - iter->pos);
920 + iter->base.ref.oid = &iter->oid;
921
922 eol = memchr(p, '\n', iter->eof - p);
923 if (!eol)
@@ -1194,7 +1196,6 @@ static struct ref_iterator *packed_ref_iterator_begin(
1196 iter->snapshot = snapshot;
1197 acquire_snapshot(snapshot);
1198 strbuf_init(&iter->refname_buf, 0);
1197 - iter->base.ref.oid = &iter->oid;
1199 iter->repo = ref_store->repo;
1200 iter->flags = flags;
1201
refs/ref-cache.c
+1
@@ -425,6 +425,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
425 level->prefix_state = entry_prefix_state;
426 level->index = -1;
427 } else {
428 + memset(&iter->base.ref, 0, sizeof(iter->base.ref));
429 iter->base.ref.name = entry->name;
430 iter->base.ref.target = entry->u.value.referent;
431 iter->base.ref.oid = &entry->u.value.oid;
refs/reftable-backend.c
+1
@@ -704,6 +704,7 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator)
704 &iter->oid, flags))
705 continue;
706
707 + memset(&iter->base.ref, 0, sizeof(iter->base.ref));
708 iter->base.ref.name = iter->ref.refname;
709 iter->base.ref.target = referent;
710 iter->base.ref.oid = &iter->oid;