Merge branch 'js/merge-base-with-missing-commit'

Make sure failure return from merge_bases_many() is properly caught.

* js/merge-base-with-missing-commit:
  merge-ort/merge-recursive: do report errors in `merge_submodule()`
  merge-recursive: prepare for `merge_submodule()` to report errors
  commit-reach(repo_get_merge_bases_many_dirty): pass on errors
  commit-reach(repo_get_merge_bases_many): pass on "missing commits" errors
  commit-reach(get_octopus_merge_bases): pass on "missing commits" errors
  commit-reach(repo_get_merge_bases): pass on "missing commits" errors
  commit-reach(get_merge_bases_many_0): pass on "missing commits" errors
  commit-reach(merge_bases_many): pass on "missing commits" errors
  commit-reach(paint_down_to_common): start reporting errors
  commit-reach(paint_down_to_common): prepare for handling shallow commits
  commit-reach(repo_in_merge_bases_many): report missing commits
  commit-reach(repo_in_merge_bases_many): optionally expect missing commits
  commit-reach(paint_down_to_common): plug two memory leaks
This commit is contained in:
Junio C Hamano
2024-03-11 14:12:30 -07:00
29 changed files with 460 additions and 188 deletions

View File

@@ -836,10 +836,11 @@ static void handle_skipped_merge_base(const struct object_id *mb)
static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev, int no_checkout) static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev, int no_checkout)
{ {
enum bisect_error res = BISECT_OK; enum bisect_error res = BISECT_OK;
struct commit_list *result; struct commit_list *result = NULL;
result = repo_get_merge_bases_many(the_repository, rev[0], rev_nr - 1, if (repo_get_merge_bases_many(the_repository, rev[0], rev_nr - 1,
rev + 1); rev + 1, &result) < 0)
exit(128);
for (; result; result = result->next) { for (; result; result = result->next) {
const struct object_id *mb = &result->item->object.oid; const struct object_id *mb = &result->item->object.oid;

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, merged = reference_rev ? repo_in_merge_bases(the_repository, rev,
reference_rev) : 0; reference_rev) : 0;
if (merged < 0)
exit(128);
/* /*
* After the safety valve is fully redefined to "check with * 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, * any of the following code, but during the transition period,
* a gentle reminder is in order. * a gentle reminder is in order.
*/ */
if ((head_rev != reference_rev) && if (head_rev != reference_rev) {
(head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0) != merged) { int expect = head_rev ? repo_in_merge_bases(the_repository, rev, head_rev) : 0;
if (merged) if (expect < 0)
exit(128);
if (expect == merged)
; /* okay */
else if (merged)
warning(_("deleting branch '%s' that has been merged to\n" warning(_("deleting branch '%s' that has been merged to\n"
" '%s', but not yet merged to HEAD"), " '%s', but not yet merged to HEAD"),
name, reference_name); name, reference_name);

View File

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

View File

@@ -981,6 +981,8 @@ static int update_local_ref(struct ref *ref,
uint64_t t_before = getnanotime(); uint64_t t_before = getnanotime();
fast_forward = repo_in_merge_bases(the_repository, current, fast_forward = repo_in_merge_bases(the_repository, current,
updated); updated);
if (fast_forward < 0)
exit(128);
forced_updates_ms += (getnanotime() - t_before) / 1000000; forced_updates_ms += (getnanotime() - t_before) / 1000000;
} else { } else {
fast_forward = 1; 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 *base = NULL;
struct commit **rev; 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) { switch (auto_base) {
case AUTO_BASE_NEVER: case AUTO_BASE_NEVER:
@@ -1658,7 +1658,7 @@ static struct commit *get_base_commit(const char *base_commit,
struct branch *curr_branch = branch_get(NULL); struct branch *curr_branch = branch_get(NULL);
const char *upstream = branch_get_upstream(curr_branch, NULL); const char *upstream = branch_get_upstream(curr_branch, NULL);
if (upstream) { if (upstream) {
struct commit_list *base_list; struct commit_list *base_list = NULL;
struct commit *commit; struct commit *commit;
struct object_id oid; struct object_id oid;
@@ -1669,11 +1669,12 @@ static struct commit *get_base_commit(const char *base_commit,
return NULL; return NULL;
} }
commit = lookup_commit_or_die(&oid, "upstream base"); commit = lookup_commit_or_die(&oid, "upstream base");
base_list = repo_get_merge_bases_many(the_repository, if (repo_get_merge_bases_many(the_repository,
commit, total, commit, total,
list); list,
/* There should be one and only one merge base. */ &base_list) < 0 ||
if (!base_list || base_list->next) { /* There should be one and only one merge base. */
!base_list || base_list->next) {
if (die_on_failure) { if (die_on_failure) {
die(_("could not find exact merge base")); die(_("could not find exact merge base"));
} else { } else {
@@ -1704,11 +1705,11 @@ static struct commit *get_base_commit(const char *base_commit,
*/ */
while (rev_nr > 1) { while (rev_nr > 1) {
for (i = 0; i < rev_nr / 2; i++) { for (i = 0; i < rev_nr / 2; i++) {
struct commit_list *merge_base; struct commit_list *merge_base = NULL;
merge_base = repo_get_merge_bases(the_repository, if (repo_get_merge_bases(the_repository,
rev[2 * i], rev[2 * i],
rev[2 * i + 1]); rev[2 * i + 1], &merge_base) < 0 ||
if (!merge_base || merge_base->next) { !merge_base || merge_base->next) {
if (die_on_failure) { if (die_on_failure) {
die(_("failed to find exact merge base")); die(_("failed to find exact merge base"));
} else { } else {
@@ -1725,7 +1726,10 @@ static struct commit *get_base_commit(const char *base_commit,
rev_nr = DIV_ROUND_UP(rev_nr, 2); 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) { if (die_on_failure) {
die(_("base commit should be the ancestor of revision list")); die(_("base commit should be the ancestor of revision list"));
} else { } else {

View File

@@ -10,10 +10,13 @@
static int show_merge_base(struct commit **rev, int rev_nr, int show_all) static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
{ {
struct commit_list *result, *r; struct commit_list *result = NULL, *r;
result = repo_get_merge_bases_many_dirty(the_repository, rev[0], if (repo_get_merge_bases_many_dirty(the_repository, rev[0],
rev_nr - 1, rev + 1); rev_nr - 1, rev + 1, &result) < 0) {
free_commit_list(result);
return -1;
}
if (!result) if (!result)
return 1; return 1;
@@ -74,13 +77,17 @@ static int handle_independent(int count, const char **args)
static int handle_octopus(int count, const char **args, int show_all) static int handle_octopus(int count, const char **args, int show_all)
{ {
struct commit_list *revs = NULL; struct commit_list *revs = NULL;
struct commit_list *result, *rev; struct commit_list *result = NULL, *rev;
int i; int i;
for (i = count - 1; i >= 0; i--) for (i = count - 1; i >= 0; i--)
commit_list_insert(get_commit_reference(args[i]), &revs); commit_list_insert(get_commit_reference(args[i]), &revs);
result = get_octopus_merge_bases(revs); if (get_octopus_merge_bases(revs, &result) < 0) {
free_commit_list(revs);
free_commit_list(result);
return 128;
}
free_commit_list(revs); free_commit_list(revs);
reduce_heads_replace(&result); reduce_heads_replace(&result);
@@ -100,12 +107,16 @@ static int handle_octopus(int count, const char **args, int show_all)
static int handle_is_ancestor(int argc, const char **argv) static int handle_is_ancestor(int argc, const char **argv)
{ {
struct commit *one, *two; struct commit *one, *two;
int ret;
if (argc != 2) if (argc != 2)
die("--is-ancestor takes exactly two commits"); die("--is-ancestor takes exactly two commits");
one = get_commit_reference(argv[0]); one = get_commit_reference(argv[0]);
two = get_commit_reference(argv[1]); 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; return 0;
else else
return 1; return 1;

View File

@@ -476,8 +476,9 @@ static int real_merge(struct merge_tree_options *o,
* Get the merge bases, in reverse order; see comment above * Get the merge bases, in reverse order; see comment above
* merge_incore_recursive in merge-ort.h * merge_incore_recursive in merge-ort.h
*/ */
merge_bases = repo_get_merge_bases(the_repository, parent1, if (repo_get_merge_bases(the_repository, parent1,
parent2); parent2, &merge_bases) < 0)
exit(128);
if (!merge_bases && !o->allow_unrelated_histories) if (!merge_bases && !o->allow_unrelated_histories)
die(_("refusing to merge unrelated histories")); die(_("refusing to merge unrelated histories"));
merge_bases = reverse_commit_list(merge_bases); merge_bases = reverse_commit_list(merge_bases);

View File

@@ -1513,13 +1513,20 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (!remoteheads) if (!remoteheads)
; /* already up-to-date */ ; /* already up-to-date */
else if (!remoteheads->next) else if (!remoteheads->next) {
common = repo_get_merge_bases(the_repository, head_commit, if (repo_get_merge_bases(the_repository, head_commit,
remoteheads->item); remoteheads->item, &common) < 0) {
else { ret = 2;
goto done;
}
} else {
struct commit_list *list = remoteheads; struct commit_list *list = remoteheads;
commit_list_insert(head_commit, &list); commit_list_insert(head_commit, &list);
common = get_octopus_merge_bases(list); if (get_octopus_merge_bases(list, &common) < 0) {
free(list);
ret = 2;
goto done;
}
free(list); free(list);
} }
@@ -1626,7 +1633,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
struct commit_list *j; struct commit_list *j;
for (j = remoteheads; j; j = j->next) { for (j = remoteheads; j; j = j->next) {
struct commit_list *common_one; struct commit_list *common_one = NULL;
struct commit *common_item; struct commit *common_item;
/* /*
@@ -1634,9 +1641,10 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* merge_bases again, otherwise "git merge HEAD^ * merge_bases again, otherwise "git merge HEAD^
* HEAD^^" would be missed. * HEAD^^" would be missed.
*/ */
common_one = repo_get_merge_bases(the_repository, if (repo_get_merge_bases(the_repository, head_commit,
head_commit, j->item, &common_one) < 0)
j->item); exit(128);
common_item = common_one->item; common_item = common_one->item;
free_commit_list(common_one); free_commit_list(common_one);
if (!oideq(&common_item->object.oid, &j->item->object.oid)) { if (!oideq(&common_item->object.oid, &j->item->object.oid)) {

View File

@@ -815,7 +815,7 @@ static int get_octopus_merge_base(struct object_id *merge_base,
const struct object_id *merge_head, const struct object_id *merge_head,
const struct object_id *fork_point) const struct object_id *fork_point)
{ {
struct commit_list *revs = NULL, *result; struct commit_list *revs = NULL, *result = NULL;
commit_list_insert(lookup_commit_reference(the_repository, curr_head), commit_list_insert(lookup_commit_reference(the_repository, curr_head),
&revs); &revs);
@@ -825,7 +825,8 @@ static int get_octopus_merge_base(struct object_id *merge_base,
commit_list_insert(lookup_commit_reference(the_repository, fork_point), commit_list_insert(lookup_commit_reference(the_repository, fork_point),
&revs); &revs);
result = get_octopus_merge_bases(revs); if (get_octopus_merge_bases(revs, &result) < 0)
exit(128);
free_commit_list(revs); free_commit_list(revs);
reduce_heads_replace(&result); reduce_heads_replace(&result);
@@ -926,6 +927,8 @@ static int get_can_ff(struct object_id *orig_head,
merge_head = lookup_commit_reference(the_repository, orig_merge_head); merge_head = lookup_commit_reference(the_repository, orig_merge_head);
ret = repo_is_descendant_of(the_repository, merge_head, list); ret = repo_is_descendant_of(the_repository, merge_head, list);
free_commit_list(list); free_commit_list(list);
if (ret < 0)
exit(128);
return ret; return ret;
} }
@@ -950,6 +953,8 @@ static int already_up_to_date(struct object_id *orig_head,
commit_list_insert(theirs, &list); commit_list_insert(theirs, &list);
ok = repo_is_descendant_of(the_repository, ours, list); ok = repo_is_descendant_of(the_repository, ours, list);
free_commit_list(list); free_commit_list(list);
if (ok < 0)
exit(128);
if (!ok) if (!ok)
return 0; return 0;
} }

View File

@@ -867,7 +867,8 @@ static int can_fast_forward(struct commit *onto, struct commit *upstream,
if (!upstream) if (!upstream)
goto done; goto done;
merge_bases = repo_get_merge_bases(the_repository, upstream, head); if (repo_get_merge_bases(the_repository, upstream, head, &merge_bases) < 0)
exit(128);
if (!merge_bases || merge_bases->next) if (!merge_bases || merge_bases->next)
goto done; goto done;
@@ -886,8 +887,9 @@ static void fill_branch_base(struct rebase_options *options,
{ {
struct commit_list *merge_bases = NULL; struct commit_list *merge_bases = NULL;
merge_bases = repo_get_merge_bases(the_repository, options->onto, if (repo_get_merge_bases(the_repository, options->onto,
options->orig_head); options->orig_head, &merge_bases) < 0)
exit(128);
if (!merge_bases || merge_bases->next) if (!merge_bases || merge_bases->next)
oidcpy(branch_base, null_oid()); oidcpy(branch_base, null_oid());
else else

View File

@@ -1526,6 +1526,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
starts_with(name, "refs/heads/")) { starts_with(name, "refs/heads/")) {
struct object *old_object, *new_object; struct object *old_object, *new_object;
struct commit *old_commit, *new_commit; struct commit *old_commit, *new_commit;
int ret2;
old_object = parse_object(the_repository, old_oid); old_object = parse_object(the_repository, old_oid);
new_object = parse_object(the_repository, new_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; old_commit = (struct commit *)old_object;
new_commit = (struct commit *)new_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" rp_error("denying non-fast-forward %s"
" (you should pull first)", name); " (you should pull first)", name);
ret = "non-fast-forward"; ret = "non-fast-forward";

View File

@@ -297,7 +297,7 @@ static int try_difference(const char *arg)
show_rev(NORMAL, &end_oid, end); show_rev(NORMAL, &end_oid, end);
show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start); show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start);
if (symmetric) { if (symmetric) {
struct commit_list *exclude; struct commit_list *exclude = NULL;
struct commit *a, *b; struct commit *a, *b;
a = lookup_commit_reference(the_repository, &start_oid); a = lookup_commit_reference(the_repository, &start_oid);
b = lookup_commit_reference(the_repository, &end_oid); b = lookup_commit_reference(the_repository, &end_oid);
@@ -305,7 +305,8 @@ static int try_difference(const char *arg)
*dotdot = '.'; *dotdot = '.';
return 0; return 0;
} }
exclude = repo_get_merge_bases(the_repository, a, b); if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0)
exit(128);
while (exclude) { while (exclude) {
struct commit *commit = pop_commit(&exclude); struct commit *commit = pop_commit(&exclude);
show_rev(REVERSED, &commit->object.oid, NULL); show_rev(REVERSED, &commit->object.oid, NULL);

View File

@@ -49,13 +49,14 @@ static int queue_has_nonstale(struct prio_queue *queue)
} }
/* all input commits in one and twos[] must have been parsed! */ /* all input commits in one and twos[] must have been parsed! */
static struct commit_list *paint_down_to_common(struct repository *r, static int paint_down_to_common(struct repository *r,
struct commit *one, int n, struct commit *one, int n,
struct commit **twos, struct commit **twos,
timestamp_t min_generation) timestamp_t min_generation,
int ignore_missing_commits,
struct commit_list **result)
{ {
struct prio_queue queue = { compare_commits_by_gen_then_commit_date }; struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
struct commit_list *result = NULL;
int i; int i;
timestamp_t last_gen = GENERATION_NUMBER_INFINITY; timestamp_t last_gen = GENERATION_NUMBER_INFINITY;
@@ -64,8 +65,8 @@ static struct commit_list *paint_down_to_common(struct repository *r,
one->object.flags |= PARENT1; one->object.flags |= PARENT1;
if (!n) { if (!n) {
commit_list_append(one, &result); commit_list_append(one, result);
return result; return 0;
} }
prio_queue_put(&queue, one); prio_queue_put(&queue, one);
@@ -93,7 +94,7 @@ static struct commit_list *paint_down_to_common(struct repository *r,
if (flags == (PARENT1 | PARENT2)) { if (flags == (PARENT1 | PARENT2)) {
if (!(commit->object.flags & RESULT)) { if (!(commit->object.flags & RESULT)) {
commit->object.flags |= RESULT; commit->object.flags |= RESULT;
commit_list_insert_by_date(commit, &result); commit_list_insert_by_date(commit, result);
} }
/* Mark parents of a found merge stale */ /* Mark parents of a found merge stale */
flags |= STALE; flags |= STALE;
@@ -104,67 +105,97 @@ static struct commit_list *paint_down_to_common(struct repository *r,
parents = parents->next; parents = parents->next;
if ((p->object.flags & flags) == flags) if ((p->object.flags & flags) == flags)
continue; continue;
if (repo_parse_commit(r, p)) if (repo_parse_commit(r, p)) {
return NULL; clear_prio_queue(&queue);
free_commit_list(*result);
*result = NULL;
/*
* At this stage, we know that the commit is
* missing: `repo_parse_commit()` uses
* `OBJECT_INFO_DIE_IF_CORRUPT` and therefore
* corrupt commits would already have been
* dispatched with a `die()`.
*/
if (ignore_missing_commits)
return 0;
return error(_("could not parse commit %s"),
oid_to_hex(&p->object.oid));
}
p->object.flags |= flags; p->object.flags |= flags;
prio_queue_put(&queue, p); prio_queue_put(&queue, p);
} }
} }
clear_prio_queue(&queue); clear_prio_queue(&queue);
return result; return 0;
} }
static struct commit_list *merge_bases_many(struct repository *r, static int merge_bases_many(struct repository *r,
struct commit *one, int n, struct commit *one, int n,
struct commit **twos) struct commit **twos,
struct commit_list **result)
{ {
struct commit_list *list = NULL; struct commit_list *list = NULL;
struct commit_list *result = NULL;
int i; int i;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
if (one == twos[i]) if (one == twos[i]) {
/* /*
* We do not mark this even with RESULT so we do not * We do not mark this even with RESULT so we do not
* have to clean it up. * have to clean it up.
*/ */
return commit_list_insert(one, &result); *result = commit_list_insert(one, result);
return 0;
}
} }
if (!one)
return 0;
if (repo_parse_commit(r, one)) if (repo_parse_commit(r, one))
return NULL; return error(_("could not parse commit %s"),
oid_to_hex(&one->object.oid));
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
if (!twos[i])
return 0;
if (repo_parse_commit(r, twos[i])) if (repo_parse_commit(r, twos[i]))
return NULL; return error(_("could not parse commit %s"),
oid_to_hex(&twos[i]->object.oid));
} }
list = paint_down_to_common(r, one, n, twos, 0); if (paint_down_to_common(r, one, n, twos, 0, 0, &list)) {
free_commit_list(list);
return -1;
}
while (list) { while (list) {
struct commit *commit = pop_commit(&list); struct commit *commit = pop_commit(&list);
if (!(commit->object.flags & STALE)) if (!(commit->object.flags & STALE))
commit_list_insert_by_date(commit, &result); commit_list_insert_by_date(commit, result);
} }
return result; return 0;
} }
struct commit_list *get_octopus_merge_bases(struct commit_list *in) int get_octopus_merge_bases(struct commit_list *in, struct commit_list **result)
{ {
struct commit_list *i, *j, *k, *ret = NULL; struct commit_list *i, *j, *k;
if (!in) if (!in)
return ret; return 0;
commit_list_insert(in->item, &ret); commit_list_insert(in->item, result);
for (i = in->next; i; i = i->next) { for (i = in->next; i; i = i->next) {
struct commit_list *new_commits = NULL, *end = NULL; struct commit_list *new_commits = NULL, *end = NULL;
for (j = ret; j; j = j->next) { for (j = *result; j; j = j->next) {
struct commit_list *bases; struct commit_list *bases = NULL;
bases = repo_get_merge_bases(the_repository, i->item, if (repo_get_merge_bases(the_repository, i->item,
j->item); j->item, &bases) < 0) {
free_commit_list(bases);
free_commit_list(*result);
*result = NULL;
return -1;
}
if (!new_commits) if (!new_commits)
new_commits = bases; new_commits = bases;
else else
@@ -172,10 +203,10 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)
for (k = bases; k; k = k->next) for (k = bases; k; k = k->next)
end = k; end = k;
} }
free_commit_list(ret); free_commit_list(*result);
ret = new_commits; *result = new_commits;
} }
return ret; return 0;
} }
static int remove_redundant_no_gen(struct repository *r, static int remove_redundant_no_gen(struct repository *r,
@@ -193,7 +224,7 @@ static int remove_redundant_no_gen(struct repository *r,
for (i = 0; i < cnt; i++) for (i = 0; i < cnt; i++)
repo_parse_commit(r, array[i]); repo_parse_commit(r, array[i]);
for (i = 0; i < cnt; i++) { for (i = 0; i < cnt; i++) {
struct commit_list *common; struct commit_list *common = NULL;
timestamp_t min_generation = commit_graph_generation(array[i]); timestamp_t min_generation = commit_graph_generation(array[i]);
if (redundant[i]) if (redundant[i])
@@ -209,8 +240,16 @@ static int remove_redundant_no_gen(struct repository *r,
if (curr_generation < min_generation) if (curr_generation < min_generation)
min_generation = curr_generation; min_generation = curr_generation;
} }
common = paint_down_to_common(r, array[i], filled, if (paint_down_to_common(r, array[i], filled,
work, min_generation); work, min_generation, 0, &common)) {
clear_commit_marks(array[i], all_flags);
clear_commit_marks_many(filled, work, all_flags);
free_commit_list(common);
free(work);
free(redundant);
free(filled_index);
return -1;
}
if (array[i]->object.flags & PARENT2) if (array[i]->object.flags & PARENT2)
redundant[i] = 1; redundant[i] = 1;
for (j = 0; j < filled; j++) for (j = 0; j < filled; j++)
@@ -375,69 +414,77 @@ static int remove_redundant(struct repository *r, struct commit **array, int cnt
return remove_redundant_no_gen(r, array, cnt); return remove_redundant_no_gen(r, array, cnt);
} }
static struct commit_list *get_merge_bases_many_0(struct repository *r, static int get_merge_bases_many_0(struct repository *r,
struct commit *one, struct commit *one,
int n, int n,
struct commit **twos, struct commit **twos,
int cleanup) int cleanup,
struct commit_list **result)
{ {
struct commit_list *list; struct commit_list *list;
struct commit **rslt; struct commit **rslt;
struct commit_list *result;
int cnt, i; int cnt, i;
result = merge_bases_many(r, one, n, twos); if (merge_bases_many(r, one, n, twos, result) < 0)
return -1;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
if (one == twos[i]) if (one == twos[i])
return result; return 0;
} }
if (!result || !result->next) { if (!*result || !(*result)->next) {
if (cleanup) { if (cleanup) {
clear_commit_marks(one, all_flags); clear_commit_marks(one, all_flags);
clear_commit_marks_many(n, twos, all_flags); clear_commit_marks_many(n, twos, all_flags);
} }
return result; return 0;
} }
/* There are more than one */ /* There are more than one */
cnt = commit_list_count(result); cnt = commit_list_count(*result);
CALLOC_ARRAY(rslt, cnt); CALLOC_ARRAY(rslt, cnt);
for (list = result, i = 0; list; list = list->next) for (list = *result, i = 0; list; list = list->next)
rslt[i++] = list->item; rslt[i++] = list->item;
free_commit_list(result); free_commit_list(*result);
*result = NULL;
clear_commit_marks(one, all_flags); clear_commit_marks(one, all_flags);
clear_commit_marks_many(n, twos, all_flags); clear_commit_marks_many(n, twos, all_flags);
cnt = remove_redundant(r, rslt, cnt); cnt = remove_redundant(r, rslt, cnt);
result = NULL; if (cnt < 0) {
free(rslt);
return -1;
}
for (i = 0; i < cnt; i++) for (i = 0; i < cnt; i++)
commit_list_insert_by_date(rslt[i], &result); commit_list_insert_by_date(rslt[i], result);
free(rslt); free(rslt);
return result; return 0;
} }
struct commit_list *repo_get_merge_bases_many(struct repository *r, int repo_get_merge_bases_many(struct repository *r,
struct commit *one, struct commit *one,
int n, int n,
struct commit **twos) struct commit **twos,
struct commit_list **result)
{ {
return get_merge_bases_many_0(r, one, n, twos, 1); return get_merge_bases_many_0(r, one, n, twos, 1, result);
} }
struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r, int repo_get_merge_bases_many_dirty(struct repository *r,
struct commit *one, struct commit *one,
int n, int n,
struct commit **twos) struct commit **twos,
struct commit_list **result)
{ {
return get_merge_bases_many_0(r, one, n, twos, 0); return get_merge_bases_many_0(r, one, n, twos, 0, result);
} }
struct commit_list *repo_get_merge_bases(struct repository *r, int repo_get_merge_bases(struct repository *r,
struct commit *one, struct commit *one,
struct commit *two) struct commit *two,
struct commit_list **result)
{ {
return get_merge_bases_many_0(r, one, 1, &two, 1); return get_merge_bases_many_0(r, one, 1, &two, 1, result);
} }
/* /*
@@ -460,11 +507,13 @@ int repo_is_descendant_of(struct repository *r,
} else { } else {
while (with_commit) { while (with_commit) {
struct commit *other; struct commit *other;
int ret;
other = with_commit->item; other = with_commit->item;
with_commit = with_commit->next; with_commit = with_commit->next;
if (repo_in_merge_bases_many(r, other, 1, &commit)) ret = repo_in_merge_bases_many(r, other, 1, &commit, 0);
return 1; if (ret)
return ret;
} }
return 0; return 0;
} }
@@ -474,17 +523,18 @@ int repo_is_descendant_of(struct repository *r,
* Is "commit" an ancestor of one of the "references"? * Is "commit" an ancestor of one of the "references"?
*/ */
int repo_in_merge_bases_many(struct repository *r, struct commit *commit, int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
int nr_reference, struct commit **reference) int nr_reference, struct commit **reference,
int ignore_missing_commits)
{ {
struct commit_list *bases; struct commit_list *bases = NULL;
int ret = 0, i; int ret = 0, i;
timestamp_t generation, max_generation = GENERATION_NUMBER_ZERO; timestamp_t generation, max_generation = GENERATION_NUMBER_ZERO;
if (repo_parse_commit(r, commit)) if (repo_parse_commit(r, commit))
return ret; return ignore_missing_commits ? 0 : -1;
for (i = 0; i < nr_reference; i++) { for (i = 0; i < nr_reference; i++) {
if (repo_parse_commit(r, reference[i])) if (repo_parse_commit(r, reference[i]))
return ret; return ignore_missing_commits ? 0 : -1;
generation = commit_graph_generation(reference[i]); generation = commit_graph_generation(reference[i]);
if (generation > max_generation) if (generation > max_generation)
@@ -495,10 +545,11 @@ int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
if (generation > max_generation) if (generation > max_generation)
return ret; return ret;
bases = paint_down_to_common(r, commit, if (paint_down_to_common(r, commit,
nr_reference, reference, nr_reference, reference,
generation); generation, ignore_missing_commits, &bases))
if (commit->object.flags & PARENT2) ret = -1;
else if (commit->object.flags & PARENT2)
ret = 1; ret = 1;
clear_commit_marks(commit, all_flags); clear_commit_marks(commit, all_flags);
clear_commit_marks_many(nr_reference, reference, all_flags); clear_commit_marks_many(nr_reference, reference, all_flags);
@@ -551,6 +602,10 @@ struct commit_list *reduce_heads(struct commit_list *heads)
} }
} }
num_head = remove_redundant(the_repository, array, num_head); num_head = remove_redundant(the_repository, array, num_head);
if (num_head < 0) {
free(array);
return NULL;
}
for (i = 0; i < num_head; i++) for (i = 0; i < num_head; i++)
tail = &commit_list_insert(array[i], tail)->next; tail = &commit_list_insert(array[i], tail)->next;
free(array); free(array);
@@ -593,6 +648,8 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid)
commit_list_insert(old_commit, &old_commit_list); commit_list_insert(old_commit, &old_commit_list);
ret = repo_is_descendant_of(the_repository, ret = repo_is_descendant_of(the_repository,
new_commit, old_commit_list); new_commit, old_commit_list);
if (ret < 0)
exit(128);
free_commit_list(old_commit_list); free_commit_list(old_commit_list);
return ret; return ret;
} }

View File

@@ -9,18 +9,21 @@ struct ref_filter;
struct object_id; struct object_id;
struct object_array; struct object_array;
struct commit_list *repo_get_merge_bases(struct repository *r, int repo_get_merge_bases(struct repository *r,
struct commit *rev1, struct commit *rev1,
struct commit *rev2); struct commit *rev2,
struct commit_list *repo_get_merge_bases_many(struct repository *r, struct commit_list **result);
struct commit *one, int n, int repo_get_merge_bases_many(struct repository *r,
struct commit **twos); struct commit *one, int n,
struct commit **twos,
struct commit_list **result);
/* To be used only when object flags after this call no longer matter */ /* To be used only when object flags after this call no longer matter */
struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r, int repo_get_merge_bases_many_dirty(struct repository *r,
struct commit *one, int n, struct commit *one, int n,
struct commit **twos); struct commit **twos,
struct commit_list **result);
struct commit_list *get_octopus_merge_bases(struct commit_list *in); int get_octopus_merge_bases(struct commit_list *in, struct commit_list **result);
int repo_is_descendant_of(struct repository *r, int repo_is_descendant_of(struct repository *r,
struct commit *commit, struct commit *commit,
@@ -30,7 +33,8 @@ int repo_in_merge_bases(struct repository *r,
struct commit *reference); struct commit *reference);
int repo_in_merge_bases_many(struct repository *r, int repo_in_merge_bases_many(struct repository *r,
struct commit *commit, struct commit *commit,
int nr_reference, struct commit **reference); int nr_reference, struct commit **reference,
int ignore_missing_commits);
/* /*
* Takes a list of commits and returns a new list where those * Takes a list of commits and returns a new list where those

View File

@@ -1052,7 +1052,7 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
{ {
struct object_id oid; struct object_id oid;
struct rev_collect revs; struct rev_collect revs;
struct commit_list *bases; struct commit_list *bases = NULL;
int i; int i;
struct commit *ret = NULL; struct commit *ret = NULL;
char *full_refname; char *full_refname;
@@ -1077,8 +1077,9 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
for (i = 0; i < revs.nr; i++) for (i = 0; i < revs.nr; i++)
revs.commit[i]->object.flags &= ~TMP_MARK; revs.commit[i]->object.flags &= ~TMP_MARK;
bases = repo_get_merge_bases_many(the_repository, commit, revs.nr, if (repo_get_merge_bases_many(the_repository, commit, revs.nr,
revs.commit); revs.commit, &bases) < 0)
exit(128);
/* /*
* There should be one and only one merge base, when we found * There should be one and only one merge base, when we found

View File

@@ -570,7 +570,7 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb)
{ {
int i; int i;
struct commit *mb_child[2] = {0}; struct commit *mb_child[2] = {0};
struct commit_list *merge_bases; struct commit_list *merge_bases = NULL;
for (i = 0; i < revs->pending.nr; i++) { for (i = 0; i < revs->pending.nr; i++) {
struct object *obj = revs->pending.objects[i].item; struct object *obj = revs->pending.objects[i].item;
@@ -597,7 +597,8 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb)
mb_child[1] = lookup_commit_reference(the_repository, &oid); mb_child[1] = lookup_commit_reference(the_repository, &oid);
} }
merge_bases = repo_get_merge_bases(the_repository, mb_child[0], mb_child[1]); if (repo_get_merge_bases(the_repository, mb_child[0], mb_child[1], &merge_bases) < 0)
exit(128);
if (!merge_bases) if (!merge_bases)
die(_("no merge base found")); die(_("no merge base found"));
if (merge_bases->next) if (merge_bases->next)

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 *head = lookup_commit_or_die(head_oid, "HEAD");
struct commit *branch = lookup_commit_or_die(&remote->old_oid, struct commit *branch = lookup_commit_or_die(&remote->old_oid,
remote->name); 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) static int delete_remote_branch(const char *pattern, int force)

View File

@@ -1011,7 +1011,7 @@ static int do_remerge_diff(struct rev_info *opt,
struct object_id *oid) struct object_id *oid)
{ {
struct merge_options o; struct merge_options o;
struct commit_list *bases; struct commit_list *bases = NULL;
struct merge_result res = {0}; struct merge_result res = {0};
struct pretty_print_context ctx = {0}; struct pretty_print_context ctx = {0};
struct commit *parent1 = parents->item; struct commit *parent1 = parents->item;
@@ -1036,7 +1036,8 @@ static int do_remerge_diff(struct rev_info *opt,
/* Parse the relevant commits and get the merge bases */ /* Parse the relevant commits and get the merge bases */
parse_commit_or_die(parent1); parse_commit_or_die(parent1);
parse_commit_or_die(parent2); parse_commit_or_die(parent2);
bases = repo_get_merge_bases(the_repository, parent1, parent2); if (repo_get_merge_bases(the_repository, parent1, parent2, &bases) < 0)
exit(128);
/* Re-merge the parents */ /* Re-merge the parents */
merge_incore_recursive(&o, bases, parent1, parent2, &res); merge_incore_recursive(&o, bases, parent1, parent2, &res);

View File

@@ -543,6 +543,7 @@ enum conflict_and_info_types {
CONFLICT_SUBMODULE_HISTORY_NOT_AVAILABLE, CONFLICT_SUBMODULE_HISTORY_NOT_AVAILABLE,
CONFLICT_SUBMODULE_MAY_HAVE_REWINDS, CONFLICT_SUBMODULE_MAY_HAVE_REWINDS,
CONFLICT_SUBMODULE_NULL_MERGE_BASE, CONFLICT_SUBMODULE_NULL_MERGE_BASE,
CONFLICT_SUBMODULE_CORRUPT,
/* Keep this entry _last_ in the list */ /* Keep this entry _last_ in the list */
NB_CONFLICT_TYPES, NB_CONFLICT_TYPES,
@@ -595,7 +596,9 @@ static const char *type_short_descriptions[] = {
[CONFLICT_SUBMODULE_MAY_HAVE_REWINDS] = [CONFLICT_SUBMODULE_MAY_HAVE_REWINDS] =
"CONFLICT (submodule may have rewinds)", "CONFLICT (submodule may have rewinds)",
[CONFLICT_SUBMODULE_NULL_MERGE_BASE] = [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 { struct logical_conflict_info {
@@ -1710,7 +1713,14 @@ static int find_first_merges(struct repository *repo,
die("revision walk setup failed"); die("revision walk setup failed");
while ((commit = get_revision(&revs)) != NULL) { while ((commit = get_revision(&revs)) != NULL) {
struct object *o = &(commit->object); 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); add_object_array(o, NULL, &merges);
} }
reset_revision_walk(); reset_revision_walk();
@@ -1725,9 +1735,17 @@ static int find_first_merges(struct repository *repo,
contains_another = 0; contains_another = 0;
for (j = 0; j < merges.nr; j++) { for (j = 0; j < merges.nr; j++) {
struct commit *m2 = (struct commit *) merges.objects[j].item; struct commit *m2 = (struct commit *) merges.objects[j].item;
if (i != j && repo_in_merge_bases(repo, m2, m1)) { if (i != j) {
contains_another = 1; int ret = repo_in_merge_bases(repo, m2, m1);
break; if (ret < 0) {
object_array_clear(&merges);
release_revisions(&revs);
return ret;
}
if (ret > 0) {
contains_another = 1;
break;
}
} }
} }
@@ -1749,7 +1767,7 @@ static int merge_submodule(struct merge_options *opt,
{ {
struct repository subrepo; struct repository subrepo;
struct strbuf sb = STRBUF_INIT; struct strbuf sb = STRBUF_INIT;
int ret = 0; int ret = 0, ret2;
struct commit *commit_o, *commit_a, *commit_b; struct commit *commit_o, *commit_a, *commit_b;
int parent_count; int parent_count;
struct object_array merges; struct object_array merges;
@@ -1796,8 +1814,28 @@ static int merge_submodule(struct merge_options *opt,
} }
/* check whether both changes are forward */ /* check whether both changes are forward */
if (!repo_in_merge_bases(&subrepo, commit_o, commit_a) || ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_a);
!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);
ret = -1;
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);
ret = -1;
goto cleanup;
}
if (!ret2) {
path_msg(opt, CONFLICT_SUBMODULE_MAY_HAVE_REWINDS, 0, path_msg(opt, CONFLICT_SUBMODULE_MAY_HAVE_REWINDS, 0,
path, NULL, NULL, NULL, path, NULL, NULL, NULL,
_("Failed to merge submodule %s " _("Failed to merge submodule %s "
@@ -1807,7 +1845,17 @@ static int merge_submodule(struct merge_options *opt,
} }
/* Case #1: a is contained in b or vice versa */ /* 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);
ret = -1;
goto cleanup;
}
if (ret2 > 0) {
oidcpy(result, b); oidcpy(result, b);
path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1, path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1,
path, NULL, NULL, NULL, path, NULL, NULL, NULL,
@@ -1816,7 +1864,17 @@ static int merge_submodule(struct merge_options *opt,
ret = 1; ret = 1;
goto cleanup; 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);
ret = -1;
goto cleanup;
}
if (ret2 > 0) {
oidcpy(result, a); oidcpy(result, a);
path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1, path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1,
path, NULL, NULL, NULL, path, NULL, NULL, NULL,
@@ -1841,6 +1899,14 @@ static int merge_submodule(struct merge_options *opt,
parent_count = find_first_merges(&subrepo, path, commit_a, commit_b, parent_count = find_first_merges(&subrepo, path, commit_a, commit_b,
&merges); &merges);
switch (parent_count) { switch (parent_count) {
case -1:
path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
path, NULL, NULL, NULL,
_("Failed to merge submodule %s "
"(repository corrupt)"),
path);
ret = -1;
break;
case 0: case 0:
path_msg(opt, CONFLICT_SUBMODULE_FAILED_TO_MERGE, 0, path_msg(opt, CONFLICT_SUBMODULE_FAILED_TO_MERGE, 0,
path, NULL, NULL, NULL, path, NULL, NULL, NULL,
@@ -5014,7 +5080,11 @@ static void merge_ort_internal(struct merge_options *opt,
struct strbuf merge_base_abbrev = STRBUF_INIT; struct strbuf merge_base_abbrev = STRBUF_INIT;
if (!merge_bases) { if (!merge_bases) {
merge_bases = repo_get_merge_bases(the_repository, h1, h2); if (repo_get_merge_bases(the_repository, h1, h2,
&merge_bases) < 0) {
result->clean = -1;
return;
}
/* See merge-ort.h:merge_incore_recursive() declaration NOTE */ /* See merge-ort.h:merge_incore_recursive() declaration NOTE */
merge_bases = reverse_commit_list(merge_bases); merge_bases = reverse_commit_list(merge_bases);
} }

View File

@@ -1140,7 +1140,13 @@ static int find_first_merges(struct repository *repo,
die("revision walk setup failed"); die("revision walk setup failed");
while ((commit = get_revision(&revs)) != NULL) { while ((commit = get_revision(&revs)) != NULL) {
struct object *o = &(commit->object); 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); add_object_array(o, NULL, &merges);
} }
reset_revision_walk(); reset_revision_walk();
@@ -1155,9 +1161,17 @@ static int find_first_merges(struct repository *repo,
contains_another = 0; contains_another = 0;
for (j = 0; j < merges.nr; j++) { for (j = 0; j < merges.nr; j++) {
struct commit *m2 = (struct commit *) merges.objects[j].item; struct commit *m2 = (struct commit *) merges.objects[j].item;
if (i != j && repo_in_merge_bases(repo, m2, m1)) { if (i != j) {
contains_another = 1; int ret = repo_in_merge_bases(repo, m2, m1);
break; if (ret < 0) {
object_array_clear(&merges);
release_revisions(&revs);
return ret;
}
if (ret > 0) {
contains_another = 1;
break;
}
} }
} }
@@ -1193,7 +1207,7 @@ static int merge_submodule(struct merge_options *opt,
const struct object_id *b) const struct object_id *b)
{ {
struct repository subrepo; struct repository subrepo;
int ret = 0; int ret = 0, ret2;
struct commit *commit_base, *commit_a, *commit_b; struct commit *commit_base, *commit_a, *commit_b;
int parent_count; int parent_count;
struct object_array merges; struct object_array merges;
@@ -1230,14 +1244,32 @@ static int merge_submodule(struct merge_options *opt,
} }
/* check whether both changes are forward */ /* check whether both changes are forward */
if (!repo_in_merge_bases(&subrepo, commit_base, commit_a) || ret2 = repo_in_merge_bases(&subrepo, commit_base, commit_a);
!repo_in_merge_bases(&subrepo, commit_base, commit_b)) { if (ret2 < 0) {
output(opt, 1, _("Failed to merge submodule %s (repository corrupt)"), path);
ret = -1;
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);
ret = -1;
goto cleanup;
}
if (!ret2) {
output(opt, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path); output(opt, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path);
goto cleanup; goto cleanup;
} }
/* Case #1: a is contained in b or vice versa */ /* 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);
ret = -1;
goto cleanup;
}
if (ret2) {
oidcpy(result, b); oidcpy(result, b);
if (show(opt, 3)) { if (show(opt, 3)) {
output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path); output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
@@ -1250,7 +1282,13 @@ static int merge_submodule(struct merge_options *opt,
ret = 1; ret = 1;
goto cleanup; 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);
ret = -1;
goto cleanup;
}
if (ret2) {
oidcpy(result, a); oidcpy(result, a);
if (show(opt, 3)) { if (show(opt, 3)) {
output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path); output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
@@ -1279,6 +1317,10 @@ static int merge_submodule(struct merge_options *opt,
parent_count = find_first_merges(&subrepo, &merges, path, parent_count = find_first_merges(&subrepo, &merges, path,
commit_a, commit_b); commit_a, commit_b);
switch (parent_count) { switch (parent_count) {
case -1:
output(opt, 1,_("Failed to merge submodule %s (repository corrupt)"), path);
ret = -1;
break;
case 0: case 0:
output(opt, 1, _("Failed to merge submodule %s (merge following commits not found)"), path); output(opt, 1, _("Failed to merge submodule %s (merge following commits not found)"), path);
break; break;
@@ -1393,11 +1435,14 @@ static int merge_mode_and_contents(struct merge_options *opt,
/* FIXME: bug, what if modes didn't match? */ /* FIXME: bug, what if modes didn't match? */
result->clean = (merge_status == 0); result->clean = (merge_status == 0);
} else if (S_ISGITLINK(a->mode)) { } else if (S_ISGITLINK(a->mode)) {
result->clean = merge_submodule(opt, &result->blob.oid, int clean = merge_submodule(opt, &result->blob.oid,
o->path, o->path,
&o->oid, &o->oid,
&a->oid, &a->oid,
&b->oid); &b->oid);
if (clean < 0)
return -1;
result->clean = clean;
} else if (S_ISLNK(a->mode)) { } else if (S_ISLNK(a->mode)) {
switch (opt->recursive_variant) { switch (opt->recursive_variant) {
case MERGE_VARIANT_NORMAL: case MERGE_VARIANT_NORMAL:
@@ -3598,7 +3643,9 @@ static int merge_recursive_internal(struct merge_options *opt,
} }
if (!merge_bases) { if (!merge_bases) {
merge_bases = repo_get_merge_bases(the_repository, h1, h2); if (repo_get_merge_bases(the_repository, h1, h2,
&merge_bases) < 0)
return -1;
merge_bases = reverse_commit_list(merge_bases); merge_bases = reverse_commit_list(merge_bases);
} }

View File

@@ -607,7 +607,8 @@ int notes_merge(struct notes_merge_options *o,
assert(local && remote); assert(local && remote);
/* Find merge bases */ /* Find merge bases */
bases = repo_get_merge_bases(the_repository, local, remote); if (repo_get_merge_bases(the_repository, local, remote, &bases) < 0)
exit(128);
if (!bases) { if (!bases) {
base_oid = null_oid(); base_oid = null_oid();
base_tree_oid = the_hash_algo->empty_tree; base_tree_oid = the_hash_algo->empty_tree;

View File

@@ -1488,7 +1488,7 @@ int repo_get_oid_mb(struct repository *r,
struct object_id *oid) struct object_id *oid)
{ {
struct commit *one, *two; struct commit *one, *two;
struct commit_list *mbs; struct commit_list *mbs = NULL;
struct object_id oid_tmp; struct object_id oid_tmp;
const char *dots; const char *dots;
int st; int st;
@@ -1516,7 +1516,10 @@ int repo_get_oid_mb(struct repository *r,
two = lookup_commit_reference_gently(r, &oid_tmp, 0); two = lookup_commit_reference_gently(r, &oid_tmp, 0);
if (!two) if (!two)
return -1; return -1;
mbs = repo_get_merge_bases(r, one, two); if (repo_get_merge_bases(r, one, two, &mbs) < 0) {
free_commit_list(mbs);
return -1;
}
if (!mbs || mbs->next) if (!mbs || mbs->next)
st = -1; st = -1;
else { else {

View File

@@ -2679,7 +2679,7 @@ static int is_reachable_in_reflog(const char *local, const struct ref *remote)
if (MERGE_BASES_BATCH_SIZE < size) if (MERGE_BASES_BATCH_SIZE < size)
size = MERGE_BASES_BATCH_SIZE; size = MERGE_BASES_BATCH_SIZE;
if ((ret = repo_in_merge_bases_many(the_repository, commit, size, chunk))) if ((ret = repo_in_merge_bases_many(the_repository, commit, size, chunk, 0)))
break; break;
} }

View File

@@ -1992,7 +1992,7 @@ static const char *lookup_other_head(struct object_id *oid)
static void prepare_show_merge(struct rev_info *revs) static void prepare_show_merge(struct rev_info *revs)
{ {
struct commit_list *bases; struct commit_list *bases = NULL;
struct commit *head, *other; struct commit *head, *other;
struct object_id oid; struct object_id oid;
const char *other_name; const char *other_name;
@@ -2007,7 +2007,8 @@ static void prepare_show_merge(struct rev_info *revs)
other = lookup_commit_or_die(&oid, other_name); other = lookup_commit_or_die(&oid, other_name);
add_pending_object(revs, &head->object, "HEAD"); add_pending_object(revs, &head->object, "HEAD");
add_pending_object(revs, &other->object, other_name); add_pending_object(revs, &other->object, other_name);
bases = repo_get_merge_bases(the_repository, head, other); if (repo_get_merge_bases(the_repository, head, other, &bases) < 0)
exit(128);
add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM); add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM);
add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM); add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM);
free_commit_list(bases); free_commit_list(bases);
@@ -2095,14 +2096,17 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
} else { } else {
/* A...B -- find merge bases between the two */ /* A...B -- find merge bases between the two */
struct commit *a, *b; struct commit *a, *b;
struct commit_list *exclude; struct commit_list *exclude = NULL;
a = lookup_commit_reference(revs->repo, &a_obj->oid); a = lookup_commit_reference(revs->repo, &a_obj->oid);
b = lookup_commit_reference(revs->repo, &b_obj->oid); b = lookup_commit_reference(revs->repo, &b_obj->oid);
if (!a || !b) if (!a || !b)
return dotdot_missing(arg, dotdot, revs, symmetric); return dotdot_missing(arg, dotdot, revs, symmetric);
exclude = repo_get_merge_bases(the_repository, a, b); if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0) {
free_commit_list(exclude);
return -1;
}
add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE, add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE,
flags_exclude); flags_exclude);
add_pending_commit_list(revs, exclude, flags_exclude); add_pending_commit_list(revs, exclude, flags_exclude);

View File

@@ -3912,7 +3912,7 @@ static int do_merge(struct repository *r,
int run_commit_flags = 0; int run_commit_flags = 0;
struct strbuf ref_name = STRBUF_INIT; struct strbuf ref_name = STRBUF_INIT;
struct commit *head_commit, *merge_commit, *i; struct commit *head_commit, *merge_commit, *i;
struct commit_list *bases, *j; struct commit_list *bases = NULL, *j;
struct commit_list *to_merge = NULL, **tail = &to_merge; struct commit_list *to_merge = NULL, **tail = &to_merge;
const char *strategy = !opts->xopts.nr && const char *strategy = !opts->xopts.nr &&
(!opts->strategy || (!opts->strategy ||
@@ -4138,7 +4138,11 @@ static int do_merge(struct repository *r,
} }
merge_commit = to_merge->item; merge_commit = to_merge->item;
bases = repo_get_merge_bases(r, head_commit, merge_commit); if (repo_get_merge_bases(r, head_commit, merge_commit, &bases) < 0) {
ret = -1;
goto leave_merge;
}
if (bases && oideq(&merge_commit->object.oid, if (bases && oideq(&merge_commit->object.oid,
&bases->item->object.oid)) { &bases->item->object.oid)) {
ret = 0; ret = 0;

View File

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

View File

@@ -592,7 +592,12 @@ static void show_submodule_header(struct diff_options *o,
(!is_null_oid(two) && !*right)) (!is_null_oid(two) && !*right))
message = "(commits not present)"; message = "(commits not present)";
*merge_bases = repo_get_merge_bases(sub, *left, *right); *merge_bases = NULL;
if (repo_get_merge_bases(sub, *left, *right, merge_bases) < 0) {
message = "(corrupt repository)";
goto output_header;
}
if (*merge_bases) { if (*merge_bases) {
if ((*merge_bases)->item == *left) if ((*merge_bases)->item == *left)
fast_forward = 1; fast_forward = 1;

View File

@@ -111,13 +111,16 @@ int cmd__reach(int ac, const char **av)
repo_in_merge_bases(the_repository, A, B)); repo_in_merge_bases(the_repository, A, B));
else if (!strcmp(av[1], "in_merge_bases_many")) else if (!strcmp(av[1], "in_merge_bases_many"))
printf("%s(A,X):%d\n", av[1], printf("%s(A,X):%d\n", av[1],
repo_in_merge_bases_many(the_repository, A, X_nr, X_array)); repo_in_merge_bases_many(the_repository, A, X_nr, X_array, 0));
else if (!strcmp(av[1], "is_descendant_of")) else if (!strcmp(av[1], "is_descendant_of"))
printf("%s(A,X):%d\n", av[1], repo_is_descendant_of(r, A, X)); printf("%s(A,X):%d\n", av[1], repo_is_descendant_of(r, A, X));
else if (!strcmp(av[1], "get_merge_bases_many")) { else if (!strcmp(av[1], "get_merge_bases_many")) {
struct commit_list *list = repo_get_merge_bases_many(the_repository, struct commit_list *list = NULL;
A, X_nr, if (repo_get_merge_bases_many(the_repository,
X_array); A, X_nr,
X_array,
&list) < 0)
exit(128);
printf("%s(A,X):\n", av[1]); printf("%s(A,X):\n", av[1]);
print_sorted_commit_ids(list); print_sorted_commit_ids(list);
} else if (!strcmp(av[1], "reduce_heads")) { } else if (!strcmp(av[1], "reduce_heads")) {

View File

@@ -978,4 +978,16 @@ test_expect_success 'error out on missing blob objects' '
test_must_be_empty actual test_must_be_empty actual
' '
test_expect_success 'error out on missing commits as well' '
git init --bare missing-commit.git &&
git rev-list --objects side1 side3 >list-including-initial &&
grep -v ^$(git rev-parse side1^) <list-including-initial >list &&
git pack-objects missing-commit.git/objects/pack/missing-initial <list &&
side1=$(git rev-parse side1) &&
side3=$(git rev-parse side3) &&
test_must_fail git --git-dir=missing-commit.git \
merge-tree --allow-unrelated-histories $side1 $side3 >actual &&
test_must_be_empty actual
'
test_done test_done