[PATCH] Reorder Makefile rules
[git.git] / commit.c
index 03f8ca8..d4cd455 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -3,6 +3,22 @@
 #include "commit.h"
 #include "cache.h"
 
+struct sort_node
+{
+       /*
+         * the number of children of the associated commit
+         * that also occur in the list being sorted.
+         */
+       unsigned int indegree;
+
+       /*
+         * reference to original list item that we will re-use
+         * on output.
+         */
+       struct commit_list * list_item;
+
+};
+
 const char *commit_type = "commit";
 
 enum cmit_fmt get_commit_format(const char *arg)
@@ -36,8 +52,9 @@ struct commit *lookup_commit_reference(const unsigned char *sha1)
 
        if (!obj)
                return NULL;
-       if (obj->type == tag_type)
-               obj = ((struct tag *)obj)->tagged;
+       while (obj->type == tag_type)
+               obj = parse_object(((struct tag *)obj)->tagged->sha1);
+
        return check_commit(obj, sha1);
 }
 
@@ -76,22 +93,28 @@ static unsigned long parse_commit_date(const char *buf)
 
 int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
 {
-       void *bufptr = buffer;
+       char *bufptr = buffer;
        unsigned char parent[20];
        struct commit_list **pptr;
 
        if (item->object.parsed)
                return 0;
        item->object.parsed = 1;
-       get_sha1_hex(bufptr + 5, parent);
+       if (memcmp(bufptr, "tree ", 5))
+               return error("bogus commit object %s", sha1_to_hex(item->object.sha1));
+       if (get_sha1_hex(bufptr + 5, parent) < 0)
+               return error("bad tree pointer in commit %s\n", sha1_to_hex(item->object.sha1));
        item->tree = lookup_tree(parent);
        if (item->tree)
                add_ref(&item->object, &item->tree->object);
        bufptr += 46; /* "tree " + "hex sha1" + "\n" */
        pptr = &item->parents;
-       while (!memcmp(bufptr, "parent ", 7) &&
-              !get_sha1_hex(bufptr + 7, parent)) {
-               struct commit *new_parent = lookup_commit(parent);
+       while (!memcmp(bufptr, "parent ", 7)) {
+               struct commit *new_parent;
+
+               if (get_sha1_hex(bufptr + 7, parent) || bufptr[47] != '\n')
+                       return error("bad parents in commit %s", sha1_to_hex(item->object.sha1));
+               new_parent = lookup_commit(parent);
                if (new_parent) {
                        pptr = &commit_list_insert(new_parent, pptr)->next;
                        add_ref(&item->object, &new_parent->object);
@@ -147,7 +170,7 @@ void free_commit_list(struct commit_list *list)
        }
 }
 
-void insert_by_date(struct commit_list **list, struct commit *item)
+struct commit_list * insert_by_date(struct commit *item, struct commit_list **list)
 {
        struct commit_list **pp = list;
        struct commit_list *p;
@@ -157,7 +180,7 @@ void insert_by_date(struct commit_list **list, struct commit *item)
                }
                pp = &p->next;
        }
-       commit_list_insert(item, pp);
+       return commit_list_insert(item, pp);
 }
 
        
@@ -165,7 +188,7 @@ void sort_by_date(struct commit_list **list)
 {
        struct commit_list *ret = NULL;
        while (*list) {
-               insert_by_date(&ret, (*list)->item);
+               insert_by_date((*list)->item, &ret);
                *list = (*list)->next;
        }
        *list = ret;
@@ -186,7 +209,7 @@ struct commit *pop_most_recent_commit(struct commit_list **list,
                parse_commit(commit);
                if (!(commit->object.flags & mark)) {
                        commit->object.flags |= mark;
-                       insert_by_date(list, commit);
+                       insert_by_date(commit, list);
                }
                parents = parents->next;
        }
@@ -346,3 +369,94 @@ int count_parents(struct commit * commit)
         return count;
 }
 
+/*
+ * Performs an in-place topological sort on the list supplied.
+ */
+void sort_in_topological_order(struct commit_list ** list)
+{
+       struct commit_list * next = *list;
+       struct commit_list * work = NULL;
+       struct commit_list ** pptr = list;
+       struct sort_node * nodes;
+       struct sort_node * next_nodes;
+       int count = 0;
+
+       /* determine the size of the list */
+       while (next) {
+               next = next->next;
+               count++;
+       }
+       /* allocate an array to help sort the list */
+       nodes = xcalloc(count, sizeof(*nodes));
+       /* link the list to the array */
+       next_nodes = nodes;
+       next=*list;
+       while (next) {
+               next_nodes->list_item = next;
+               next->item->object.util = next_nodes;
+               next_nodes++;
+               next = next->next;
+       }
+       /* update the indegree */
+       next=*list;
+       while (next) {
+               struct commit_list * parents = next->item->parents;
+               while (parents) {
+                       struct commit * parent=parents->item;
+                       struct sort_node * pn = (struct sort_node *)parent->object.util;
+                       
+                       if (pn)
+                               pn->indegree++;
+                       parents=parents->next;
+               }
+               next=next->next;
+       }
+       /* 
+         * find the tips
+         *
+         * tips are nodes not reachable from any other node in the list 
+         * 
+         * the tips serve as a starting set for the work queue.
+         */
+       next=*list;
+       while (next) {
+               struct sort_node * node = (struct sort_node *)next->item->object.util;
+
+               if (node->indegree == 0) {
+                       commit_list_insert(next->item, &work);
+               }
+               next=next->next;
+       }
+       /* process the list in topological order */
+       while (work) {
+               struct commit * work_item = pop_commit(&work);
+               struct sort_node * work_node = (struct sort_node *)work_item->object.util;
+               struct commit_list * parents = work_item->parents;
+
+               while (parents) {
+                       struct commit * parent=parents->item;
+                       struct sort_node * pn = (struct sort_node *)parent->object.util;
+                       
+                       if (pn) {
+                               /* 
+                                * parents are only enqueued for emission 
+                                 * when all their children have been emitted thereby
+                                 * guaranteeing topological order.
+                                 */
+                               pn->indegree--;
+                               if (!pn->indegree) 
+                                       commit_list_insert(parent, &work);
+                       }
+                       parents=parents->next;
+               }
+               /*
+                 * work_item is a commit all of whose children
+                 * have already been emitted. we can emit it now.
+                 */
+               *pptr = work_node->list_item;
+               pptr = &(*pptr)->next;
+               *pptr = NULL;
+               work_item->object.util = NULL;
+       }
+       free(nodes);
+}