Browse Source

get_merge_bases_many(): walk from many tips in parallel

The get_merge_bases_many() function reduces the result returned by
the merge_bases_many() function, which is a set of possible merge
bases, by excluding commits that can be reached from other commits.
We used to do N*(N-1) traversals for this, but we can check if one
commit reaches which other (N-1) commits by a single traversal, and
repeat it for all the candidates to find the answer.

Introduce remove_redundant() helper function to do this painting; we
should be able to use it to reimplement reduce_heads() as well.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
Junio C Hamano 12 years ago
parent
commit
94f0ced0d0
  1. 79
      commit.c

79
commit.c

@ -692,6 +692,60 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in) @@ -692,6 +692,60 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)
return ret;
}

static int remove_redundant(struct commit **array, int cnt)
{
/*
* Some commit in the array may be an ancestor of
* another commit. Move such commit to the end of
* the array, and return the number of commits that
* are independent from each other.
*/
struct commit **work;
unsigned char *redundant;
int *filled_index;
int i, j, filled;

work = xcalloc(cnt, sizeof(*work));
redundant = xcalloc(cnt, 1);
filled_index = xmalloc(sizeof(*filled_index) * (cnt - 1));

for (i = 0; i < cnt; i++) {
struct commit_list *common;

if (redundant[i])
continue;
for (j = filled = 0; j < cnt; j++) {
if (i == j || redundant[j])
continue;
filled_index[filled] = j;
work[filled++] = array[j];
}
common = paint_down_to_common(array[i], filled, work);
if (array[i]->object.flags & PARENT2)
redundant[i] = 1;
for (j = 0; j < filled; j++)
if (work[j]->object.flags & PARENT1)
redundant[filled_index[j]] = 1;
clear_commit_marks(array[i], all_flags);
for (j = 0; j < filled; j++)
clear_commit_marks(work[j], all_flags);
free_commit_list(common);
}

/* Now collect the result */
memcpy(work, array, sizeof(*array) * cnt);
for (i = filled = 0; i < cnt; i++)
if (!redundant[i])
array[filled++] = work[i];
for (j = filled, i = 0; i < cnt; i++)
if (redundant[i])
array[j++] = work[i];
free(work);
free(redundant);
free(filled_index);
return filled;
}

struct commit_list *get_merge_bases_many(struct commit *one,
int n,
struct commit **twos,
@ -700,7 +754,7 @@ struct commit_list *get_merge_bases_many(struct commit *one, @@ -700,7 +754,7 @@ struct commit_list *get_merge_bases_many(struct commit *one,
struct commit_list *list;
struct commit **rslt;
struct commit_list *result;
int cnt, i, j;
int cnt, i;

result = merge_bases_many(one, n, twos);
for (i = 0; i < n; i++) {
@ -731,28 +785,11 @@ struct commit_list *get_merge_bases_many(struct commit *one, @@ -731,28 +785,11 @@ struct commit_list *get_merge_bases_many(struct commit *one,
clear_commit_marks(one, all_flags);
for (i = 0; i < n; i++)
clear_commit_marks(twos[i], all_flags);
for (i = 0; i < cnt - 1; i++) {
for (j = i+1; j < cnt; j++) {
if (!rslt[i] || !rslt[j])
continue;
result = merge_bases_many(rslt[i], 1, &rslt[j]);
clear_commit_marks(rslt[i], all_flags);
clear_commit_marks(rslt[j], all_flags);
for (list = result; list; list = list->next) {
if (rslt[i] == list->item)
rslt[i] = NULL;
if (rslt[j] == list->item)
rslt[j] = NULL;
}
}
}

/* Surviving ones in rslt[] are the independent results */
cnt = remove_redundant(rslt, cnt);
result = NULL;
for (i = 0; i < cnt; i++) {
if (rslt[i])
commit_list_insert_by_date(rslt[i], &result);
}
for (i = 0; i < cnt; i++)
commit_list_insert_by_date(rslt[i], &result);
free(rslt);
return result;
}

Loading…
Cancel
Save