aboutsummaryrefslogtreecommitdiffstats
path: root/commit.c
diff options
context:
space:
mode:
authorJon Seymour <jon.seymour@gmail.com>2005-07-07 02:39:34 +1000
committerLinus Torvalds <torvalds@g5.osdl.org>2005-07-06 10:19:04 -0700
commitab580acea4b1b79daaca0c8727b810073a610e2a (patch)
tree51ac13f6cc73bfdadf8ca08a93db46e9f6a5ee50 /commit.c
parent9e9824ba0e641095f1cbc01c724d8e0a382c0580 (diff)
downloadgit-ab580acea4b1b79daaca0c8727b810073a610e2a.tar.gz
[PATCH] Add a topological sort procedure to commit.c
This introduces an in-place topological sort procedure to commit.c. Given a list of commits, sort_in_topological_order() will perform an in-place topological sort of that list. The invariant that applies to the resulting list is: a reachable from b => ord(b) < ord(a) This invariant is weaker than the --merge-order invariant, but is cheaper to calculate (assuming the list has been identified) and will serve any purpose where only a minimal topological order guarantee is required. Signed-off-by: Jon Seymour <jon.seymour@gmail.com> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'commit.c')
-rw-r--r--commit.c107
1 files changed, 107 insertions, 0 deletions
diff --git a/commit.c b/commit.c
index 254c902716..0bbfa2ab31 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)
@@ -346,3 +362,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);
+}