~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tsort.py

  • Committer: Alexander Belchenko
  • Date: 2007-01-30 23:05:35 UTC
  • mto: This revision was merged to the branch mainline in revision 2259.
  • Revision ID: bialix@ukr.net-20070130230535-kx1rd478rtigyc3v
standalone installer: win98 support

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2008 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
18
18
"""Topological sorting routines."""
19
19
 
20
20
 
21
 
from bzrlib import errors
22
 
import bzrlib.revision as _mod_revision
 
21
import bzrlib.errors as errors
23
22
 
24
23
 
25
24
__all__ = ["topo_sort", "TopoSorter", "merge_sort", "MergeSorter"]
62
61
        """
63
62
        # a dict of the graph.
64
63
        self._graph = dict(graph)
65
 
        self._visitable = set(self._graph)
66
64
        ### if debugging:
67
65
        # self._original_graph = dict(graph)
68
66
        
122
120
                            # this parent was completed by a child on the
123
121
                            # call stack. skip it.
124
122
                            continue
125
 
                        if next_node_name not in self._visitable:
126
 
                            continue
127
123
                        # otherwise transfer it from the source graph into the
128
124
                        # top of the current depth first search stack.
129
125
                        try:
190
186
 
191
187
class MergeSorter(object):
192
188
 
193
 
    __slots__ = ['_node_name_stack',
194
 
                 '_node_merge_depth_stack',
195
 
                 '_pending_parents_stack',
196
 
                 '_first_child_stack',
197
 
                 '_left_subtree_pushed_stack',
198
 
                 '_generate_revno',
199
 
                 '_graph',
200
 
                 '_mainline_revisions',
201
 
                 '_stop_revision',
202
 
                 '_original_graph',
203
 
                 '_revnos',
204
 
                 '_revno_to_branch_count',
205
 
                 '_completed_node_names',
206
 
                 '_scheduled_nodes',
207
 
                ]
208
 
 
209
189
    def __init__(self, graph, branch_tip, mainline_revisions=None,
210
190
        generate_revno=False):
211
191
        """Merge-aware topological sorting of a graph.
240
220
           found.
241
221
         * revno_sequence: When requested this field provides a sequence of
242
222
             revision numbers for all revisions. The format is:
243
 
             (REVNO, BRANCHNUM, BRANCHREVNO). BRANCHNUM is the number of the
 
223
             REVNO[[.BRANCHREVNO.REVNO] ...]. BRANCHREVNO is the number of the
244
224
             branch that the revno is on. From left to right the REVNO numbers
245
225
             are the sequence numbers within that branch of the revision.
246
226
             For instance, the graph {A:[], B:['A'], C:['A', 'B']} will get
363
343
        self._original_graph = dict(self._graph.items())
364
344
        # we need to know the revision numbers of revisions to determine
365
345
        # the revision numbers of their descendants
366
 
        # this is a graph from node to [revno_tuple, first_child]
367
 
        # where first_child is True if no other children have seen this node
 
346
        # this is a graph from node to [revno_tuple, sequence_number]
 
347
        # where sequence is the number of branches made from the node,
368
348
        # and revno_tuple is the tuple that was assigned to the node.
369
349
        # we dont know revnos to start with, so we start it seeded with
370
 
        # [None, True]
371
 
        self._revnos = dict((revision, [None, True])
372
 
                            for revision in self._graph)
373
 
        # Each mainline revision counts how many child branches have spawned from it.
374
 
        self._revno_to_branch_count = {}
 
350
        # [None, 0]
 
351
        self._revnos = dict((revision, [None, 0]) for revision in self._graph)
 
352
        # the global implicit root node has revno 0, but we need to know
 
353
        # the sequence number for it too:
 
354
        self._root_sequence = 0
375
355
        
376
356
        # this is a stack storing the depth first search into the graph.
377
357
        self._node_name_stack = []
383
363
        self._pending_parents_stack = []
384
364
        # When we first look at a node we assign it a seqence number from its
385
365
        # leftmost parent.
386
 
        self._first_child_stack = []
 
366
        self._assigned_sequence_stack = []
387
367
        # this is a set of the nodes who have been completely analysed for fast
388
368
        # membership checking
389
369
        self._completed_node_names = set()
407
387
        self._left_subtree_pushed_stack = []
408
388
 
409
389
        # seed the search with the tip of the branch
410
 
        if (branch_tip is not None and
411
 
            branch_tip != _mod_revision.NULL_REVISION):
 
390
        if branch_tip is not None:
