commit: add short-circuit to paint_down_to_common()

When running 'git branch --contains', the in_merge_bases_many() method calls paint_down_to_common() to discover if a specific commit is reachable from a set of branches. Commits with lower generation number are not needed to correctly answer the containment query of in_merge_bases_many(). Add a new parameter, min_generation, to paint_down_to_common() that prevents walking commits with generation number strictly less than min_generation. If 0 is given, then there is no functional change. For in_merge_bases_many(), we can pass commit->generation as the cutoff, and this saves time during 'git branch --contains' queries that would otherwise walk "around" the commit we are inspecting. For a copy of the Linux repository, where HEAD is checked out at v4.13~100, we get the following performance improvement for 'git branch --contains' over the previous commit: Before: 0.21s After: 0.13s Rel %: -38% Signed-off-by: Derrick Stolee <dstolee@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed May 1, 2018 at 12:47 UTC d7c1ec3efd0092ee665085cba4e8a2bcef95143b
1 file changed +16 -4
commit.c
+16 -4
@@ -808,11 +808,14 @@ static int queue_has_nonstale(struct prio_queue *queue)
808 }
809
810 /* all input commits in one and twos[] must have been parsed! */
811 -static struct commit_list *paint_down_to_common(struct commit *one, int n, struct commit **twos)
811 +static struct commit_list *paint_down_to_common(struct commit *one, int n,
812 + struct commit **twos,
813 + int min_generation)
814 {
815 struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
816 struct commit_list *result = NULL;
817 int i;
818 + uint32_t last_gen = GENERATION_NUMBER_INFINITY;
819
820 one->object.flags |= PARENT1;
821 if (!n) {
@@ -831,6 +834,15 @@ static struct commit_list *paint_down_to_common(struct commit *one, int n, struc
834 struct commit_list *parents;
835 int flags;
836
837 + if (commit->generation > last_gen)
838 + BUG("bad generation skip %8x > %8x at %s",
839 + commit->generation, last_gen,
840 + oid_to_hex(&commit->object.oid));
841 + last_gen = commit->generation;
842 +
843 + if (commit->generation < min_generation)
844 + break;
845 +
846 flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
847 if (flags == (PARENT1 | PARENT2)) {
848 if (!(commit->object.flags & RESULT)) {
@@ -879,7 +891,7 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
891 return NULL;
892 }
893
882 - list = paint_down_to_common(one, n, twos);
894 + list = paint_down_to_common(one, n, twos, 0);
895
896 while (list) {
897 struct commit *commit = pop_commit(&list);
@@ -946,7 +958,7 @@ static int remove_redundant(struct commit **array, int cnt)
958 filled_index[filled] = j;
959 work[filled++] = array[j];
960 }
949 - common = paint_down_to_common(array[i], filled, work);
961 + common = paint_down_to_common(array[i], filled, work, 0);
962 if (array[i]->object.flags & PARENT2)
963 redundant[i] = 1;
964 for (j = 0; j < filled; j++)
@@ -1070,7 +1082,7 @@ int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit *
1082 if (commit->generation > min_generation)
1083 return ret;
1084
1073 - bases = paint_down_to_common(commit, nr_reference, reference);
1085 + bases = paint_down_to_common(commit, nr_reference, reference, commit->generation);
1086 if (commit->object.flags & PARENT2)
1087 ret = 1;
1088 clear_commit_marks(commit, all_flags);