Commit 655d381a1948783d7d26ff9ec5ef54ed6bbefb29

Vicent Marti 2010-05-23T16:51:31

Add topological sorting and new insertion methods for commit lists. 'git_commit_list_toposort()' and 'git_commit_list_timesort()' now sort a commit list by topological and time order respectively. Both sorts are stable and in place. 'git_commit_list_append' has been replaced by 'git_commit_list_push_back' and 'git_commit_list_push_front'. Signed-off-by: Vicent Marti <tanoku@gmail.com> Signed-off-by: Andreas Ericsson <ae@op5.se>

diff --git a/src/commit.c b/src/commit.c
index 10b759e..9b52f41 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -193,7 +193,7 @@ int git_commit__parse_buffer(git_commit *commit, void *data, size_t len)
         if (commit->uninteresting)
             parent->uninteresting = 1;
 
-        git_commit_list_append(&commit->parents, parent);
+        git_commit_list_push_back(&commit->parents, parent);
     }
 
     if (git_commit__parse_time(&commit->commit_time, buffer, buffer_end) < 0)
@@ -204,7 +204,7 @@ int git_commit__parse_buffer(git_commit *commit, void *data, size_t len)
     return 0;
 }
 
-void git_commit_list_append(git_commit_list *list, git_commit *commit)
+void git_commit_list_push_back(git_commit_list *list, git_commit *commit)
 {
     git_commit_node *node = NULL;
 
@@ -230,6 +230,33 @@ void git_commit_list_append(git_commit_list *list, git_commit *commit)
     list->size++;
 }
 
+void git_commit_list_push_front(git_commit_list *list, git_commit *commit)
+{
+    git_commit_node *node = NULL;
+
+    node = git__malloc(sizeof(git_commit_list));
+
+    if (node == NULL)
+        return;
+
+    node->commit = commit;
+    node->next = list->head;
+    node->prev = NULL;
+
+    if (list->head == NULL)
+    {
+        list->head = list->tail = node;
+    }
+    else
+    {
+        list->head->next = node;
+        list->head = node;
+    }
+
+    list->size++;
+}
+
+
 git_commit *git_commit_list_pop_back(git_commit_list *list)
 {
     git_commit_node *node;
@@ -291,7 +318,7 @@ void git_commit_list_clear(git_commit_list *list, int free_commits)
     list->size = 0;
 }
 
-void git_commit_list_sort(git_commit_list *list)
+void git_commit_list_timesort(git_commit_list *list)
 {
     git_commit_node *p, *q, *e;
     int in_size, p_size, q_size, merge_count, i;
@@ -345,3 +372,38 @@ void git_commit_list_sort(git_commit_list *list)
 
     } while (merge_count > 1);
 }
+
+void git_commit_list_toposort(git_commit_list *list)
+{
+    git_commit *commit;
+    git_commit_list topo;
+    memset(&topo, 0x0, sizeof(git_commit_list));
+
+    while ((commit = git_commit_list_pop_front(list)) != NULL)
+    {
+        git_commit_node *p;
+
+        if (commit->in_degree > 0)
+        {
+            commit->topo_delay = 1;
+            continue;
+        }
+
+        for (p = commit->parents.head; p != NULL; p = p->next)
+        {
+            p->commit->in_degree--;
+
+            if (p->commit->in_degree == 0 && p->commit->topo_delay)
+            {
+                p->commit->topo_delay = 0;
+                git_commit_list_push_front(list, p->commit);
+            }
+        }
+
+        git_commit_list_push_back(&topo, commit);
+    }
+
+    list->head = topo.head;
+    list->tail = topo.tail;
+    list->size = topo.size;
+}
diff --git a/src/commit.h b/src/commit.h
index 72ea4e9..e8d0c53 100644
--- a/src/commit.h
+++ b/src/commit.h
@@ -43,10 +43,16 @@ void git_commit__mark_uninteresting(git_commit *commit);
 
 int git_commit_parse_existing(git_commit *commit);
 
-void git_commit_list_clear(git_commit_list *list, int free_commits);
-void git_commit_list_append(git_commit_list *list, git_commit *commit);
+
+void git_commit_list_push_back(git_commit_list *list, git_commit *commit);
+void git_commit_list_push_front(git_commit_list *list, git_commit *commit);
+
 git_commit *git_commit_list_pop_back(git_commit_list *list);
 git_commit *git_commit_list_pop_front(git_commit_list *list);
