~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tsort.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-06-12 02:17:42 UTC
  • mfrom: (2521.1.1 56322)
  • Revision ID: pqm@pqm.ubuntu.com-20070612021742-uetsy3g747iq3xkk
mergeĀ initĀ --create-prefix

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# (C) 2005, 2006 Canonical Limited.
 
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
 
import bzrlib.errors as errors
 
21
from bzrlib import errors
22
22
 
23
23
 
24
24
__all__ = ["topo_sort", "TopoSorter", "merge_sort", "MergeSorter"]
159
159
        return node_name
160
160
 
161
161
 
162
 
def merge_sort(graph, branch_tip, mainline_revisions=None):
 
162
def merge_sort(graph, branch_tip, mainline_revisions=None, generate_revno=False):
163
163
    """Topological sort a graph which groups merges.
164
164
 
165
165
    :param graph: sequence of pairs of node->parents_list.
174
174
                               old revision listed in the mainline revisions
175
175
                               list.
176
176
                               The order for this parameter is oldest-first.
177
 
 
178
 
    The result is a list of node names, such that all parents come before
179
 
    their children.
180
 
 
 
177
    :param generate_revno: Optional parameter controlling the generation of
 
178
        revision number sequences in the output. See the output description of
 
179
        the MergeSorter docstring for details.
 
180
    :result: See the MergeSorter docstring for details.
181
181
    node identifiers can be any hashable object, and are typically strings.
182
182
    """
183
 
    return MergeSorter(graph, branch_tip, mainline_revisions).sorted()
 
183
    return MergeSorter(graph, branch_tip, mainline_revisions,
 
184
        generate_revno).sorted()
184
185
 
185
186
 
186
187
class MergeSorter(object):
187
188
 
188
 
    def __init__(self, graph, branch_tip, mainline_revisions=None):
 
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
 
 
205
    def __init__(self, graph, branch_tip, mainline_revisions=None,
 
206
        generate_revno=False):
189
207
        """Merge-aware topological sorting of a graph.
190
208
    
191
209
        :param graph: sequence of pairs of node_name->parent_names_list.
204
222
                               old revision listed in the mainline revisions
205
223
                               list.
206
224
                               The order for this parameter is oldest-first.
 
225
        :param generate_revno: Optional parameter controlling the generation of
 
226
            revision number sequences in the output. See the output description
 
227
            for more details.
 
228
 
 
229
        The result is a list sorted so that all parents come before
 
230
        their children. Each element of the list is a tuple containing:
 
231
        (sequence_number, node_name, merge_depth, end_of_merge)
 
232
         * sequence_number: The sequence of this row in the output. Useful for 
 
233
           GUIs.
 
234
         * node_name: The node name: opaque text to the merge routine.
 
235
         * merge_depth: How many levels of merging deep this node has been
 
236
           found.
 
237
         * revno_sequence: When requested this field provides a sequence of
 
238
             revision numbers for all revisions. The format is:
 
239
             REVNO[[.BRANCHREVNO.REVNO] ...]. BRANCHREVNO is the number of the
 
240
             branch that the revno is on. From left to right the REVNO numbers
 
241
             are the sequence numbers within that branch of the revision.
 
242
             For instance, the graph {A:[], B:['A'], C:['A', 'B']} will get
 
243
             the following revno_sequences assigned: A:(1,), B:(1,1,1), C:(2,).
 
244
             This should be read as 'A is the first commit in the trunk',
 
245
             'B is the first commit on the first branch made from A', 'C is the
 
246
             second commit in the trunk'.
 
247
         * end_of_merge: When True the next node is part of a different merge.
207
248
 
208
249
        
209
250
        node identifiers can be any hashable object, and are typically strings.
285
326
             XXXX revisit when awake. ddaa asks about the relevance of each one
286
327
             - maybe more than one parent is relevant
287
328
        """
 
329
        self._generate_revno = generate_revno
288
330
        # a dict of the graph.
289
331
        self._graph = dict(graph)
290
332
        # if there is an explicit mainline, alter the graph to match. This is
315
357
        # which requires the parents to be accessible: its easier for now
316
358
        # to just keep the original graph around.
317
359
        self._original_graph = dict(self._graph.items())
 
360
        # we need to know the revision numbers of revisions to determine
 
361
        # the revision numbers of their descendants
 
