summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2014-03-20 20:24:11 +0100
committerCarlos Martín Nieto <cmn@dwim.me>2014-03-20 20:24:11 +0100
commit704b55cce33aa8665c558347c104041d3ec6e2f3 (patch)
tree3aff39274e7b95b9ff6a6f493e1f4ce3dc7200fc
parentf29e48995e1af91127157a1eca4177c2f411b1bb (diff)
downloadlibgit2-704b55cce33aa8665c558347c104041d3ec6e2f3.tar.gz
revwalk: don't try to find merge bases when there can be none
As a way to speed up the cases where we need to hide some commits, we find out what the merge bases are so we know to stop marking commits as uninteresting and avoid walking down a potentially very large amount of commits which we will never see. There are however two oversights in current code. The merge-base finding algorithm fails to recognize that if it is only given one commit, there can be no merge base. It instead walks down the whole ancestor chain needlessly. Make it return an empty list immediately in this situation. The revwalk does not know whether the user has asked to hide any commits at all. In situation where the user pushes multiple commits but doesn't hide any, the above fix wouldn't do the trick. Keep track of whether the user wants to hide any commits and only run the merge-base finding algorithm when it's needed.
-rw-r--r--src/merge.c6
-rw-r--r--src/revwalk.c18
-rw-r--r--src/revwalk.h3
3 files changed, 22 insertions, 5 deletions
diff --git a/src/merge.c b/src/merge.c
index 0b11c0da3..66952c074 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -205,6 +205,12 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l
git_commit_list *result = NULL, *tmp = NULL;
git_pqueue list;
+ /* If there's only the one commit, there can be no merge bases */
+ if (twos->length == 0) {
+ *out = NULL;
+ return 0;
+ }
+
/* if the commit is repeated, we have a our merge base already */
git_vector_foreach(twos, i, two) {
if (one == two)
diff --git a/src/revwalk.c b/src/revwalk.c
index 753911246..f037ee692 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -141,6 +141,9 @@ static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting,
if (commit == NULL)
return -1; /* error already reported by failed lookup */
+ if (uninteresting)
+ walk->did_hide = 1;
+
commit->uninteresting = uninteresting;
if (walk->one == NULL && !uninteresting) {
walk->one = commit;
@@ -390,11 +393,18 @@ static int prepare_walk(git_revwalk *walk)
return GIT_ITEROVER;
}
- /* first figure out what the merge bases are */
- if (git_merge__bases_many(&bases, walk, walk->one, &walk->twos) < 0)
- return -1;
+ /*
+ * If the user asked to hide commits, we need to figure out
+ * what the merge bases are so we can know when we can stop
+ * marking parents uninteresting.
+ */
+ if (walk->did_hide) {
+ if (git_merge__bases_many(&bases, walk, walk->one, &walk->twos) < 0)
+ return -1;
+
+ git_commit_list_free(&bases);
+ }
- git_commit_list_free(&bases);
if (process_commit(walk, walk->one, walk->one->uninteresting) < 0)
return -1;
diff --git a/src/revwalk.h b/src/revwalk.h
index 8c821d098..a0ce1ae86 100644
--- a/src/revwalk.h
+++ b/src/revwalk.h
@@ -32,7 +32,8 @@ struct git_revwalk {
int (*enqueue)(git_revwalk *, git_commit_list_node *);
unsigned walking:1,
- first_parent: 1;
+ first_parent: 1,
+ did_hide: 1;
unsigned int sorting;
/* merge base calculation */