-void git_commit_list_sort(git_commit_list *list);
+
+void git_commit_list_clear(git_commit_list *list, int free_commits);
+
+void git_commit_list_timesort(git_commit_list *list);
+void git_commit_list_toposort(git_commit_list *list);
 
 #endif
diff --git a/src/revobject.h b/src/revobject.h
index 4a59d93..c073337 100644
--- a/src/revobject.h
+++ b/src/revobject.h
@@ -26,10 +26,19 @@ struct git_revpool_table
     unsigned int max_count;
 };
 
+struct git_revpool_tableit
+{
+    struct git_revpool_node **nodes;
+    struct git_revpool_node *current_node;
+    unsigned int current_pos;
+    unsigned int size;
+};
+
 
 typedef struct git_revpool_node git_revpool_node;
 typedef struct git_revpool_object git_revpool_object;
 typedef struct git_revpool_table git_revpool_table;
+typedef struct git_revpool_tableit git_revpool_tableit;
 
 git_revpool_table *git_revpool_table_create(unsigned int min_size);
 int git_revpool_table_insert(git_revpool_table *table, git_revpool_object *object);
diff --git a/src/revwalk.c b/src/revwalk.c
index 088171c..4575d8b 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -73,7 +73,7 @@ void gitrp_push(git_revpool *pool, git_commit *commit)
     if (commit->uninteresting)
         git_commit__mark_uninteresting(commit);
 
-    git_commit_list_append(&pool->roots, commit);
+    git_commit_list_push_back(&pool->roots, commit);
 }
 
 void gitrp_hide(git_revpool *pool, git_commit *commit)
@@ -95,9 +95,12 @@ void gitrp__enroot(git_revpool *pool, git_commit *commit)
     commit->seen = 1;
 
     for (parents = commit->parents.head; parents != NULL; parents = parents->next)
+    {
+        parents->commit->in_degree++;
         gitrp__enroot(pool, parents->commit);
+    }
 
-    git_commit_list_append(&pool->iterator, commit);
+    git_commit_list_push_back(&pool->iterator, commit);
 }
 
 void gitrp_prepare_walk(git_revpool *pool)
@@ -107,7 +110,11 @@ void gitrp_prepare_walk(git_revpool *pool)
     for (it = pool->roots.head; it != NULL; it = it->next)
         gitrp__enroot(pool, it->commit);
 
-    // TODO: topo sort, time sort
+    if (pool->sorting & GIT_REVPOOL_SORT_TIME)
+        git_commit_list_timesort(&pool->iterator);
+
+    if (pool->sorting & GIT_REVPOOL_SORT_TOPO)
+        git_commit_list_toposort(&pool->iterator);
 
     if (pool->sorting & GIT_REVPOOL_SORT_REVERSE)
         pool->next_commit = &git_commit_list_pop_back;
diff --git a/tests/t0403-lists.c b/tests/t0403-lists.c
index 1093c8f..c16281e 100644
--- a/tests/t0403-lists.c
+++ b/tests/t0403-lists.c
@@ -4,7 +4,7 @@
 #include <git/odb.h>
 #include <git/commit.h>
 
-BEGIN_TEST(list_sort_test)
+BEGIN_TEST(list_timesort_test)
 
     git_commit_list list;
     git_commit_node *n;
@@ -32,10 +32,10 @@ BEGIN_TEST(list_sort_test)
             git_commit *c = git__malloc(sizeof(git_commit));
             c->commit_time = (time_t)rand();
 
-            git_commit_list_append(&list, c);
+            git_commit_list_push_back(&list, c);
         }
 
-        git_commit_list_sort(&list);
+        git_commit_list_timesort(&list);
         TEST_SORTED();
         git_commit_list_clear(&list, 1);
     }
@@ -46,15 +46,15 @@ BEGIN_TEST(list_sort_test)
         git_commit *c = git__malloc(sizeof(git_commit));
         c->commit_time = 0;
 
-        git_commit_list_append(&list, c);
+        git_commit_list_push_back(&list, c);
     }
 
-    git_commit_list_sort(&list);
+    git_commit_list_timesort(&list);
     TEST_SORTED();
     git_commit_list_clear(&list, 1);
 
     // Try to sort empty list
-    git_commit_list_sort(&list);
+    git_commit_list_timesort(&list);
     TEST_SORTED();
 
 END_TEST