reduce_heads(): reimplement on top of remove_redundant()

This is used by "git merge" and "git merge-base --independent" but
used to use a similar N*(N-1) traversals to reject commits that are
ancestors of other commits.

Reimplement it on top of remove_redundant().  Note that the callers
of this function are allowed to pass the same commit more than once,
but remove_redundant() is designed to be fed each commit only once.
The function removes duplicates before calling remove_redundant().

Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Junio C Hamano 2012-08-30 23:20:40 -07:00
Родитель 5907cda1b2
Коммит f37d3c7552
1 изменённых файлов: 19 добавлений и 39 удалений

Просмотреть файл

@ -842,51 +842,31 @@ struct commit_list *reduce_heads(struct commit_list *heads)
{ {
struct commit_list *p; struct commit_list *p;
struct commit_list *result = NULL, **tail = &result; struct commit_list *result = NULL, **tail = &result;
struct commit **other; struct commit **array;
size_t num_head, num_other; int num_head, i;
if (!heads) if (!heads)
return NULL; return NULL;
/* Avoid unnecessary reallocations */ /* Uniquify */
for (p = heads, num_head = 0; p; p = p->next) for (p = heads; p; p = p->next)
p->item->object.flags &= ~STALE;
for (p = heads, num_head = 0; p; p = p->next) {
if (p->item->object.flags & STALE)
continue;
p->item->object.flags |= STALE;
num_head++; num_head++;
other = xcalloc(sizeof(*other), num_head);
/* For each commit, see if it can be reached by others */
for (p = heads; p; p = p->next) {
struct commit_list *q, *base;
/* Do we already have this in the result? */
for (q = result; q; q = q->next)
if (p->item == q->item)
break;
if (q)
continue;
num_other = 0;
for (q = heads; q; q = q->next) {
if (p->item == q->item)
continue;
other[num_other++] = q->item;
} }
if (num_other) array = xcalloc(sizeof(*array), num_head);
base = get_merge_bases_many(p->item, num_other, other, 1); for (p = heads, i = 0; p; p = p->next) {
else if (p->item->object.flags & STALE) {
base = NULL; array[i++] = p->item;
/* p->item->object.flags &= ~STALE;
* If p->item does not have anything common with other
* commits, there won't be any merge base. If it is
* reachable from some of the others, p->item will be
* the merge base. If its history is connected with
* others, but p->item is not reachable by others, we
* will get something other than p->item back.
*/
if (!base || (base->item != p->item))
tail = &(commit_list_insert(p->item, tail)->next);
free_commit_list(base);
} }
free(other); }
num_head = remove_redundant(array, num_head);
for (i = 0; i < num_head; i++)
tail = &commit_list_insert(array[i], tail)->next;
return result; return result;
} }