Merge branch 'jt/commit-graph-missing'

A regression where commit objects missing from a commit-graph can
cause an infinite loop when doing a fetch in a partial clone has
been fixed.

* jt/commit-graph-missing:
  fetch-pack: die if in commit graph but not obj db
  Revert "fetch-pack: add a deref_without_lazy_fetch_extended()"
maint
Junio C Hamano 2024-11-13 08:35:33 +09:00
commit 486c9d3995
2 changed files with 24 additions and 22 deletions

View File

@ -122,29 +122,41 @@ static void for_each_cached_alternate(struct fetch_negotiator *negotiator,
cb(negotiator, cache.items[i]);
}

static struct commit *deref_without_lazy_fetch_extended(const struct object_id *oid,
int mark_tags_complete,
enum object_type *type,
unsigned int oi_flags)
static void die_in_commit_graph_only(const struct object_id *oid)
{
struct object_info info = { .typep = type };
die(_("You are attempting to fetch %s, which is in the commit graph file but not in the object database.\n"
"This is probably due to repo corruption.\n"
"If you are attempting to repair this repo corruption by refetching the missing object, use 'git fetch --refetch' with the missing object."),
oid_to_hex(oid));
}

static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
int mark_tags_complete_and_check_obj_db)
{
enum object_type type;
struct object_info info = { .typep = &type };
struct commit *commit;

commit = lookup_commit_in_graph(the_repository, oid);
if (commit)
if (commit) {
if (mark_tags_complete_and_check_obj_db) {
if (!has_object(the_repository, oid, 0))
die_in_commit_graph_only(oid);
}
return commit;
}

while (1) {
if (oid_object_info_extended(the_repository, oid, &info,
oi_flags))
OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK))
return NULL;
if (*type == OBJ_TAG) {
if (type == OBJ_TAG) {
struct tag *tag = (struct tag *)
parse_object(the_repository, oid);

if (!tag->tagged)
return NULL;
if (mark_tags_complete)
if (mark_tags_complete_and_check_obj_db)
tag->object.flags |= COMPLETE;
oid = &tag->tagged->oid;
} else {
@ -152,7 +164,7 @@ static struct commit *deref_without_lazy_fetch_extended(const struct object_id *
}
}

if (*type == OBJ_COMMIT) {
if (type == OBJ_COMMIT) {
struct commit *commit = lookup_commit(the_repository, oid);
if (!commit || repo_parse_commit(the_repository, commit))
return NULL;
@ -162,16 +174,6 @@ static struct commit *deref_without_lazy_fetch_extended(const struct object_id *
return NULL;
}


static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
int mark_tags_complete)
{
enum object_type type;
unsigned flags = OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK;
return deref_without_lazy_fetch_extended(oid, mark_tags_complete,
&type, flags);
}

static int rev_list_insert_ref(struct fetch_negotiator *negotiator,
const struct object_id *oid)
{

View File

@ -38,9 +38,9 @@ test_expect_success 'fetch any commit from promisor with the usage of the commit
git -C with-commit-graph config remote.origin.partialclonefilter blob:none &&
test_commit -C with-commit any-commit &&
anycommit=$(git -C with-commit rev-parse HEAD) &&
GIT_TRACE="$(pwd)/trace.txt" \
test_must_fail env GIT_TRACE="$(pwd)/trace.txt" \
git -C with-commit-graph fetch origin $anycommit 2>err &&
! grep "fatal: promisor-remote: unable to fork off fetch subprocess" err &&
test_grep ! "fatal: promisor-remote: unable to fork off fetch subprocess" err &&
grep "git fetch origin" trace.txt >actual &&
test_line_count = 1 actual
'