Skip to content

Commit 48c6436

Browse files
committed
revwalk: port over the topological sorting
After porting over the commit hiding and selection we were still left with mistmaching output due to the topologial sort. This ports the topological sorting code to make us match with our equivalent of `--date-order` and `--topo-order` against the output from `rev-list`.
1 parent 938f8e3 commit 48c6436

File tree

2 files changed

+109
-49
lines changed

2 files changed

+109
-49
lines changed

src/revwalk.c

Lines changed: 107 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -357,39 +357,16 @@ static int revwalk_next_unsorted(git_commit_list_node **object_out, git_revwalk
357357

358358
static int revwalk_next_toposort(git_commit_list_node **object_out, git_revwalk *walk)
359359
{
360-
git_commit_list_node *next;
361-
unsigned short i, max;
362-
363-
for (;;) {
364-
next = git_commit_list_pop(&walk->iterator_topo);
365-
if (next == NULL) {
366-
giterr_clear();
367-
return GIT_ITEROVER;
368-
}
369-
370-
if (next->in_degree > 0) {
371-
next->topo_delay = 1;
372-
continue;
373-
}
374-
375-
376-
max = next->out_degree;
377-
if (walk->first_parent && next->out_degree)
378-
max = 1;
360+
git_commit_list_node *node;
379361

380-
for (i = 0; i < max; ++i) {
381-
git_commit_list_node *parent = next->parents[i];
382-
383-
if (--parent->in_degree == 0 && parent->topo_delay) {
384-
parent->topo_delay = 0;
385-
if (git_commit_list_insert(parent, &walk->iterator_topo) == NULL)
386-
return -1;
387-
}
388-
}
389-
390-
*object_out = next;
362+
node = git_commit_list_pop(&walk->iterator_topo);
363+
if (node) {
364+
*object_out = node;
391365
return 0;
392366
}
367+
368+
giterr_clear();
369+
return GIT_ITEROVER;
393370
}
394371

395372
static int revwalk_next_reverse(git_commit_list_node **object_out, git_revwalk *walk)
@@ -453,8 +430,6 @@ static int add_parents_to_list(git_revwalk *walk, git_commit_list_node *commit,
453430

454431
commit->added = 1;
455432

456-
/* TODO: add the insertion callback here as well */
457-
458433
/*
459434
* Go full on in the uninteresting case as we want to include
460435
* as many of these as we can.
@@ -494,7 +469,7 @@ static int add_parents_to_list(git_revwalk *walk, git_commit_list_node *commit,
494469
return error;
495470

496471
if (walk->hide_cb && walk->hide_cb(&p->oid, walk->hide_cb_payload))
497-
continue;
472+
continue;
498473

499474
if (!p->seen) {
500475
p->seen = 1;
@@ -578,10 +553,104 @@ static int limit_list(git_commit_list **out, git_revwalk *walk, git_commit_list
578553
p = &git_commit_list_insert(commit, p)->next;
579554
}
580555

556+
git_commit_list_free(&list);
581557
*out = newlist;
582558
return 0;
583559
}
584560

561+
static int sort_in_topological_order(git_commit_list **out, git_revwalk *walk)
562+
{
563+
git_commit_list *list = NULL, *ll = NULL, *newlist, **pptr;
564+
git_commit_list_node *next;
565+
git_pqueue queue;
566+
git_vector_cmp queue_cmp = NULL;
567+
unsigned short i;
568+
int error;
569+
570+
if (walk->sorting & GIT_SORT_TIME)
571+
queue_cmp = git_commit_list_time_cmp;
572+
573+
if ((error = git_pqueue_init(&queue, 0, 8, queue_cmp)))
574+
return error;
575+
576+
/*
577+
* Start by resetting the in-degree to 1 for the commits in
578+
* our list. We want to go through this list again, so we
579+
* store it in the commit list as we extract it from the lower
580+
* machinery.
581+
*/
582+
while ((error = walk->get_next(&next, walk)) == 0) {
583+
next->in_degree = 1;
584+
git_commit_list_insert(next, &list);
585+
}
586+
587+
if (error != GIT_ITEROVER)
588+
goto cleanup;
589+
590+
error = 0;
591+
592+
/*
593+
* Count up how many children each commit has. We limit
594+
* ourselves to those commits in the original list (in-degree
595+
* of 1) avoiding setting it for any parent that was hidden.
596+
*/
597+
for(ll = list; ll; ll = ll->next) {
598+
for (i = 0; i < ll->item->out_degree; ++i) {
599+
git_commit_list_node *parent = ll->item->parents[i];
600+
if (parent->in_degree)
601+
parent->in_degree++;
602+
}
603+
}
604+
605+
/*
606+
* Now we find the tips i.e. those not reachable from any other node
607+
* i.e. those which still have an in-degree of 1.
608+
*/
609+
for(ll = list; ll; ll = ll->next) {
610+
if (ll->item->in_degree == 1) {
611+
if ((error = git_pqueue_insert(&queue, ll->item)))
612+
goto cleanup;
613+
}
614+
}
615+
616+
/*
617+
* We need to output the tips in the order that they came out of the
618+
* traversal, so if we're not doing time-sorting, we need to reverse the
619+
* pqueue in order to get them to come out as we inserted them.
620+
*/
621+
if ((walk->sorting & GIT_SORT_TIME) == 0)
622+
git_pqueue_reverse(&queue);
623+
624+
625+
pptr = &newlist;
626+
newlist = NULL;
627+
while ((next = git_pqueue_pop(&queue)) != NULL) {
628+
for (i = 0; i < next->out_degree; ++i) {
629+
git_commit_list_node *parent = next->parents[i];
630+
if (parent->in_degree == 0)
631+
continue;
632+
633+
if (--parent->in_degree == 1) {
634+
if ((error = git_pqueue_insert(&queue, parent)))
635+
goto cleanup;
636+
}
637+
}
638+
639+
/* All the children of 'item' have been emitted (since we got to it via the priority queue) */
640+
next->in_degree = 0;
641+
642+
pptr = &git_commit_list_insert(next, pptr)->next;
643+
}
644+
645+
*out = newlist;
646+
error = 0;
647+
648+
cleanup:
649+
git_commit_list_free(&list);
650+
git_pqueue_free(&queue);
651+
return error;
652+
}
653+
585654
static int prepare_walk(git_revwalk *walk)
586655
{
587656
int error;
@@ -615,25 +684,16 @@ static int prepare_walk(git_revwalk *walk)
615684
if (list->item->uninteresting)
616685
continue;
617686

618-
if ((error = walk->enqueue(walk, list->item)) < 0)
687+
if ((error = walk->enqueue(walk, list->item)) < 0) {
688+
git_commit_list_free(&commits);
619689
return error;
690+
}
620691
}
621692

693+
git_commit_list_free(&commits);
622694

623695
if (walk->sorting & GIT_SORT_TOPOLOGICAL) {
624-
unsigned short i;
625-
626-
while ((error = walk->get_next(&next, walk)) == 0) {
627-
for (i = 0; i < next->out_degree; ++i) {
628-
git_commit_list_node *parent = next->parents[i];
629-
parent->in_degree++;
630-
}
631-
632-
if (git_commit_list_insert(next, &walk->iterator_topo) == NULL)
633-
return -1;
634-
}
635-
636-
if (error != GIT_ITEROVER)
696+
if ((error = sort_in_topological_order(&walk->iterator_topo, walk)))
637697
return error;
638698

639699
walk->get_next = &revwalk_next_toposort;

tests/revwalk/hidecb.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ void test_revwalk_hidecb__hide_some_commits(void)
158158

159159
cl_git_pass(git_revwalk_new(&walk, _repo));
160160
cl_git_pass(git_revwalk_push(walk, &_head_id));
161-
git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL | GIT_SORT_TIME);
161+
git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL);
162162

163163
/* Add hide callback */
164164
cl_git_pass(git_revwalk_add_hide_cb(walk, hide_commit_cb, NULL));
@@ -183,7 +183,7 @@ void test_revwalk_hidecb__test_payload(void)
183183

184184
cl_git_pass(git_revwalk_new(&walk, _repo));
185185
cl_git_pass(git_revwalk_push(walk, &_head_id));
186-
git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL | GIT_SORT_TIME);
186+
git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL);
187187

188188
/* Add hide callback, pass id of parent of initial commit as payload data */
189189
cl_git_pass(git_revwalk_add_hide_cb(walk, hide_commit_use_payload_cb, &commit_ids[5]));

0 commit comments

Comments
 (0)