X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=commit.c;h=caee5bc218f6db03ec4c8f16f39b892ebb7c762c;hb=d48a72f337fa8edfb7a09e620e75cbbb2a5fe836;hp=03f8ca87960bff7694f9eac5640bd42d928f1b25;hpb=9b66ec04741b4030e7654dcb7b978a29a8214b31;p=git.git diff --git a/commit.c b/commit.c index 03f8ca87..caee5bc2 100644 --- 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); } @@ -147,7 +164,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 +174,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 +182,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 +203,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 +363,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); +}