[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>
This commit is contained in:
parent
9e9824ba0e
commit
ab580acea4
107
commit.c
107
commit.c
@ -3,6 +3,22 @@
|
|||||||
#include "commit.h"
|
#include "commit.h"
|
||||||
#include "cache.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";
|
const char *commit_type = "commit";
|
||||||
|
|
||||||
enum cmit_fmt get_commit_format(const char *arg)
|
enum cmit_fmt get_commit_format(const char *arg)
|
||||||
@ -346,3 +362,94 @@ int count_parents(struct commit * commit)
|
|||||||
return count;
|
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);
|
||||||
|
}
|
||||||
|
13
commit.h
13
commit.h
@ -54,4 +54,17 @@ struct commit *pop_most_recent_commit(struct commit_list **list,
|
|||||||
struct commit *pop_commit(struct commit_list **stack);
|
struct commit *pop_commit(struct commit_list **stack);
|
||||||
|
|
||||||
int count_parents(struct commit * commit);
|
int count_parents(struct commit * commit);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Performs an in-place topological sort of list supplied.
|
||||||
|
*
|
||||||
|
* Pre-conditions:
|
||||||
|
* all commits in input list and all parents of those
|
||||||
|
* commits must have object.util == NULL
|
||||||
|
*
|
||||||
|
* Post-conditions:
|
||||||
|
* invariant of resulting list is:
|
||||||
|
* a reachable from b => ord(b) < ord(a)
|
||||||
|
*/
|
||||||
|
void sort_in_topological_order(struct commit_list ** list);
|
||||||
#endif /* COMMIT_H */
|
#endif /* COMMIT_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user