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
}