412
391
            parents = self._graph.pop(branch_tip)
413
392
            self._push_node(branch_tip, 0, parents)
414
393
 
425
404
        After finishing iteration the sorter is empty and you cannot continue
426
405
        iteration.
427
406
        """
428
 
        # These are safe to offload to local variables, because they are used
429
 
        # as a stack and modified in place, never assigned to.
430
 
        node_name_stack = self._node_name_stack
431
 
        node_merge_depth_stack = self._node_merge_depth_stack
432
 
        pending_parents_stack = self._pending_parents_stack
433
 
        left_subtree_pushed_stack = self._left_subtree_pushed_stack
434
 
        completed_node_names = self._completed_node_names
435
 
        scheduled_nodes = self._scheduled_nodes
436
 
 
437
 
        graph_pop = self._graph.pop
438
 
 
439
 
        def push_node(node_name, merge_depth, parents,
440
 
                      node_name_stack_append=node_name_stack.append,
441
 
                      node_merge_depth_stack_append=node_merge_depth_stack.append,
442
 
                      left_subtree_pushed_stack_append=left_subtree_pushed_stack.append,
443
 
                      pending_parents_stack_append=pending_parents_stack.append,
444
 
                      first_child_stack_append=self._first_child_stack.append,
445
 
                      revnos=self._revnos,
446
 
                      ):
447
 
            """Add node_name to the pending node stack.
448
 
 
449
 
            Names in this stack will get emitted into the output as they are popped
450
 
            off the stack.
451
 
 
452
 
            This inlines a lot of self._variable.append functions as local
453
 
            variables.
454
 
            """
455
 
            node_name_stack_append(node_name)
456
 
            node_merge_depth_stack_append(merge_depth)
457
 
            left_subtree_pushed_stack_append(False)
458
 
            pending_parents_stack_append(list(parents))
459
 
            # as we push it, check if it is the first child
460
 
            if parents:
461
 
                # node has parents, assign from the left most parent.
462
 
                parent_info = revnos[parents[0]]
463
 
                first_child = parent_info[1]
464
 
                parent_info[1] = False
465
 
            else:
466
 
                # We don't use the same algorithm here, but we need to keep the
467
 
                # stack in line
468
 
                first_child = None
469
 
            first_child_stack_append(first_child)
470
 
 
471
 
        def pop_node(node_name_stack_pop=node_name_stack.pop,
472
 
                     node_merge_depth_stack_pop=node_merge_depth_stack.pop,
473
 
                     first_child_stack_pop=self._first_child_stack.pop,
474
 
                     left_subtree_pushed_stack_pop=left_subtree_pushed_stack.pop,
475
 
                     pending_parents_stack_pop=pending_parents_stack.pop,
476
 
                     original_graph=self._original_graph,
477
 
                     revnos=self._revnos,
478
 
                     completed_node_names_add=self._completed_node_names.add,
479
 
                     scheduled_nodes_append=scheduled_nodes.append,
480
 
                     revno_to_branch_count=self._revno_to_branch_count,
481
 
                    ):
482
 
            """Pop the top node off the stack
483
 
 
484
 
            The node is appended to the sorted output.
