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
commit 7745f92507
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)
{
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,
rev + 1);
if (repo_get_merge_bases_many(the_repository, rev[0], rev_nr - 1,
rev + 1, &result) < 0)
exit(128);
for (; result; result = result->next) {
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,
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

@ -981,6 +981,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:
@ -1658,7 +1658,7 @@ static struct commit *get_base_commit(const char *base_commit,
struct branch *curr_branch = branch_get(NULL);
const char *upstream = branch_get_upstream(curr_branch, NULL);
if (upstream) {
struct commit_list *base_list;
struct commit_list *base_list = NULL;
struct commit *commit;
struct object_id oid;
@ -1669,11 +1669,12 @@ static struct commit *get_base_commit(const char *base_commit,
return NULL;
}
commit = lookup_commit_or_die(&oid, "upstream base");
base_list = repo_get_merge_bases_many(the_repository,
commit, total,
list);
/* There should be one and only one merge base. */
if (!base_list || base_list->next) {
if (repo_get_merge_bases_many(the_repository,
commit, total,
list,
&base_list) < 0 ||
/* There should be one and only one merge base. */
!base_list || base_list->next) {
if (die_on_failure) {
die(_("could not find exact merge base"));
} else {
@ -1704,11 +1705,11 @@ static struct commit *get_base_commit(const char *base_commit,
*/
while (rev_nr > 1) {
for (i = 0; i < rev_nr / 2; i++) {
struct commit_list *merge_base;
merge_base = repo_get_merge_bases(the_repository,
rev[2 * i],
rev[2 * i + 1]);
if (!merge_base || merge_base->next) {
struct commit_list *merge_base = NULL;
if (repo_get_merge_bases(the_repository,
rev[2 * i],
rev[2 * i + 1], &merge_base) < 0 ||
!merge_base || merge_base->next) {
if (die_on_failure) {
die(_("failed to find exact merge base"));
} else {
@ -1725,7 +1726,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

@ -10,10 +10,13 @@
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],
rev_nr - 1, rev + 1);
if (repo_get_merge_bases_many_dirty(the_repository, rev[0],
rev_nr - 1, rev + 1, &result) < 0) {
free_commit_list(result);
return -1;
}
if (!result)
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)
{
struct commit_list *revs = NULL;
struct commit_list *result, *rev;
struct commit_list *result = NULL, *rev;
int i;
for (i = count - 1; i >= 0; i--)
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);
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)
{
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

@ -476,8 +476,9 @@ static int real_merge(struct merge_tree_options *o,
* Get the merge bases, in reverse order; see comment above
* merge_incore_recursive in merge-ort.h
*/
merge_bases = repo_get_merge_bases(the_repository, parent1,
parent2);
if (repo_get_merge_bases(the_repository, parent1,
parent2, &merge_bases) < 0)
exit(128);
if (!merge_bases && !o->allow_unrelated_histories)
die(_("refusing to merge unrelated histories"));
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)
; /* already up-to-date */
else if (!remoteheads->next)
common = repo_get_merge_bases(the_repository, head_commit,
remoteheads->item);
else {
else if (!remoteheads->next) {
if (repo_get_merge_bases(the_repository, head_commit,
remoteheads->item, &common) < 0) {
ret = 2;
goto done;
}
} else {
struct commit_list *list = remoteheads;
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);
}
@ -1626,7 +1633,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
struct commit_list *j;
for (j = remoteheads; j; j = j->next) {
struct commit_list *common_one;
struct commit_list *common_one = NULL;
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^
* HEAD^^" would be missed.
*/
common_one = repo_get_merge_bases(the_repository,
head_commit,
j->item);
if (repo_get_merge_bases(the_repository, head_commit,
j->item, &common_one) < 0)
exit(128);
common_item = common_one->item;
free_commit_list(common_one);
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 *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),
&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),
&revs);
result = get_octopus_merge_bases(revs);
if (get_octopus_merge_bases(revs, &result) < 0)
exit(128);
free_commit_list(revs);
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);
ret = repo_is_descendant_of(the_repository, merge_head, list);
free_commit_list(list);
if (ret < 0)
exit(128);
return ret;
}
@ -950,6 +953,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

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

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

@ -297,7 +297,7 @@ static int try_difference(const char *arg)
show_rev(NORMAL, &end_oid, end);
show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start);
if (symmetric) {
struct commit_list *exclude;
struct commit_list *exclude = NULL;
struct commit *a, *b;
a = lookup_commit_reference(the_repository, &start_oid);
b = lookup_commit_reference(the_repository, &end_oid);
@ -305,7 +305,8 @@ static int try_difference(const char *arg)
*dotdot = '.';
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) {
struct commit *commit = pop_commit(&exclude);
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! */
static struct commit_list *paint_down_to_common(struct repository *r,
struct commit *one, int n,
struct commit **twos,
timestamp_t min_generation)
static int paint_down_to_common(struct repository *r,
struct commit *one, int n,
struct commit **twos,
timestamp_t min_generation,
int ignore_missing_commits,
struct commit_list **result)
{
struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
struct commit_list *result = NULL;
int i;
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;
if (!n) {
commit_list_append(one, &result);
return result;
commit_list_append(one, result);
return 0;
}
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 (!(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 */
flags |= STALE;
@ -104,67 +105,97 @@ static struct commit_list *paint_down_to_common(struct repository *r,
parents = parents->next;
if ((p->object.flags & flags) == flags)
continue;
if (repo_parse_commit(r, p))
return NULL;
if (repo_parse_commit(r, p)) {
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;
prio_queue_put(&queue, p);
}
}
clear_prio_queue(&queue);
return result;
return 0;
}
static struct commit_list *merge_bases_many(struct repository *r,
struct commit *one, int n,
struct commit **twos)
static int merge_bases_many(struct repository *r,
struct commit *one, int n,
struct commit **twos,
struct commit_list **result)
{
struct commit_list *list = NULL;
struct commit_list *result = NULL;
int 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
* 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))
return NULL;
return error(_("could not parse commit %s"),
oid_to_hex(&one->object.oid));
for (i = 0; i < n; i++) {
if (!twos[i])
return 0;
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) {
struct commit *commit = pop_commit(&list);
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)
return ret;
return 0;
commit_list_insert(in->item, &ret);
commit_list_insert(in->item, result);
for (i = in->next; i; i = i->next) {
struct commit_list *new_commits = NULL, *end = NULL;
for (j = ret; j; j = j->next) {
struct commit_list *bases;
bases = repo_get_merge_bases(the_repository, i->item,
j->item);
for (j = *result; j; j = j->next) {
struct commit_list *bases = NULL;
if (repo_get_merge_bases(the_repository, i->item,
j->item, &bases) < 0) {
free_commit_list(bases);
free_commit_list(*result);
*result = NULL;
return -1;
}
if (!new_commits)
new_commits = bases;
else
@ -172,10 +203,10 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)
for (k = bases; k; k = k->next)
end = k;
}
free_commit_list(ret);
ret = new_commits;
free_commit_list(*result);
*result = new_commits;
}
return ret;
return 0;
}
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++)
repo_parse_commit(r, array[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]);
if (redundant[i])
@ -209,8 +240,16 @@ static int remove_redundant_no_gen(struct repository *r,
if (curr_generation < min_generation)
min_generation = curr_generation;
}
common = paint_down_to_common(r, array[i], filled,
work, min_generation);
if (paint_down_to_common(r, array[i], filled,
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)
redundant[i] = 1;
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);
}
static struct commit_list *get_merge_bases_many_0(struct repository *r,
struct commit *one,
int n,
struct commit **twos,
int cleanup)
static int get_merge_bases_many_0(struct repository *r,
struct commit *one,
int n,
struct commit **twos,
int cleanup,
struct commit_list **result)
{
struct commit_list *list;
struct commit **rslt;
struct commit_list *result;
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++) {
if (one == twos[i])
return result;
return 0;
}
if (!result || !result->next) {
if (!*result || !(*result)->next) {
if (cleanup) {
clear_commit_marks(one, all_flags);
clear_commit_marks_many(n, twos, all_flags);
}
return result;
return 0;
}
/* There are more than one */
cnt = commit_list_count(result);
cnt = commit_list_count(*result);
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;
free_commit_list(result);
free_commit_list(*result);
*result = NULL;
clear_commit_marks(one, all_flags);
clear_commit_marks_many(n, twos, all_flags);
cnt = remove_redundant(r, rslt, cnt);
result = NULL;
if (cnt < 0) {
free(rslt);
return -1;
}
for (i = 0; i < cnt; i++)
commit_list_insert_by_date(rslt[i], &result);
commit_list_insert_by_date(rslt[i], result);
free(rslt);
return result;
return 0;
}
struct commit_list *repo_get_merge_bases_many(struct repository *r,
struct commit *one,
int n,
struct commit **twos)
int repo_get_merge_bases_many(struct repository *r,
struct commit *one,
int n,
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,
struct commit *one,
int n,
struct commit **twos)
int repo_get_merge_bases_many_dirty(struct repository *r,
struct commit *one,
int n,
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,
struct commit *one,
struct commit *two)
int repo_get_merge_bases(struct repository *r,
struct commit *one,
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 {
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))
return 1;
ret = repo_in_merge_bases_many(r, other, 1, &commit, 0);
if (ret)
return ret;
}
return 0;
}
@ -474,17 +523,18 @@ int repo_is_descendant_of(struct repository *r,
* Is "commit" an ancestor of one of the "references"?
*/
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;
timestamp_t generation, max_generation = GENERATION_NUMBER_ZERO;
if (repo_parse_commit(r, commit))
return ret;
return ignore_missing_commits ? 0 : -1;
for (i = 0; i < nr_reference; i++) {
if (repo_parse_commit(r, reference[i]))
return ret;
return ignore_missing_commits ? 0 : -1;
generation = commit_graph_generation(reference[i]);
if (generation > max_generation)
@ -495,10 +545,11 @@ int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
if (generation > max_generation)
return ret;
bases = paint_down_to_common(r, commit,
nr_reference, reference,
generation);
if (commit->object.flags & PARENT2)
if (paint_down_to_common(r, commit,
nr_reference, reference,
generation, ignore_missing_commits, &bases))
ret = -1;
else if (commit->object.flags & PARENT2)
ret = 1;
clear_commit_marks(commit, 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);
if (num_head < 0) {
free(array);
return NULL;
}
for (i = 0; i < num_head; i++)
tail = &commit_list_insert(array[i], tail)->next;
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);
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

@ -9,18 +9,21 @@ struct ref_filter;
struct object_id;
struct object_array;
struct commit_list *repo_get_merge_bases(struct repository *r,
struct commit *rev1,
struct commit *rev2);
struct commit_list *repo_get_merge_bases_many(struct repository *r,
struct commit *one, int n,
struct commit **twos);
int repo_get_merge_bases(struct repository *r,
struct commit *rev1,
struct commit *rev2,
struct commit_list **result);
int repo_get_merge_bases_many(struct repository *r,
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 */
struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r,
struct commit *one, int n,
struct commit **twos);
int repo_get_merge_bases_many_dirty(struct repository *r,
struct commit *one, int n,
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,
struct commit *commit,
@ -30,7 +33,8 @@ int repo_in_merge_bases(struct repository *r,
struct commit *reference);
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);
/*
* 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 rev_collect revs;
struct commit_list *bases;
struct commit_list *bases = NULL;
int i;
struct commit *ret = NULL;
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++)
revs.commit[i]->object.flags &= ~TMP_MARK;
bases = repo_get_merge_bases_many(the_repository, commit, revs.nr,
revs.commit);
if (repo_get_merge_bases_many(the_repository, commit, revs.nr,
revs.commit, &bases) < 0)
exit(128);
/*
* 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;
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++) {
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);
}
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)
die(_("no merge base found"));
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 *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

@ -1011,7 +1011,7 @@ static int do_remerge_diff(struct rev_info *opt,
struct object_id *oid)
{
struct merge_options o;
struct commit_list *bases;
struct commit_list *bases = NULL;
struct merge_result res = {0};
struct pretty_print_context ctx = {0};
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_commit_or_die(parent1);
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 */
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_MAY_HAVE_REWINDS,
CONFLICT_SUBMODULE_NULL_MERGE_BASE,
CONFLICT_SUBMODULE_CORRUPT,
/* Keep this entry _last_ in the list */
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_NULL_MERGE_BASE] =
"CONFLICT (submodule lacks merge base)"
"CONFLICT (submodule lacks merge base)",
[CONFLICT_SUBMODULE_CORRUPT] =
"CONFLICT (submodule corrupt)"
};
struct logical_conflict_info {
@ -1710,7 +1713,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();
@ -1725,9 +1735,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;
}
}
}
@ -1749,7 +1767,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;
@ -1796,8 +1814,28 @@ 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);
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, NULL, NULL, NULL,
_("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 */
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);
path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1,
path, NULL, NULL, NULL,
@ -1816,7 +1864,17 @@ 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);
ret = -1;
goto cleanup;
}
if (ret2 > 0) {
oidcpy(result, a);
path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1,
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,
&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);
ret = -1;
break;
case 0:
path_msg(opt, CONFLICT_SUBMODULE_FAILED_TO_MERGE, 0,
path, NULL, NULL, NULL,
@ -5014,7 +5080,11 @@ static void merge_ort_internal(struct merge_options *opt,
struct strbuf merge_base_abbrev = STRBUF_INIT;
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 */
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");
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();
@ -1155,9 +1161,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;
}
}
}
@ -1193,7 +1207,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;
@ -1230,14 +1244,32 @@ 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);
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);
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);
ret = -1;
goto cleanup;
}
if (ret2) {
oidcpy(result, b);
if (show(opt, 3)) {
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;
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);
if (show(opt, 3)) {
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,
commit_a, commit_b);
switch (parent_count) {
case -1:
output(opt, 1,_("Failed to merge submodule %s (repository corrupt)"), path);
ret = -1;
break;
case 0:
output(opt, 1, _("Failed to merge submodule %s (merge following commits not found)"), path);
break;
@ -1393,11 +1435,14 @@ static int merge_mode_and_contents(struct merge_options *opt,
/* FIXME: bug, what if modes didn't match? */
result->clean = (merge_status == 0);
} else if (S_ISGITLINK(a->mode)) {
result->clean = merge_submodule(opt, &result->blob.oid,
o->path,
&o->oid,
&a->oid,
&b->oid);
int clean = merge_submodule(opt, &result->blob.oid,
o->path,
&o->oid,
&a->oid,
&b->oid);
if (clean < 0)
return -1;
result->clean = clean;
} else if (S_ISLNK(a->mode)) {
switch (opt->recursive_variant) {
case MERGE_VARIANT_NORMAL:
@ -3598,7 +3643,9 @@ static int merge_recursive_internal(struct merge_options *opt,
}
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);
}

View File

@ -607,7 +607,8 @@ int notes_merge(struct notes_merge_options *o,
assert(local && remote);
/* 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) {
base_oid = null_oid();
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 commit *one, *two;
struct commit_list *mbs;
struct commit_list *mbs = NULL;
struct object_id oid_tmp;
const char *dots;
int st;
@ -1516,7 +1516,10 @@ int repo_get_oid_mb(struct repository *r,
two = lookup_commit_reference_gently(r, &oid_tmp, 0);
if (!two)
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)
st = -1;
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)
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;
}

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)
{
struct commit_list *bases;
struct commit_list *bases = NULL;
struct commit *head, *other;
struct object_id oid;
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);
add_pending_object(revs, &head->object, "HEAD");
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_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM);
free_commit_list(bases);
@ -2095,14 +2096,17 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
} else {
/* A...B -- find merge bases between the two */
struct commit *a, *b;
struct commit_list *exclude;
struct commit_list *exclude = NULL;
a = lookup_commit_reference(revs->repo, &a_obj->oid);
b = lookup_commit_reference(revs->repo, &b_obj->oid);
if (!a || !b)
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,
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;
struct strbuf ref_name = STRBUF_INIT;
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;
const char *strategy = !opts->xopts.nr &&
(!opts->strategy ||
@ -4138,7 +4138,11 @@ static int do_merge(struct repository *r,
}
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,
&bases->item->object.oid)) {
ret = 0;

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)) {
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;
@ -827,7 +831,10 @@ int delayed_reachability_test(struct shallow_info *si, int c)
si->reachable[c] = repo_in_merge_bases_many(the_repository,
commit,
si->nr_commits,
si->commits);
si->commits,
1);
if (si->reachable[c] < 0)
exit(128);
si->need_reachability_test[c] = 0;
}
return si->reachable[c];

View File

@ -592,7 +592,12 @@ static void show_submodule_header(struct diff_options *o,
(!is_null_oid(two) && !*right))
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)->item == *left)
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));
else if (!strcmp(av[1], "in_merge_bases_many"))
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"))
printf("%s(A,X):%d\n", av[1], repo_is_descendant_of(r, A, X));
else if (!strcmp(av[1], "get_merge_bases_many")) {
struct commit_list *list = repo_get_merge_bases_many(the_repository,
A, X_nr,
X_array);
struct commit_list *list = NULL;
if (repo_get_merge_bases_many(the_repository,
A, X_nr,
X_array,
&list) < 0)
exit(128);
printf("%s(A,X):\n", av[1]);
print_sorted_commit_ids(list);
} 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_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