odb: do not use "blank" substitute for NULL

When various *object_info() functions are given an extended object info structure as NULL by a caller that does not want any details, the code uses a file-scope static blank_oi and passes it down to the helper functions they use, to avoid handling NULL specifically. The ps/object-read-stream topic graduated to 'master' recently however had a bug that assumed that two identically named file-scope static variables in two functions are the same, which of course is not the case. This made "git commit" take 0.38 seconds to 1508 seconds in some case, as reported by Aaron Plattner here: https://lore.kernel.org/git/f4ba7e89-4717-4b36-921f-56537131fd69@nvidia.com/ We _could_ move the blank_oi variable to the global scope in common section to fix this regression, but explicitly handling the NULL is a much safer fix. It would also reduce the chance of errors that somebody accidentally writes into blank_oi, making its contents dirty, which potentially will make subsequent calls into the function misbehave. By explicitly handling NULL input, we no longer have to worry about it. Reported-by: Aaron Plattner <aplattner@nvidia.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Dec 18, 2025 at 12:35 UTC a650ad996db85b64643970dd7dc5920f989260a0
3 files changed +18 -22
object-file.c
+4 -4
@@ -426,7 +426,7 @@ int odb_source_loose_read_object_info(struct odb_source *source,
426 unsigned long size_scratch;
427 enum object_type type_scratch;
428
429 - if (oi->delta_base_oid)
429 + if (oi && oi->delta_base_oid)
430 oidclr(oi->delta_base_oid, source->odb->repo->hash_algo);
431
432 /*
@@ -437,13 +437,13 @@ int odb_source_loose_read_object_info(struct odb_source *source,
437 * return value implicitly indicates whether the
438 * object even exists.
439 */
440 - if (!oi->typep && !oi->sizep && !oi->contentp) {
440 + if (!oi || (!oi->typep && !oi->sizep && !oi->contentp)) {
441 struct stat st;
442 - if (!oi->disk_sizep && (flags & OBJECT_INFO_QUICK))
442 + if ((!oi || !oi->disk_sizep) && (flags & OBJECT_INFO_QUICK))
443 return quick_has_loose(source->loose, oid) ? 0 : -1;
444 if (stat_loose_object(source->loose, oid, &st, &path) < 0)
445 return -1;
446 - if (oi->disk_sizep)
446 + if (oi && oi->disk_sizep)
447 *oi->disk_sizep = st.st_size;
448 return 0;
449 }
odb.c
+13 -16
@@ -664,34 +664,31 @@ static int do_oid_object_info_extended(struct object_database *odb,
664 const struct object_id *oid,
665 struct object_info *oi, unsigned flags)
666 {
667 - static struct object_info blank_oi = OBJECT_INFO_INIT;
667 const struct cached_object *co;
668 const struct object_id *real = oid;
669 int already_retried = 0;
670
672 -
671 if (flags & OBJECT_INFO_LOOKUP_REPLACE)
672 real = lookup_replace_object(odb->repo, oid);
673
674 if (is_null_oid(real))
675 return -1;
676
679 - if (!oi)
680 - oi = &blank_oi;
681 -
677 co = find_cached_object(odb, real);
678 if (co) {
684 - if (oi->typep)
685 - *(oi->typep) = co->type;
686 - if (oi->sizep)
687 - *(oi->sizep) = co->size;
688 - if (oi->disk_sizep)
689 - *(oi->disk_sizep) = 0;
690 - if (oi->delta_base_oid)
691 - oidclr(oi->delta_base_oid, odb->repo->hash_algo);
692 - if (oi->contentp)
693 - *oi->contentp = xmemdupz(co->buf, co->size);
694 - oi->whence = OI_CACHED;
679 + if (oi) {
680 + if (oi->typep)
681 + *(oi->typep) = co->type;
682 + if (oi->sizep)
683 + *(oi->sizep) = co->size;
684 + if (oi->disk_sizep)
685 + *(oi->disk_sizep) = 0;
686 + if (oi->delta_base_oid)
687 + oidclr(oi->delta_base_oid, odb->repo->hash_algo);
688 + if (oi->contentp)
689 + *oi->contentp = xmemdupz(co->buf, co->size);
690 + oi->whence = OI_CACHED;
691 + }
692 return 0;
693 }
694
packfile.c
+1 -2
@@ -2095,7 +2095,6 @@ int packfile_store_read_object_info(struct packfile_store *store,
2095 struct object_info *oi,
2096 unsigned flags UNUSED)
2097 {
2098 - static struct object_info blank_oi = OBJECT_INFO_INIT;
2098 struct pack_entry e;
2099 int rtype;
2100
@@ -2106,7 +2105,7 @@ int packfile_store_read_object_info(struct packfile_store *store,
2105 * We know that the caller doesn't actually need the
2106 * information below, so return early.
2107 */
2109 - if (oi == &blank_oi)
2108 + if (!oi)
2109 return 0;
2110
2111 rtype = packed_object_info(store->odb->repo, e.p, e.offset, oi);