362
        # this is a graph from node to [revno_tuple, sequence_number]
 
363
        # where sequence is the number of branches made from the node,
 
364
        # and revno_tuple is the tuple that was assigned to the node.
 
365
        # we dont know revnos to start with, so we start it seeded with
 
366
        # [None, 0]
 
367
        self._revnos = dict((revision, [None, 0]) for revision in self._graph)
 
368
        # the global implicit root node has revno 0, but we need to know
 
369
        # the sequence number for it too:
 
370
        self._root_sequence = 0
318
371
        
319
372
        # this is a stack storing the depth first search into the graph.
320
373
        self._node_name_stack = []
324
377
        # stack stores the parents we have not yet checked for the node at the 
325
378
        # matching depth in _node_name_stack
326
379
        self._pending_parents_stack = []
 
380
        # When we first look at a node we assign it a seqence number from its
 
381
        # leftmost parent.
 
382
        self._assigned_sequence_stack = []
327
383
        # this is a set of the nodes who have been completely analysed for fast
328
384
        # membership checking
329
385
        self._completed_node_names = set()
343
399
        # This records for each node when we have processed its left most 
344
400
        # unmerged subtree. After this subtree is scheduled, all other subtrees
345
401
        # have their merge depth increased by one from this nodes merge depth.
346
 
        self._left_subtree_done_stack = []
 
402
        # it contains tuples - name, merge_depth
 
403
        self._left_subtree_pushed_stack = []
347
404
 
348
405
        # seed the search with the tip of the branch
349
406
        if branch_tip is not None:
363
420
        After finishing iteration the sorter is empty and you cannot continue
364
421
        iteration.
365
422
        """
366
 
        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:
367
517
            # loop until this call completes.
368
 
            parents_to_visit = self._pending_parents_stack[-1]
 
518
            parents_to_visit = pending_parents_stack[-1]
369
519
            # if all parents are done, the revision is done
370
520
            if not parents_to_visit:
371
521
                # append the revision to the topo sorted scheduled list:
372
522
                # all the nodes parents have been scheduled added, now
373
523
                # we can add it to the output.
374
 
                self._pop_node()
 
524
                pop_node()
375
525
            else:
376
 
                while self._pending_parents_stack[-1]:
377
 
                    if not self._left_subtree_done_stack[-1]:
 
526
                while pending_parents_stack[-1]:
 
527
                    if not left_subtree_pushed_stack[-1]:
378
528
                        # recurse depth first into the primary parent
379
 
                        next_node_name = self._pending_parents_stack[-1].pop(0)
 
529
                        next_node_name = pending_parents_stack[-1].pop(0)
380
530
                    else:
381
531
                        # place any merges in right-to-left order for scheduling
382
532
                        # which gives us left-to-right order after we reverse
385
535
                        # subtree rather than the left most, which will 
386
536
                        # display nicely (you get smaller trees at the top
387
537
                        # of the combined merge).
388
 
                        next_node_name = self._pending_parents_stack[-1].pop()
389
 
                    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:
390
540
                        # this parent was completed by a child on the
391
541
                        # call stack. skip it.
392
542
                        continue
393
543
                    # otherwise transfer it from the source graph into the
394
544
                    # top of the current depth first search stack.
395
545
                    try:
396
 
                        parents = self._graph.pop(next_node_name)
 
546
                        parents = graph_pop(next_node_name)
397
547
                    except KeyError:
398
548
                        # if the next node is not in the source graph it has
399
549
                        # already been popped from it and placed into the
400
550
                        # current search stack (but not completed or we would
401
551
                        # have hit the continue 4 lines up.
402
552
                        # this indicates a cycle.
403
 
                        raise errors.GraphCycleError(self._node_name_stack)
 
553
                        raise errors.GraphCycleError(node_name_stack)
404
554
                    next_merge_depth = 0
405
 
                    if self._left_subtree_done_stack[-1]:
 
555
                    if left_subtree_pushed_stack[-1]:
 
556
                        # a new child branch from name_stack[-1]
406
557
                        next_merge_depth = 1
407
558
                    else:
408
559
                        next_merge_depth = 0
409
 
                        self._left_subtree_done_stack[-1] = True
 
560
                        left_subtree_pushed_stack[-1] = True
410
561
                    next_merge_depth = (
411
 
                        self._node_merge_depth_stack[-1] + next_merge_depth)
412
 
                    self._push_node(
 
562
                        node_merge_depth_stack[-1] + next_merge_depth)
 
563
                    push_node(
413
564
                        next_node_name,
414
565
                        next_merge_depth,
415
566
                        parents)
416
567
                    # and do not continue processing parents until this 'call' 
417
568
                    # has recursed.
418
569
                    break
 
570
 
419
571
        # We have scheduled the graph. Now deliver the ordered output:
420
572
        sequence_number = 0
421
 
        while self._scheduled_nodes:
422
 
            node_name, merge_depth = self._scheduled_nodes.pop()
423
 
            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:
424
580
                return
425
 
            if not len(self._scheduled_nodes):
 
581
            if not len(scheduled_nodes):
 
582
                # last revision is the end of a merge
426
583
                end_of_merge = True
427
 
            elif self._scheduled_nodes[-1][1] < merge_depth:
 
584
            elif scheduled_nodes[-1][1] < merge_depth:
428
585
                # the next node is to our left
429
586
                end_of_merge = True
430
 
            elif (self._scheduled_nodes[-1][1] == merge_depth and
431
 
                  (self._scheduled_nodes[-1][0] not in
432
 
                   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])):
433
590
                # the next node was part of a multiple-merge.
434
591
                end_of_merge = True
435
592
            else:
436
593
                end_of_merge = False
437
 
            yield (sequence_number, node_name, merge_depth, end_of_merge)
 
594
            if generate_revno:
 
595
                yield (sequence_number, node_name, merge_depth, revno, end_of_merge)
 
596
            else:
 
597
                yield (sequence_number, node_name, merge_depth, end_of_merge)
438
598
            sequence_number += 1
439
599
 
440
600
    def _push_node(self, node_name, merge_depth, parents):
445
605
        """
