commit: don't lazy-fetch commits

When parsing commits, fail fast when the commit is missing or corrupt, instead of attempting to fetch them. This is done by inlining repo_read_object_file() and setting the flag that prevents fetching. This is motivated by a situation in which through a bug (not necessarily through Git), there was corruption in the object store of a partial clone. In this particular case, the problem was exposed when "git gc" tried to expire reflogs, which calls repo_parse_commit(), which triggers fetches of the missing commits. (There are other possible solutions to this problem including passing an argument from "git gc" to "git reflog" to inhibit all lazy fetches, but I think that this fix is at the wrong level - fixing "git reflog" means that this particular command works fine, or so we think (it will fail if it somehow needs to read a legitimately missing blob, say, a .gitmodules file), but fixing repo_parse_commit() will fix a whole class of bugs.) Signed-off-by: Jonathan Tan <jonathantanmy@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jonathan Tan committed Dec 14, 2022 at 11:17 UTC 7e2ad1cda2759a14f7b046c4c82e2418f8af878c
1 file changed +13 -2
commit.c
+13 -2
@@ -508,6 +508,17 @@ int repo_parse_commit_internal(struct repository *r,
508 enum object_type type;
509 void *buffer;
510 unsigned long size;
511 + struct object_info oi = {
512 + .typep = &type,
513 + .sizep = &size,
514 + .contentp = &buffer,
515 + };
516 + /*
517 + * Git does not support partial clones that exclude commits, so set
518 + * OBJECT_INFO_SKIP_FETCH_OBJECT to fail fast when an object is missing.
519 + */
520 + int flags = OBJECT_INFO_LOOKUP_REPLACE | OBJECT_INFO_SKIP_FETCH_OBJECT |
521 + OBJECT_INFO_DIE_IF_CORRUPT;
522 int ret;
523
524 if (!item)
@@ -516,8 +527,8 @@ int repo_parse_commit_internal(struct repository *r,
527 return 0;
528 if (use_commit_graph && parse_commit_in_graph(r, item))
529 return 0;
519 - buffer = repo_read_object_file(r, &item->object.oid, &type, &size);
520 - if (!buffer)
530 +
531 + if (oid_object_info_extended(r, &item->object.oid, &oi, flags) < 0)
532 return quiet_on_missing ? -1 :
533 error("Could not read %s",
534 oid_to_hex(&item->object.oid));