~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tsort.py

  • Committer: Robert Collins
  • Date: 2007-04-23 02:29:35 UTC
  • mfrom: (2441 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2442.
  • Revision ID: robertc@robertcollins.net-20070423022935-9hhongamvk6bfdso
Resolve conflicts with bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
"""Topological sorting routines."""
19
19
 
20
20
 
21
 
import bzrlib.errors as errors
 
21
from bzrlib import errors
22
22
 
23
23
 
24
24
__all__ = ["topo_sort", "TopoSorter", "merge_sort", "MergeSorter"]
186
186
 
187
187
class MergeSorter(object):
188
188
 
 
189
    __slots__ = ['_node_name_stack',
 
190
                 '_node_merge_depth_stack',
 
191
                 '_pending_parents_stack',
 
192
                 '_assigned_sequence_stack',
 
193
                 '_left_subtree_pushed_stack',
 
194
                 '_generate_revno',
 
195
                 '_graph',
 
196
                 '_mainline_revisions',
 
197
                 '_stop_revision',
 
198
                 '_original_graph',
 
199
                 '_revnos',
 
200
                 '_root_sequence',
 
201
                 '_completed_node_names',
 
202
                 '_scheduled_nodes',
 
203
                ]
 
204
 
189
205
    def __init__(self, graph, branch_tip, mainline_revisions=None,
190
206
        generate_revno=False):
191
207
        """Merge-aware topological sorting of a graph.
404
420
        After finishing iteration the sorter is empty and you cannot continue
405
421
        iteration.
406
422
        """
407
 
        while self._node_name_stack:
 
423
        # These are safe to offload to local variables, because they are used
 
424
        # as a stack and modified in place, never assigned to.
 
425
        node_name_stack = self._node_name_stack
 
426
        node_merge_depth_stack = self._node_merge_depth_stack
 
427
        pending_parents_stack = self._pending_parents_stack
 
428
        left_subtree_pushed_stack = self._left_subtree_pushed_stack
 
429
        completed_node_names = self._completed_node_names
 
430
        scheduled_nodes = self._scheduled_nodes
 
431
 
 
432
        graph_pop = self._graph.pop
 
433
 
 
434
        def push_node(node_name, merge_depth, parents,
 
435
                      node_name_stack_append=node_name_stack.append,
 
436
                      node_merge_depth_stack_append=node_merge_depth_stack.append,
 
437
                      left_subtree_pushed_stack_append=left_subtree_pushed_stack.append,
 
438
                      pending_parents_stack_append=pending_parents_stack.append,
 
439
                      assigned_sequence_stack_append=self._assigned_sequence_stack.append,
 
440
                      original_graph=self._original_graph,
 
441
                      revnos=self._revnos,
 
442
                      ):
 
443
            """Add node_name to the pending node stack.
 
444
 
 
445
            Names in this stack will get emitted into the output as they are popped
 
446
            off the stack.
 
447
 
 
448
            This inlines a lot of self._variable.append functions as local
 
449
            variables.
 
450
            """
 
451
            node_name_stack_append(node_name)
 
452
            node_merge_depth_stack_append(merge_depth)
 
453
            left_subtree_pushed_stack_append(False)
 
454
            pending_parents_stack_append(list(parents))
 
455
            # as we push it, assign it a sequence number against its parent:
 
456
            parents = original_graph[node_name]
 
457
            if parents:
 
458
                # node has parents, assign from the left most parent.
 
459
                parent_revno = revnos[parents[0]]
 
460
                sequence = parent_revno[1]
 
461
                parent_revno[1] += 1
 
462
            else:
 
463
                # no parents, use the root sequence
 
464
                sequence = self._root_sequence
 
465
                self._root_sequence +=1
 
466
            assigned_sequence_stack_append(sequence)
 
467
 
 
468
        def pop_node(node_name_stack_pop=node_name_stack.pop,
 
469
                     node_merge_depth_stack_pop=node_merge_depth_stack.pop,
 
470
                     assigned_sequence_stack_pop=self._assigned_sequence_stack.pop,
 
471
                     left_subtree_pushed_stack_pop=left_subtree_pushed_stack.pop,
 
472
                     pending_parents_stack_pop=pending_parents_stack.pop,
 
473
                     original_graph=self._original_graph,
 
474
                     revnos=self._revnos,
 
475
                     completed_node_names_add=self._completed_node_names.add,
 
476
                     scheduled_nodes_append=scheduled_nodes.append,
 
477
                    ):
 
478
            """Pop the top node off the stack
 
479
 
 
480
            The node is appended to the sorted output.
 
481
            """
 
482
            # we are returning from the flattened call frame:
 
483
            # pop off the local variables
 
484
            node_name = node_name_stack_pop()
 
485
            merge_depth = node_merge_depth_stack_pop()
 
486
            sequence = assigned_sequence_stack_pop()
 
487
            # remove this node from the pending lists:
 
488
            left_subtree_pushed_stack_pop()
 
489
            pending_parents_stack_pop()
 
490
 
 
491
            parents = original_graph[node_name]
 
492
            if parents:
 
493
                # node has parents, assign from the left most parent.
 
494
                parent_revno = revnos[parents[0]]
 
495
                if sequence:
 
496
                    # not the first child, make a new branch
 
497
                    revno = parent_revno[0] + (sequence, 1)
 
498
                else:
 
499
                    # increment the sequence number within the branch
 
500
                    revno = parent_revno[0][:-1] + (parent_revno[0][-1] + 1,)
 
501
            else:
 
502
                # no parents, use the root sequence
 
503
                if sequence:
 
504
                    # make a parallel import revision number
 
505
                    revno = (0, sequence, 1)
 
506
                else:
 
507
                    revno = (1,)
 
508
 
 
509
            # store the revno for this node for future reference
 
510
            revnos[node_name][0] = revno
 
511
            completed_node_names_add(node_name)
 
512
            scheduled_nodes_append((node_name, merge_depth, revno))
 
513
            return node_name
 
514
 
 
515
 
 
516
        while node_name_stack:
408
517
            # loop until this call completes.
409
 
            parents_to_visit = self._pending_parents_stack[-1]
 
518
            parents_to_visit = pending_parents_stack[-1]
410
519
            # if all parents are done, the revision is done
411
520
            if not parents_to_visit:
412
521
                # append the revision to the topo sorted scheduled list:
413
522
                # all the nodes parents have been scheduled added, now
414
523
                # we can add it to the output.
415
 
                self._pop_node()
 
524
                pop_node()
416
525
            else:
417
 
                while self._pending_parents_stack[-1]:
418
 
                    if not self._left_subtree_pushed_stack[-1]:
 
526
                while pending_parents_stack[-1]:
 
527
                    if not left_subtree_pushed_stack[-1]:
419
528
                        # recurse depth first into the primary parent
420
 
                        next_node_name = self._pending_parents_stack[-1].pop(0)
 
529
                        next_node_name = pending_parents_stack[-1].pop(0)
421
530
                    else:
422
531
                        # place any merges in right-to-left order for scheduling
423
532
                        # which gives us left-to-right order after we reverse
426
535
                        # subtree rather than the left most, which will 
427
536
                        # display nicely (you get smaller trees at the top
428
537
                        # of the combined merge).
429
 
                        next_node_name = self._pending_parents_stack[-1].pop()
430
 
                    if next_node_name in self._completed_node_names:
 
538
                        next_node_name = pending_parents_stack[-1].pop()
 
539
                    if next_node_name in completed_node_names:
431
540
                        # this parent was completed by a child on the
432
541
                        # call stack. skip it.
433
542
                        continue
434
543
                    # otherwise transfer it from the source graph into the
435
544
                    # top of the current depth first search stack.
436
545
                    try:
437
 
                        parents = self._graph.pop(next_node_name)
 
546
                        parents = graph_pop(next_node_name)
438
547
                    except KeyError:
439
548
                        # if the next node is not in the source graph it has
440
549
                        # already been popped from it and placed into the
441
550
                        # current search stack (but not completed or we would
442
551
                        # have hit the continue 4 lines up.
443
552
                        # this indicates a cycle.
444
 
                        raise errors.GraphCycleError(self._node_name_stack)
 
553
                        raise errors.GraphCycleError(node_name_stack)
445
554
                    next_merge_depth = 0
446
 
                    if self._left_subtree_pushed_stack[-1]:
 
555
                    if left_subtree_pushed_stack[-1]:
447
556
                        # a new child branch from name_stack[-1]
448
557
                        next_merge_depth = 1
449
558
                    else:
450
559
                        next_merge_depth = 0
451
 
                        self._left_subtree_pushed_stack[-1] = True
 
560
                        left_subtree_pushed_stack[-1] = True
452
561
                    next_merge_depth = (
453
 
                        self._node_merge_depth_stack[-1] + next_merge_depth)
454
 
                    self._push_node(
 
562
                        node_merge_depth_stack[-1] + next_merge_depth)
 
563
                    push_node(
455
564
                        next_node_name,
456
565
                        next_merge_depth,
457
566
                        parents)
458
567
                    # and do not continue processing parents until this 'call' 
459
568
                    # has recursed.
460
569
                    break
 
570
 
461
571
        # We have scheduled the graph. Now deliver the ordered output:
462
572
        sequence_number = 0
463
 
        while self._scheduled_nodes:
464
 
            node_name, merge_depth, revno = self._scheduled_nodes.pop()
465
 
            if node_name == self._stop_revision:
 
573
        stop_revision = self._stop_revision
 
574
        generate_revno = self._generate_revno
 
575
        original_graph = self._original_graph
 
576
 
 
577
        while scheduled_nodes:
 
578
            node_name, merge_depth, revno = scheduled_nodes.pop()
 
579
            if node_name == stop_revision:
466
580
                return
467
 
            if not len(self._scheduled_nodes):
 
581
            if not len(scheduled_nodes):
468
582
                # last revision is the end of a merge
469
583
                end_of_merge = True
470
 
            elif self._scheduled_nodes[-1][1] < merge_depth:
 
584
            elif scheduled_nodes[-1][1] < merge_depth:
471
585
                # the next node is to our left
472
586
                end_of_merge = True
473
 
            elif (self._scheduled_nodes[-1][1] == merge_depth and
474
 
                  (self._scheduled_nodes[-1][0] not in
475
 
                   self._original_graph[node_name])):
 
587
            elif (scheduled_nodes[-1][1] == merge_depth and
 
588
                  (scheduled_nodes[-1][0] not in
 
589
                   original_graph[node_name])):
476
590
                # the next node was part of a multiple-merge.
477
591
                end_of_merge = True
478
592
            else:
479
593
                end_of_merge = False
480
 
            if self._generate_revno:
 
594
            if generate_revno:
481
595
                yield (sequence_number, node_name, merge_depth, revno, end_of_merge)
482
596
            else:
483
597
                yield (sequence_number, node_name, merge_depth, end_of_merge)