446
606
        self._node_name_stack.append(node_name)
447
607
        self._node_merge_depth_stack.append(merge_depth)
448
 
        self._left_subtree_done_stack.append(False)
 
608
        self._left_subtree_pushed_stack.append(False)
449
609
        self._pending_parents_stack.append(list(parents))
 
610
        # as we push it, assign it a sequence number against its parent:
 
611
        parents = self._original_graph[node_name]
 
612
        if parents:
 
613
            # node has parents, assign from the left most parent.
 
614
            parent_revno = self._revnos[parents[0]]
 
615
            sequence = parent_revno[1]
 
616
            parent_revno[1] += 1
 
617
        else:
 
618
            # no parents, use the root sequence
 
619
            sequence = self._root_sequence
 
620
            self._root_sequence +=1
 
621
        self._assigned_sequence_stack.append(sequence)
450
622
 
451
623
    def _pop_node(self):
452
624
        """Pop the top node off the stack 
457
629
        # pop off the local variables
458
630
        node_name = self._node_name_stack.pop()
459
631
        merge_depth = self._node_merge_depth_stack.pop()
460
 
        self._left_subtree_done_stack.pop()
 
632
        sequence = self._assigned_sequence_stack.pop()
 
633
        # remove this node from the pending lists:
 
634
        self._left_subtree_pushed_stack.pop()
461
635
        self._pending_parents_stack.pop()
462
636
 
 
637
        parents = self._original_graph[node_name]
 
638
        if parents:
 
639
            # node has parents, assign from the left most parent.
 
640
            parent_revno = self._revnos[parents[0]]
 
641
            if sequence:
 
642
                # not the first child, make a new branch
 
643
                revno = parent_revno[0] + (sequence, 1)
 
644
            else:
 
645
                # increment the sequence number within the branch
 
646
                revno = parent_revno[0][:-1] + (parent_revno[0][-1] + 1,)
 
647
        else:
 
648
            # no parents, use the root sequence
 
649
            if sequence:
 
650
                # make a parallel import revision number
 
651
                revno = (0, sequence, 1)
 
652
            else:
 
653
                revno = (1,)
 
654
 
 
655
        # store the revno for this node for future reference
 
656
        self._revnos[node_name][0] = revno
463
657
        self._completed_node_names.add(node_name)
464
 
        self._scheduled_nodes.append((node_name, merge_depth))
 
658
        self._scheduled_nodes.append((node_name, merge_depth, self._revnos[node_name][0]))
465
659
        return node_name