commit-reach(repo_in_merge_bases_many): report missing commits

Some functions in Git's source code follow the convention that returning
a negative value indicates a fatal error, e.g. repository corruption.

Let's use this convention in `repo_in_merge_bases()` to report when one
of the specified commits is missing (i.e. when `repo_parse_commit()`
reports an error).

Also adjust the callers of `repo_in_merge_bases()` to handle such
negative return values.

Note: As of this patch, errors are returned only if any of the specified
merge heads is missing. Over the course of the next patches, missing
commits will also be reported by the `paint_down_to_common()` function,
which is called by `repo_in_merge_bases_many()`, and those errors will
be properly propagated back to the caller at that stage.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Johannes Schindelin 2024-02-28 09:44:09 +00:00 committed by Junio C Hamano
parent 207c40e1e4
commit 24876ebf68
12 changed files with 173 additions and 36 deletions

View File

@ -158,6 +158,8 @@ static int branch_merged(int kind, const char *name,
merged = reference_rev ? repo_in_merge_bases(the_repository, rev,
reference_rev) : 0;
if (merged < 0)
exit(128);
/*
* After the safety valve is fully redefined to "check with
@ -166,9 +168,13 @@ static int branch_merged(int kind, const char *name,
* any of the following code, but during the transition period,
* a gentle reminder is in order.
*/
if ((head_rev != reference_rev) &&
(head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0) != merged) {
if (merged)
if (head_rev != reference_rev) {
int expect = head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0;
if (expect < 0)
exit(128);
if (expect == merged)
; /* okay */
else if (merged)
warning(_("deleting branch '%s' that has been merged to\n"
" '%s', but not yet merged to HEAD"),
name, reference_name);

View File

@ -1625,6 +1625,7 @@ static int update_branch(struct branch *b)
oidclr(&old_oid);
if (!force_update && !is_null_oid(&old_oid)) {
struct commit *old_cmit, *new_cmit;
int ret;
old_cmit = lookup_commit_reference_gently(the_repository,
&old_oid, 0);
@ -1633,7 +1634,10 @@ static int update_branch(struct branch *b)
if (!old_cmit || !new_cmit)
return error("Branch %s is missing commits.", b->name);
if (!repo_in_merge_bases(the_repository, old_cmit, new_cmit)) {
ret = repo_in_merge_bases(the_repository, old_cmit, new_cmit);
if (ret < 0)
exit(128);
if (!ret) {
warning("Not updating %s"
" (new tip %s does not contain %s)",
b->name, oid_to_hex(&b->oid),

View File

@ -982,6 +982,8 @@ static int update_local_ref(struct ref *ref,
uint64_t t_before = getnanotime();
fast_forward = repo_in_merge_bases(the_repository, current,
updated);
if (fast_forward < 0)
exit(128);
forced_updates_ms += (getnanotime() - t_before) / 1000000;
} else {
fast_forward = 1;

View File

@ -1625,7 +1625,7 @@ static struct commit *get_base_commit(const char *base_commit,
{
struct commit *base = NULL;
struct commit **rev;
int i = 0, rev_nr = 0, auto_select, die_on_failure;
int i = 0, rev_nr = 0, auto_select, die_on_failure, ret;
switch (auto_base) {
case AUTO_BASE_NEVER:
@ -1725,7 +1725,10 @@ static struct commit *get_base_commit(const char *base_commit,
rev_nr = DIV_ROUND_UP(rev_nr, 2);
}
if (!repo_in_merge_bases(the_repository, base, rev[0])) {
ret = repo_in_merge_bases(the_repository, base, rev[0]);
if (ret < 0)
exit(128);
if (!ret) {
if (die_on_failure) {
die(_("base commit should be the ancestor of revision list"));
} else {

View File

@ -100,12 +100,16 @@ static int handle_octopus(int count, const char **args, int show_all)
static int handle_is_ancestor(int argc, const char **argv)
{
struct commit *one, *two;
int ret;
if (argc != 2)
die("--is-ancestor takes exactly two commits");
one = get_commit_reference(argv[0]);
two = get_commit_reference(argv[1]);
if (repo_in_merge_bases(the_repository, one, two))
ret = repo_in_merge_bases(the_repository, one, two);
if (ret < 0)
exit(128);
if (ret)
return 0;
else
return 1;

View File

@ -926,6 +926,8 @@ static int get_can_ff(struct object_id *orig_head,
merge_head = lookup_commit_reference(the_repository, orig_merge_head);
ret = repo_is_descendant_of(the_repository, merge_head, list);
free_commit_list(list);
if (ret < 0)
exit(128);
return ret;
}
@ -950,6 +952,8 @@ static int already_up_to_date(struct object_id *orig_head,
commit_list_insert(theirs, &list);
ok = repo_is_descendant_of(the_repository, ours, list);
free_commit_list(list);
if (ok < 0)
exit(128);
if (!ok)
return 0;
}

View File

@ -1526,6 +1526,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
starts_with(name, "refs/heads/")) {
struct object *old_object, *new_object;
struct commit *old_commit, *new_commit;
int ret2;
old_object = parse_object(the_repository, old_oid);
new_object = parse_object(the_repository, new_oid);
@ -1539,7 +1540,10 @@ static const char *update(struct command *cmd, struct shallow_info *si)
}
old_commit = (struct commit *)old_object;
new_commit = (struct commit *)new_object;
if (!repo_in_merge_bases(the_repository, old_commit, new_commit)) {
ret2 = repo_in_merge_bases(the_repository, old_commit, new_commit);
if (ret2 < 0)
exit(128);
if (!ret2) {
rp_error("denying non-fast-forward %s"
" (you should pull first)", name);
ret = "non-fast-forward";

View File

@ -463,11 +463,13 @@ int repo_is_descendant_of(struct repository *r,
} else {
while (with_commit) {
struct commit *other;
int ret;
other = with_commit->item;
with_commit = with_commit->next;
if (repo_in_merge_bases_many(r, other, 1, &commit, 0))
return 1;
ret = repo_in_merge_bases_many(r, other, 1, &commit, 0);
if (ret)
return ret;
}
return 0;
}
@ -597,6 +599,8 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid)
commit_list_insert(old_commit, &old_commit_list);
ret = repo_is_descendant_of(the_repository,
new_commit, old_commit_list);
if (ret < 0)
exit(128);
free_commit_list(old_commit_list);
return ret;
}

View File

@ -1575,8 +1575,11 @@ static int verify_merge_base(struct object_id *head_oid, struct ref *remote)
struct commit *head = lookup_commit_or_die(head_oid, "HEAD");
struct commit *branch = lookup_commit_or_die(&remote->old_oid,
remote->name);
int ret = repo_in_merge_bases(the_repository, branch, head);
return repo_in_merge_bases(the_repository, branch, head);
if (ret < 0)
exit(128);
return ret;
}
static int delete_remote_branch(const char *pattern, int force)

View File

@ -542,6 +542,7 @@ enum conflict_and_info_types {
CONFLICT_SUBMODULE_HISTORY_NOT_AVAILABLE,
CONFLICT_SUBMODULE_MAY_HAVE_REWINDS,
CONFLICT_SUBMODULE_NULL_MERGE_BASE,
CONFLICT_SUBMODULE_CORRUPT,
/* Keep this entry _last_ in the list */
NB_CONFLICT_TYPES,
@ -594,7 +595,9 @@ static const char *type_short_descriptions[] = {
[CONFLICT_SUBMODULE_MAY_HAVE_REWINDS] =
"CONFLICT (submodule may have rewinds)",
[CONFLICT_SUBMODULE_NULL_MERGE_BASE] =
"CONFLICT (submodule lacks merge base)"
"CONFLICT (submodule lacks merge base)",
[CONFLICT_SUBMODULE_CORRUPT] =
"CONFLICT (submodule corrupt)"
};
struct logical_conflict_info {
@ -1708,7 +1711,14 @@ static int find_first_merges(struct repository *repo,
die("revision walk setup failed");
while ((commit = get_revision(&revs)) != NULL) {
struct object *o = &(commit->object);
if (repo_in_merge_bases(repo, b, commit))
int ret = repo_in_merge_bases(repo, b, commit);
if (ret < 0) {
object_array_clear(&merges);
release_revisions(&revs);
return ret;
}
if (ret > 0)
add_object_array(o, NULL, &merges);
}
reset_revision_walk();
@ -1723,9 +1733,17 @@ static int find_first_merges(struct repository *repo,
contains_another = 0;
for (j = 0; j < merges.nr; j++) {
struct commit *m2 = (struct commit *) merges.objects[j].item;
if (i != j && repo_in_merge_bases(repo, m2, m1)) {
contains_another = 1;
break;
if (i != j) {
int ret = repo_in_merge_bases(repo, m2, m1);
if (ret < 0) {
object_array_clear(&merges);
release_revisions(&revs);
return ret;
}
if (ret > 0) {
contains_another = 1;
break;
}
}
}
@ -1747,7 +1765,7 @@ static int merge_submodule(struct merge_options *opt,
{
struct repository subrepo;
struct strbuf sb = STRBUF_INIT;
int ret = 0;
int ret = 0, ret2;
struct commit *commit_o, *commit_a, *commit_b;
int parent_count;
struct object_array merges;
@ -1794,8 +1812,26 @@ static int merge_submodule(struct merge_options *opt,
}
/* check whether both changes are forward */
if (!repo_in_merge_bases(&subrepo, commit_o, commit_a) ||
!repo_in_merge_bases(&subrepo, commit_o, commit_b)) {
ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_a);
if (ret2 < 0) {
path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
path, NULL, NULL, NULL,
_("Failed to merge submodule %s "
"(repository corrupt)"),
path);
goto cleanup;
}
if (ret2 > 0)
ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_b);
if (ret2 < 0) {
path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
path, NULL, NULL, NULL,
_("Failed to merge submodule %s "
"(repository corrupt)"),
path);
goto cleanup;
}
if (!ret2) {
path_msg(opt, CONFLICT_SUBMODULE_MAY_HAVE_REWINDS, 0,
path, NULL, NULL, NULL,
_("Failed to merge submodule %s "
@ -1805,7 +1841,16 @@ static int merge_submodule(struct merge_options *opt,
}
/* Case #1: a is contained in b or vice versa */
if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) {
ret2 = repo_in_merge_bases(&subrepo, commit_a, commit_b);
if (ret2 < 0) {
path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
path, NULL, NULL, NULL,
_("Failed to merge submodule %s "
"(repository corrupt)"),
path);
goto cleanup;
}
if (ret2 > 0) {
oidcpy(result, b);
path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1,
path, NULL, NULL, NULL,
@ -1814,7 +1859,16 @@ static int merge_submodule(struct merge_options *opt,
ret = 1;
goto cleanup;
}
if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) {
ret2 = repo_in_merge_bases(&subrepo, commit_b, commit_a);
if (ret2 < 0) {
path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
path, NULL, NULL, NULL,
_("Failed to merge submodule %s "
"(repository corrupt)"),
path);
goto cleanup;
}
if (ret2 > 0) {
oidcpy(result, a);
path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1,
path, NULL, NULL, NULL,
@ -1839,6 +1893,13 @@ static int merge_submodule(struct merge_options *opt,
parent_count = find_first_merges(&subrepo, path, commit_a, commit_b,
&merges);
switch (parent_count) {
case -1:
path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
path, NULL, NULL, NULL,
_("Failed to merge submodule %s "
"(repository corrupt)"),
path);
break;
case 0:
path_msg(opt, CONFLICT_SUBMODULE_FAILED_TO_MERGE, 0,
path, NULL, NULL, NULL,

View File

@ -1139,7 +1139,13 @@ static int find_first_merges(struct repository *repo,
die("revision walk setup failed");
while ((commit = get_revision(&revs)) != NULL) {
struct object *o = &(commit->object);
if (repo_in_merge_bases(repo, b, commit))
int ret = repo_in_merge_bases(repo, b, commit);
if (ret < 0) {
object_array_clear(&merges);
release_revisions(&revs);
return ret;
}
if (ret)
add_object_array(o, NULL, &merges);
}
reset_revision_walk();
@ -1154,9 +1160,17 @@ static int find_first_merges(struct repository *repo,
contains_another = 0;
for (j = 0; j < merges.nr; j++) {
struct commit *m2 = (struct commit *) merges.objects[j].item;
if (i != j && repo_in_merge_bases(repo, m2, m1)) {
contains_another = 1;
break;
if (i != j) {
int ret = repo_in_merge_bases(repo, m2, m1);
if (ret < 0) {
object_array_clear(&merges);
release_revisions(&revs);
return ret;
}
if (ret > 0) {
contains_another = 1;
break;
}
}
}
@ -1192,7 +1206,7 @@ static int merge_submodule(struct merge_options *opt,
const struct object_id *b)
{
struct repository subrepo;
int ret = 0;
int ret = 0, ret2;
struct commit *commit_base, *commit_a, *commit_b;
int parent_count;
struct object_array merges;
@ -1229,14 +1243,29 @@ static int merge_submodule(struct merge_options *opt,
}
/* check whether both changes are forward */
if (!repo_in_merge_bases(&subrepo, commit_base, commit_a) ||
!repo_in_merge_bases(&subrepo, commit_base, commit_b)) {
ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_a);
if (ret2 < 0) {
output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
goto cleanup;
}
if (ret2 > 0)
ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_b);
if (ret2 < 0) {
output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
goto cleanup;
}
if (!ret2) {
output(opt, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path);
goto cleanup;
}
/* Case #1: a is contained in b or vice versa */
if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) {
ret2 = repo_in_merge_bases(&subrepo, commit_a, commit_b);
if (ret2 < 0) {
output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
goto cleanup;
}
if (ret2) {
oidcpy(result, b);
if (show(opt, 3)) {
output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
@ -1249,7 +1278,12 @@ static int merge_submodule(struct merge_options *opt,
ret = 1;
goto cleanup;
}
if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) {
ret2 = repo_in_merge_bases(&subrepo, commit_b, commit_a);
if (ret2 < 0) {
output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
goto cleanup;
}
if (ret2) {
oidcpy(result, a);
if (show(opt, 3)) {
output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
@ -1397,6 +1431,8 @@ static int merge_mode_and_contents(struct merge_options *opt,
&o->oid,
&a->oid,
&b->oid);
if (result->clean < 0)
return -1;
} else if (S_ISLNK(a->mode)) {
switch (opt->recursive_variant) {
case MERGE_VARIANT_NORMAL:

View File

@ -794,12 +794,16 @@ static void post_assign_shallow(struct shallow_info *info,
if (!*bitmap)
continue;
for (j = 0; j < bitmap_nr; j++)
if (bitmap[0][j] &&
/* Step 7, reachability test at commit level */
!repo_in_merge_bases_many(the_repository, c, ca.nr, ca.commits, 1)) {
update_refstatus(ref_status, info->ref->nr, *bitmap);
dst++;
break;
if (bitmap[0][j]) {
/* Step 7, reachability test at commit level */
int ret = repo_in_merge_bases_many(the_repository, c, ca.nr, ca.commits, 1);
if (ret < 0)
exit(128);
if (!ret) {
update_refstatus(ref_status, info->ref->nr, *bitmap);
dst++;
break;
}
}
}
info->nr_ours = dst;
@ -829,6 +833,8 @@ int delayed_reachability_test(struct shallow_info *si, int c)
si->nr_commits,
si->commits,
1);
if (si->reachable[c] < 0)
exit(128);
si->need_reachability_test[c] = 0;
}
return si->reachable[c];