485
 
            """
486
 
            # we are returning from the flattened call frame:
487
 
            # pop off the local variables
488
 
            node_name = node_name_stack_pop()
489
 
            merge_depth = node_merge_depth_stack_pop()
490
 
            first_child = first_child_stack_pop()
491
 
            # remove this node from the pending lists:
492
 
            left_subtree_pushed_stack_pop()
493
 
            pending_parents_stack_pop()
494
 
 
495
 
            parents = original_graph[node_name]
496
 
            if parents:
497
 
                # node has parents, assign from the left most parent.
498
 
                parent_revno = revnos[parents[0]][0]
499
 
                if not first_child:
500
 
                    # not the first child, make a new branch
501
 
                    base_revno = parent_revno[0]
502
 
                    branch_count = revno_to_branch_count.get(base_revno, 0)
503
 
                    branch_count += 1
504
 
                    revno_to_branch_count[base_revno] = branch_count
505
 
                    revno = (parent_revno[0], branch_count, 1)
506
 
                    # revno = (parent_revno[0], branch_count, parent_revno[-1]+1)
507
 
                else:
508
 
                    # as the first child, we just increase the final revision
509
 
                    # number
510
 
                    revno = parent_revno[:-1] + (parent_revno[-1] + 1,)
511
 
            else:
512
 
                # no parents, use the root sequence
513
 
                root_count = revno_to_branch_count.get(0, 0)
514
 
                if root_count:
515
 
                    revno = (0, root_count, 1)
516
 
                else:
517
 
                    revno = (1,)
518
 
                root_count += 1
519
 
                revno_to_branch_count[0] = root_count
520
 
 
521
 
            # store the revno for this node for future reference
522
 
            revnos[node_name][0] = revno
523
 
            completed_node_names_add(node_name)
524
 
            scheduled_nodes_append((node_name, merge_depth, revno))
525
 
            return node_name
526
 
 
527
 
 
528
 
        while node_name_stack:
 
407
        while self._node_name_stack:
529
408
            # loop until this call completes.
530
 
            parents_to_visit = pending_parents_stack[-1]
 
409
            parents_to_visit = self._pending_parents_stack[-1]
531
410
            # if all parents are done, the revision is done
532
411
            if not parents_to_visit:
533
412
                # append the revision to the topo sorted scheduled list:
534
413
                # all the nodes parents have been scheduled added, now
535
414
                # we can add it to the output.
536
 
                pop_node()
 
415
                self._pop_node()
537
416
            else:
538
 
                while pending_parents_stack[-1]:
539
 
                    if not left_subtree_pushed_stack[-1]:
 
417
                while self._pending_parents_stack[-1]:
 
418
                    if not self._left_subtree_pushed_stack[-1]:
540
419
                        # recurse depth first into the primary parent
541
 
                        next_node_name = pending_parents_stack[-1].pop(0)
 
420
                        next_node_name = self._pending_parents_stack[-1].pop(0)
542
421
                    else:
543
422
                        # place any merges in right-to-left order for scheduling
544
423
                        # which gives us left-to-right order after we reverse
547
426
                        # subtree rather than the left most, which will 
548
427
                        # display nicely (you get smaller trees at the top
549
428
                        # of the combined merge).
550
 
                        next_node_name = pending_parents_stack[-1].pop()
551
 
                    if next_node_name in completed_node_names:
 
429
                        next_node_name = self._pending_parents_stack[-1].pop()
 
430
                    if next_node_name in self._completed_node_names:
552
431
                        # this parent was completed by a child on the
553
432
                        # call stack. skip it.
554
433
                        continue
555
434
                    # otherwise transfer it from the source graph into the
556
435
                    # top of the current depth first search stack.
557
436
                    try:
558
 
                        parents = graph_pop(next_node_name)
 
437
                        parents = self._graph.pop(next_node_name)
559
438
                    except KeyError:
560
439
                        # if the next node is not in the source graph it has
561
440
                        # already been popped from it and placed into the
562
441
                        # current search stack (but not completed or we would
563
442
                        # have hit the continue 4 lines up.
564
443
                        # this indicates a cycle.
565
 
                        raise errors.GraphCycleError(node_name_stack)
 
444
                        raise errors.GraphCycleError(self._node_name_stack)
566
445
                    next_merge_depth = 0
567
 
                    if left_subtree_pushed_stack[-1]:
 
446
                    if self._left_subtree_pushed_stack[-1]:
568
447
                        # a new child branch from name_stack[-1]
569
448
                        next_merge_depth = 1
570
449
                    else:
571
450
                        next_merge_depth = 0
572
 
                        left_subtree_pushed_stack[-1] = True
 
451
                        self._left_subtree_pushed_stack[-1] = True
573
452
                    next_merge_depth = (
574
 
                        node_merge_depth_stack[-1] + next_merge_depth)
575
 
                    push_node(
 
453
                        self._node_merge_depth_stack[-1] + next_merge_depth)
 
454
                    self._push_node(
576
455
                        next_node_name,
577
456
                        next_merge_depth,
578
457
                        parents)
579
458
                    # and do not continue processing parents until this 'call' 
580
459
                    # has recursed.
581
460
                    break
582
 
 
583
461
        # We have scheduled the graph. Now deliver the ordered output:
584
462
        sequence_number = 0
585
 
        stop_revision = self._stop_revision
586
 
        generate_revno = self._generate_revno
587
 
        original_graph = self._original_graph
588
 
 
589
 
        while scheduled_nodes:
590
 
            node_name, merge_depth, revno = scheduled_nodes.pop()
591
 
            if node_name == stop_revision:
 
463
        while self._scheduled_nodes:
 
464
            node_name, merge_depth, revno = self._scheduled_nodes.pop()
 
465
            if node_name == self._stop_revision:
592
466
                return
593
 
            if not len(scheduled_nodes):
 
467
            if not len(self._scheduled_nodes):
594
468
                # last revision is the end of a merge
595
469
                end_of_merge = True
596
 
            elif scheduled_nodes[-1][1] < merge_depth:
 
470
            elif self._scheduled_nodes[-1][1] < merge_depth:
597
471
                # the next node is to our left
598
472
                end_of_merge = True
599
 
            elif (scheduled_nodes[-1][1] == merge_depth and
600
 
                  (scheduled_nodes[-1][0] not in
601
 
                   original_graph[node_name])):
 
473
            elif (self._scheduled_nodes[-1][1] == merge_depth and
 
474
                  (self._scheduled_nodes[-1][0] not in
 
475
                   self._original_graph[node_name])):
602
476
                # the next node was part of a multiple-merge.
603
477
                end_of_merge = True
604
478
            else:
605
479
                end_of_merge = False
606
 
            if generate_revno:
 
480
            if self._generate_revno:
607
481
                yield (sequence_number, node_name, merge_depth, revno, end_of_merge)
608
482
            else:
609
483
                yield (sequence_number, node_name, merge_depth, end_of_merge)
619
493
        self._node_merge_depth_stack.append(merge_depth)
620
494
        self._left_subtree_pushed_stack.append(False)
621
495
        self._pending_parents_stack.append(list(parents))
622
 
        # as we push it, figure out if this is the first child
 
496
        # as we push it, assign it a sequence number against its parent:
623
497
        parents = self._original_graph[node_name]
624
498
        if parents:
625
499
            # node has parents, assign from the left most parent.
626
 
            parent_info = self._revnos[parents[0]]
627
 
            first_child = parent_info[1]
628
 
            parent_info[1] = False
 
500
            parent_revno = self._revnos[parents[0]]
 
501
            sequence = parent_revno[1]
 
502
            parent_revno[1] += 1
629
503
        else:
630
 
            # We don't use the same algorithm here, but we need to keep the
631
 
            # stack in line
632
 
            first_child = None
633
 
        self._first_child_stack.append(first_child)
 
504
            # no parents, use the root sequence
 
505
            sequence = self._root_sequence
 
506
            self._root_sequence +=1
 
507
        self._assigned_sequence_stack.append(sequence)
634
508
 
635
509
    def _pop_node(self):
636
510
        """Pop the top node off the stack 
