Commit e51c8b99 by Scott J. Goldman

Fix mark_parents() to account for bad luck traversals

If commit timestamps are off, we're more likely to hit a traversal
where the first path ends up traversing past the root commit of the tree.
If that happens, it's possible that the loop will complete before the second
path marks some of those final parents. This fix keeps track of the root
nodes that are encountered in the traversal, and verify that they are
properly marked.

In the best case, with accurate timestamps, the traversal will continue
to terminate when all the commits are STALE (parents of a merge-base), as
it did before. In the worst case, where one path makes a complete traversal
past a root commit, we will continue the loop until the root commit itself
is marked.
parent b337814e
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#include "merge.h" #include "merge.h"
#include "git2/graph.h" #include "git2/graph.h"
static int interesting(git_pqueue *list) static int interesting(git_pqueue *list, git_commit_list *roots)
{ {
unsigned int i; unsigned int i;
/* element 0 isn't used - we need to start at 1 */ /* element 0 isn't used - we need to start at 1 */
...@@ -20,6 +20,12 @@ static int interesting(git_pqueue *list) ...@@ -20,6 +20,12 @@ static int interesting(git_pqueue *list)
return 1; return 1;
} }
while(roots) {
if ((roots->item->flags & STALE) == 0)
return 1;
roots = roots->next;
}
return 0; return 0;
} }
...@@ -28,6 +34,7 @@ static int mark_parents(git_revwalk *walk, git_commit_list_node *one, ...@@ -28,6 +34,7 @@ static int mark_parents(git_revwalk *walk, git_commit_list_node *one,
{ {
int error; int error;
unsigned int i; unsigned int i;
git_commit_list *roots = NULL;
git_pqueue list; 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 */
...@@ -52,11 +59,13 @@ static int mark_parents(git_revwalk *walk, git_commit_list_node *one, ...@@ -52,11 +59,13 @@ static int mark_parents(git_revwalk *walk, git_commit_list_node *one,
return -1; return -1;
/* as long as there are non-STALE commits */ /* as long as there are non-STALE commits */
while (interesting(&list)) { while (interesting(&list, roots)) {
git_commit_list_node *commit; git_commit_list_node *commit;
int flags; int flags;
commit = git_pqueue_pop(&list); commit = git_pqueue_pop(&list);
if (commit == NULL)
break;
flags = commit->flags & (PARENT1 | PARENT2 | STALE); flags = commit->flags & (PARENT1 | PARENT2 | STALE);
if (flags == (PARENT1 | PARENT2)) { if (flags == (PARENT1 | PARENT2)) {
...@@ -78,8 +87,15 @@ static int mark_parents(git_revwalk *walk, git_commit_list_node *one, ...@@ -78,8 +87,15 @@ static int mark_parents(git_revwalk *walk, git_commit_list_node *one,
if (git_pqueue_insert(&list, p) < 0) if (git_pqueue_insert(&list, p) < 0)
return -1; return -1;
} }
if (commit->out_degree == 0) {
if (git_commit_list_insert(commit, &roots) == NULL)
return -1;
}
} }
if (roots)
git_commit_list_free(&roots);
git_pqueue_free(&list); git_pqueue_free(&list);
return 0; return 0;
......
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