Commit f9e4bfa3 by Carlos Martín Nieto

revwalk: use a priority queue for calculating merge bases

As parents are older than their children, we're appending to the
commit list most of the time, which makes an ordered linked list quite
inefficient.

While we're there, don't sort the results list in the main loop, as
we're sorting them afterwards and it creates extra work.
parent 5cf7bccd
...@@ -268,18 +268,16 @@ static int commit_parse(git_revwalk *walk, commit_object *commit) ...@@ -268,18 +268,16 @@ static int commit_parse(git_revwalk *walk, commit_object *commit)
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse commit"); return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse commit");
} }
static commit_object *interesting(commit_list *list) static int interesting(git_pqueue *list)
{ {
while (list) { unsigned int i;
commit_object *commit = list->item; for (i = 1; i < git_pqueue_size(list); i++) {
list = list->next; commit_object *commit = list->d[i];
if (commit->flags & STALE) if ((commit->flags & STALE) == 0)
continue; return 1;
return commit;
} }
return NULL; return 0;
} }
static int merge_bases_many(commit_list **out, git_revwalk *walk, commit_object *one, git_vector *twos) static int merge_bases_many(commit_list **out, git_revwalk *walk, commit_object *one, git_vector *twos)
...@@ -287,44 +285,45 @@ static int merge_bases_many(commit_list **out, git_revwalk *walk, commit_object ...@@ -287,44 +285,45 @@ static int merge_bases_many(commit_list **out, git_revwalk *walk, commit_object
int error; int error;
unsigned int i; unsigned int i;
commit_object *two; commit_object *two;
commit_list *list = NULL, *result = NULL; commit_list *result = NULL, *tmp = NULL;
git_pqueue list;
/* if the commit is repeated, we have a our merge base already */ /* if the commit is repeated, we have a our merge base already */
git_vector_foreach(twos, i, two) { git_vector_foreach(twos, i, two) {
if (one == two) if (one == two)
return commit_list_insert(one, out) ? GIT_SUCCESS : GIT_ENOMEM; return commit_list_insert(one, out) ? 0 : -1;
} }
if ((error = commit_parse(walk, one)) < GIT_SUCCESS) if (git_pqueue_init(&list, twos->length * 2, commit_time_cmp) < 0)
return error; return -1;
if (commit_parse(walk, one) < 0)
return -1;
one->flags |= PARENT1; one->flags |= PARENT1;
if (commit_list_insert(one, &list) == NULL) if (git_pqueue_insert(&list, one) < 0)
return GIT_ENOMEM; return -1;
git_vector_foreach(twos, i, two) { git_vector_foreach(twos, i, two) {
commit_parse(walk, two); commit_parse(walk, two);
two->flags |= PARENT2; two->flags |= PARENT2;
if (commit_list_insert_by_date(two, &list) == NULL) if (git_pqueue_insert(&list, two) < 0)
return GIT_ENOMEM; return -1;
} }
/* as long as there are non-STALE commits */ /* as long as there are non-STALE commits */
while (interesting(list)) { while (interesting(&list)) {
commit_object *commit = list->item; commit_object *commit;
commit_list *next;
int flags; int flags;
next = list->next; commit = git_pqueue_pop(&list);
git__free(list);
list = next;
flags = commit->flags & (PARENT1 | PARENT2 | STALE); flags = commit->flags & (PARENT1 | PARENT2 | STALE);
if (flags == (PARENT1 | PARENT2)) { if (flags == (PARENT1 | PARENT2)) {
if (!(commit->flags & RESULT)) { if (!(commit->flags & RESULT)) {
commit->flags |= RESULT; commit->flags |= RESULT;
if (commit_list_insert_by_date(commit, &result) == NULL) if (commit_list_insert(commit, &result) == NULL)
return GIT_ENOMEM; return -1;
} }
/* we mark the parents of a merge stale */ /* we mark the parents of a merge stale */
flags |= STALE; flags |= STALE;
...@@ -339,29 +338,29 @@ static int merge_bases_many(commit_list **out, git_revwalk *walk, commit_object ...@@ -339,29 +338,29 @@ static int merge_bases_many(commit_list **out, git_revwalk *walk, commit_object
return error; return error;
p->flags |= flags; p->flags |= flags;
if (commit_list_insert_by_date(p, &list) == NULL) if (git_pqueue_insert(&list, p) < 0)
return GIT_ENOMEM; return -1;
} }
} }
commit_list_free(&list); git_pqueue_free(&list);
/* filter out any stale commits in the results */ /* filter out any stale commits in the results */
list = result; tmp = result;
result = NULL; result = NULL;
while (list) { while (tmp) {
struct commit_list *next = list->next; struct commit_list *next = tmp->next;
if (!(list->item->flags & STALE)) if (!(tmp->item->flags & STALE))
if (commit_list_insert_by_date(list->item, &result) == NULL) if (commit_list_insert_by_date(tmp->item, &result) == NULL)
return GIT_ENOMEM; return -1;
free(list); git__free(tmp);
list = next; tmp = next;
} }
*out = result; *out = result;
return GIT_SUCCESS; return 0;
} }
int git_merge_base(git_oid *out, git_repository *repo, git_oid *one, git_oid *two) int git_merge_base(git_oid *out, git_repository *repo, git_oid *one, git_oid *two)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment