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>

Junio C Hamano committed Aug 30, 2012 at 15:35 UTC 94f0ced0d08c8f835992b82224231b9353490e0c
1 file changed +58 -21
commit.c
+58 -21
@@ -692,6 +692,60 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)
692 return ret;
693 }
694
695 +static int remove_redundant(struct commit **array, int cnt)
696 +{
697 + /*
698 + * Some commit in the array may be an ancestor of
699 + * another commit. Move such commit to the end of
700 + * the array, and return the number of commits that
701 + * are independent from each other.
702 + */
703 + struct commit **work;
704 + unsigned char *redundant;
705 + int *filled_index;
706 + int i, j, filled;
707 +
708 + work = xcalloc(cnt, sizeof(*work));
709 + redundant = xcalloc(cnt, 1);
710 + filled_index = xmalloc(sizeof(*filled_index) * (cnt - 1));
711 +
712 + for (i = 0; i < cnt; i++) {
713 + struct commit_list *common;
714 +
715 + if (redundant[i])
716 + continue;
717 + for (j = filled = 0; j < cnt; j++) {
718 + if (i == j || redundant[j])
719 + continue;
720 + filled_index[filled] = j;
721 + work[filled++] = array[j];
722 + }
723 + common = paint_down_to_common(array[i], filled, work);
724 + if (array[i]->object.flags & PARENT2)
725 + redundant[i] = 1;
726 + for (j = 0; j < filled; j++)
727 + if (work[j]->object.flags & PARENT1)
728 + redundant[filled_index[j]] = 1;
729 + clear_commit_marks(array[i], all_flags);
730 + for (j = 0; j < filled; j++)
731 + clear_commit_marks(work[j], all_flags);
732 + free_commit_list(common);
733 + }
734 +
735 + /* Now collect the result */
736 + memcpy(work, array, sizeof(*array) * cnt);
737 + for (i = filled = 0; i < cnt; i++)
738 + if (!redundant[i])
739 + array[filled++] = work[i];
740 + for (j = filled, i = 0; i < cnt; i++)
741 + if (redundant[i])
742 + array[j++] = work[i];
743 + free(work);
744 + free(redundant);
745 + free(filled_index);
746 + return filled;
747 +}
748 +
749 struct commit_list *get_merge_bases_many(struct commit *one,
750 int n,
751 struct commit **twos,
@@ -700,7 +754,7 @@ struct commit_list *get_merge_bases_many(struct commit *one,
754 struct commit_list *list;
755 struct commit **rslt;
756 struct commit_list *result;
703 - int cnt, i, j;
757 + int cnt, i;
758
759 result = merge_bases_many(one, n, twos);
760 for (i = 0; i < n; i++) {
@@ -731,28 +785,11 @@ struct commit_list *get_merge_bases_many(struct commit *one,
785 clear_commit_marks(one, all_flags);
786 for (i = 0; i < n; i++)
787 clear_commit_marks(twos[i], all_flags);
734 - for (i = 0; i < cnt - 1; i++) {
735 - for (j = i+1; j < cnt; j++) {
736 - if (!rslt[i] || !rslt[j])
737 - continue;
738 - result = merge_bases_many(rslt[i], 1, &rslt[j]);
739 - clear_commit_marks(rslt[i], all_flags);
740 - clear_commit_marks(rslt[j], all_flags);
741 - for (list = result; list; list = list->next) {
742 - if (rslt[i] == list->item)
743 - rslt[i] = NULL;
744 - if (rslt[j] == list->item)
745 - rslt[j] = NULL;
746 - }
747 - }
748 - }
788
750 - /* Surviving ones in rslt[] are the independent results */
789 + cnt = remove_redundant(rslt, cnt);
790 result = NULL;
752 - for (i = 0; i < cnt; i++) {
753 - if (rslt[i])
754 - commit_list_insert_by_date(rslt[i], &result);
755 - }
791 + for (i = 0; i < cnt; i++)
792 + commit_list_insert_by_date(rslt[i], &result);
793 free(rslt);
794 return result;
795 }