18
18
"""Topological sorting routines."""
24
revision as _mod_revision,
21
from bzrlib import errors
22
import bzrlib.revision as _mod_revision
28
25
__all__ = ["topo_sort", "TopoSorter", "merge_sort", "MergeSorter"]
34
31
graph -- sequence of pairs of node->parents_list.
36
The result is a list of node names, such that all parents come before their
33
The result is a list of node names, such that all parents come before
39
36
node identifiers can be any hashable object, and are typically strings.
41
38
This function has the same purpose as the TopoSorter class, but uses a
42
different algorithm to sort the graph. That means that while both return a
43
list with parents before their child nodes, the exact ordering can be
39
different algorithm to sort the graph. That means that while both return a list
40
with parents before their child nodes, the exact ordering can be different.
46
topo_sort is faster when the whole list is needed, while when iterating
47
over a part of the list, TopoSorter.iter_topo_order should be used.
42
topo_sort is faster when the whole list is needed, while when iterating over a
43
part of the list, TopoSorter.iter_topo_order should be used.
49
kg = _mod_graph.KnownGraph(dict(graph))
45
# store a dict of the graph.
47
# this is the stack storing on which the sorted nodes are pushed.
50
# count the number of children for every node in the graph
51
nchildren = dict.fromkeys(graph.iterkeys(), 0)
52
for parents in graph.itervalues():
53
for parent in parents:
54
if parent in nchildren:
55
nchildren[parent] += 1
56
# keep track of nodes without children in a separate list
57
nochildnodes = [node for (node, n) in nchildren.iteritems() if n == 0]
60
# pick a node without a child and add it to the stack.
61
node_name = nochildnodes.pop()
62
node_name_stack.append(node_name)
64
# the parents of the node lose it as a child; if it was the last
65
# child, add the parent to the list of childless nodes.
66
parents = graph.pop(node_name)
67
for parent in parents:
68
if parent in nchildren:
69
nchildren[parent] -= 1
70
if nchildren[parent] == 0:
71
nochildnodes.append(parent)
73
# if there are still nodes left in the graph,
74
# that means that there is a cycle
76
raise errors.GraphCycleError(graph)
78
# the nodes where pushed on the stack child first, so this list needs to be
79
# reversed before returning it.
80
node_name_stack.reverse()
81
return node_name_stack
53
84
class TopoSorter(object):
410
441
# seed the search with the tip of the branch
411
442
if (branch_tip is not None and
412
branch_tip != _mod_revision.NULL_REVISION and
413
branch_tip != (_mod_revision.NULL_REVISION,)):
443
branch_tip != _mod_revision.NULL_REVISION):
414
444
parents = self._graph.pop(branch_tip)
415
445
self._push_node(branch_tip, 0, parents)
459
489
left_subtree_pushed_stack_append(False)
460
490
pending_parents_stack_append(list(parents))
461
491
# as we push it, check if it is the first child
464
493
# node has parents, assign from the left most parent.
466
parent_info = revnos[parents[0]]
468
# Left-hand parent is a ghost, consider it not to exist
470
if parent_info is not None:
494
parent_info = revnos[parents[0]]
471
495
first_child = parent_info[1]
472
496
parent_info[1] = False
501
525
pending_parents_stack_pop()
503
527
parents = original_graph[node_name]
506
529
# node has parents, assign from the left most parent.
508
parent_revno = revnos[parents[0]][0]
510
# left-hand parent is a ghost, treat it as not existing
512
if parent_revno is not None:
530
parent_revno = revnos[parents[0]][0]
513
531
if not first_child:
514
532
# not the first child, make a new branch
515
533
base_revno = parent_revno[0]
553
571
if not left_subtree_pushed_stack[-1]:
554
572
# recurse depth first into the primary parent
555
573
next_node_name = pending_parents_stack[-1].pop(0)
556
is_left_subtree = True
557
left_subtree_pushed_stack[-1] = True
559
575
# place any merges in right-to-left order for scheduling
560
576
# which gives us left-to-right order after we reverse
579
594
# current search stack (but not completed or we would
580
595
# have hit the continue 4 lines up.
581
596
# this indicates a cycle.
582
if next_node_name in self._original_graph:
583
raise errors.GraphCycleError(node_name_stack)
585
# This is just a ghost parent, ignore it
597
raise errors.GraphCycleError(node_name_stack)
587
598
next_merge_depth = 0
599
if left_subtree_pushed_stack[-1]:
589
600
# a new child branch from name_stack[-1]
590
603
next_merge_depth = 0
604
left_subtree_pushed_stack[-1] = True
593
605
next_merge_depth = (
594
606
node_merge_depth_stack[-1] + next_merge_depth)
640
652
self._left_subtree_pushed_stack.append(False)
641
653
self._pending_parents_stack.append(list(parents))
642
654
# as we push it, figure out if this is the first child
655
parents = self._original_graph[node_name]
645
657
# node has parents, assign from the left most parent.
647
parent_info = self._revnos[parents[0]]
649
# Left-hand parent is a ghost, consider it not to exist
651
if parent_info is not None:
658
parent_info = self._revnos[parents[0]]
652
659
first_child = parent_info[1]
653
660
parent_info[1] = False
672
679
self._pending_parents_stack.pop()
674
681
parents = self._original_graph[node_name]
677
683
# node has parents, assign from the left most parent.
679
parent_revno = self._revnos[parents[0]][0]
681
# left-hand parent is a ghost, treat it as not existing
683
if parent_revno is not None:
684
parent_revno = self._revnos[parents[0]][0]
684
685
if not first_child:
685
686
# not the first child, make a new branch
686
687
base_revno = parent_revno[0]
697
698
# no parents, use the root sequence
698
699
root_count = self._revno_to_branch_count.get(0, 0)
699
root_count = self._revno_to_branch_count.get(0, -1)
702
701
revno = (0, root_count, 1)
705
705
self._revno_to_branch_count[0] = root_count
707
707
# store the revno for this node for future reference