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>
This commit is contained in:
79
commit.c
79
commit.c
@ -692,6 +692,60 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)
|
|||||||
return ret;
|
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,
|
struct commit_list *get_merge_bases_many(struct commit *one,
|
||||||
int n,
|
int n,
|
||||||
struct commit **twos,
|
struct commit **twos,
|
||||||
@ -700,7 +754,7 @@ struct commit_list *get_merge_bases_many(struct commit *one,
|
|||||||
struct commit_list *list;
|
struct commit_list *list;
|
||||||
struct commit **rslt;
|
struct commit **rslt;
|
||||||
struct commit_list *result;
|
struct commit_list *result;
|
||||||
int cnt, i, j;
|
int cnt, i;
|
||||||
|
|
||||||
result = merge_bases_many(one, n, twos);
|
result = merge_bases_many(one, n, twos);
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
@ -731,28 +785,11 @@ struct commit_list *get_merge_bases_many(struct commit *one,
|
|||||||
clear_commit_marks(one, all_flags);
|
clear_commit_marks(one, all_flags);
|
||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
clear_commit_marks(twos[i], all_flags);
|
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;
|
result = NULL;
|
||||||
for (i = 0; i < cnt; i++) {
|
for (i = 0; i < cnt; i++)
|
||||||
if (rslt[i])
|
commit_list_insert_by_date(rslt[i], &result);
|
||||||
commit_list_insert_by_date(rslt[i], &result);
|
|
||||||
}
|
|
||||||
free(rslt);
|
free(rslt);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user