summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff King <peff@peff.net>2014-06-25 19:39:52 -0400
committerJunio C Hamano <gitster@pobox.com>2014-06-30 13:38:27 -0700
commitd7954c875043f80fa7dc22e9c99ed86357ff1560 (patch)
treee2eaf71a9ac0060094048429e93682ad3b0c0cd8
parent0ba0846be273b594ae29f201e5814810ec07e9c5 (diff)
downloadgit-d7954c875043f80fa7dc22e9c99ed86357ff1560.tar.gz
paint_down_to_common: use prio_queue
When we are traversing to find merge bases, we keep our usual commit_list of commits to process, sorted by their commit timestamp. As we add each parent to the list, we have to spend "O(width of history)" to do the insertion, where the width of history is the number of simultaneous lines of development. If we instead use a heap-based priority queue, we can do these insertions in "O(log width)" time. This provides minor speedups to merge-base calculations (timings in linux.git, warm cache, best-of-five): [before] $ git merge-base HEAD v2.6.12 real 0m3.251s user 0m3.148s sys 0m0.104s [after] $ git merge-base HEAD v2.6.12 real 0m3.234s user 0m3.108s sys 0m0.128s That's only an 0.5% speedup, but it does help protect us against pathological cases. The downside is that our priority queue is not stable, which means that commits with the same timestamp may not come out in the order we put them in. You can see this in the test update in t6024. That test does a recursive merge across a set of commits that all have the same timestamp. For the virtual ancestor, the test currently ends up with blob like this: <<<<<<< Temporary merge branch 1 <<<<<<< Temporary merge branch 1 C ======= B >>>>>>> Temporary merge branch 2 ======= A >>>>>>> Temporary merge branch 2 but with this patch, the positions of B and A are swapped. This is probably fine, as the order is an internal implementation detail anyway (it would _not_ be fine if we were using a priority queue for "git log" traversal, which should show commits in parent order). While we are munging the "interesting" function, we also take the opportunity to give it a more descriptive name, and convert the return value to an int (we returned the first interesting commit, but nobody ever looked at it). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
-rw-r--r--commit.c42
-rwxr-xr-xt/t6024-recursive-merge.sh2
2 files changed, 20 insertions, 24 deletions
diff --git a/commit.c b/commit.c
index 881be3baa3..445b6797ad 100644
--- a/commit.c
+++ b/commit.c
@@ -729,45 +729,41 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so
static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
-static struct commit *interesting(struct commit_list *list)
+static int queue_has_nonstale(struct prio_queue *queue)
{
- while (list) {
- struct commit *commit = list->item;
- list = list->next;
- if (commit->object.flags & STALE)
- continue;
- return commit;
+ int i;
+ for (i = 0; i < queue->nr; i++) {
+ struct commit *commit = queue->array[i];
+ if (!(commit->object.flags & STALE))
+ return 1;
}
- return NULL;
+ return 0;
}
/* all input commits in one and twos[] must have been parsed! */
static struct commit_list *paint_down_to_common(struct commit *one, int n, struct commit **twos)
{
- struct commit_list *list = NULL;
+ struct prio_queue queue = { compare_commits_by_commit_date };
struct commit_list *result = NULL;
int i;
one->object.flags |= PARENT1;
- commit_list_insert_by_date(one, &list);
- if (!n)
- return list;
+ if (!n) {
+ commit_list_append(one, &result);
+ return result;
+ }
+ prio_queue_put(&queue, one);
+
for (i = 0; i < n; i++) {
twos[i]->object.flags |= PARENT2;
- commit_list_insert_by_date(twos[i], &list);
+ prio_queue_put(&queue, twos[i]);
}
- while (interesting(list)) {
- struct commit *commit;
+ while (queue_has_nonstale(&queue)) {
+ struct commit *commit = prio_queue_get(&queue);
struct commit_list *parents;
- struct commit_list *next;
int flags;
- commit = list->item;
- next = list->next;
- free(list);
- list = next;
-
flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
if (flags == (PARENT1 | PARENT2)) {
if (!(commit->object.flags & RESULT)) {
@@ -786,11 +782,11 @@ static struct commit_list *paint_down_to_common(struct commit *one, int n, struc
if (parse_commit(p))
return NULL;
p->object.flags |= flags;
- commit_list_insert_by_date(p, &list);
+ prio_queue_put(&queue, p);
}
}
- free_commit_list(list);
+ clear_prio_queue(&queue);
return result;
}
diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh
index 755d30ce2a..c3c634f8b8 100755
--- a/t/t6024-recursive-merge.sh
+++ b/t/t6024-recursive-merge.sh
@@ -76,7 +76,7 @@ test_expect_success "result contains a conflict" "test_cmp expect a1"
git ls-files --stage > out
cat > expect << EOF
-100644 439cc46de773d8a83c77799b7cc9191c128bfcff 1 a1
+100644 222cbac87715ba85080f8b1463833bb3cfb3b9e0 1 a1
100644 cf84443e49e1b366fac938711ddf4be2d4d1d9e9 2 a1
100644 fd7923529855d0b274795ae3349c5e0438333979 3 a1
EOF