641
515
        # pop off the local variables
642
516
        node_name = self._node_name_stack.pop()
643
517
        merge_depth = self._node_merge_depth_stack.pop()
644
 
        first_child = self._first_child_stack.pop()
 
518
        sequence = self._assigned_sequence_stack.pop()
645
519
        # remove this node from the pending lists:
646
520
        self._left_subtree_pushed_stack.pop()
647
521
        self._pending_parents_stack.pop()
649
523
        parents = self._original_graph[node_name]
650
524
        if parents:
651
525
            # node has parents, assign from the left most parent.
652
 
            parent_revno = self._revnos[parents[0]][0]
653
 
            if not first_child:
 
526
            parent_revno = self._revnos[parents[0]]
 
527
            if sequence:
654
528
                # not the first child, make a new branch
655
 
                base_revno = parent_revno[0]
656
 
                branch_count = self._revno_to_branch_count.get(base_revno, 0)
657
 
                branch_count += 1
658
 
                self._revno_to_branch_count[base_revno] = branch_count
659
 
                revno = (parent_revno[0], branch_count, 1)
660
 
                # revno = (parent_revno[0], branch_count, parent_revno[-1]+1)
 
529
                revno = parent_revno[0] + (sequence, 1)
661
530
            else:
662
 
                # as the first child, we just increase the final revision
663
 
                # number
664
 
                revno = parent_revno[:-1] + (parent_revno[-1] + 1,)
 
531
                # increment the sequence number within the branch
 
532
                revno = parent_revno[0][:-1] + (parent_revno[0][-1] + 1,)
665
533
        else:
666
534
            # no parents, use the root sequence
667
 
            root_count = self._revno_to_branch_count.get(0, 0)
668
 
            if root_count:
669
 
                revno = (0, root_count, 1)
 
535
            if sequence:
 
536
                # make a parallel import revision number
 
537
                revno = (0, sequence, 1)
670
538
            else:
671
539
                revno = (1,)
672
 
            root_count += 1
673
 
            self._revno_to_branch_count[0] = root_count
674
540
 
675
541
        # store the revno for this node for future reference
676
542
        self._revnos[node_name][0] = revno