~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/graph.py

  • Committer: Danny van Heumen
  • Date: 2010-03-09 21:42:11 UTC
  • mto: (4634.139.5 2.0)
  • mto: This revision was merged to the branch mainline in revision 5160.
  • Revision ID: danny@dannyvanheumen.nl-20100309214211-iqh42x6qcikgd9p3
Reverted now-useless TODO list.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007 Canonical Ltd
 
1
# Copyright (C) 2007, 2008, 2009 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
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import time
18
18
 
20
20
    debug,
21
21
    errors,
22
22
    revision,
23
 
    symbol_versioning,
24
23
    trace,
25
 
    tsort,
26
24
    )
27
 
from bzrlib.deprecated_graph import (node_distances, select_farthest)
 
25
from bzrlib.symbol_versioning import deprecated_function, deprecated_in
28
26
 
29
27
STEP_UNIQUE_SEARCHER_EVERY = 5
30
28
 
61
59
        return 'DictParentsProvider(%r)' % self.ancestry
62
60
 
63
61
    def get_parent_map(self, keys):
64
 
        """See _StackedParentsProvider.get_parent_map"""
 
62
        """See StackedParentsProvider.get_parent_map"""
65
63
        ancestry = self.ancestry
66
64
        return dict((k, ancestry[k]) for k in keys if k in ancestry)
67
65
 
68
 
 
69
 
class _StackedParentsProvider(object):
70
 
 
 
66
@deprecated_function(deprecated_in((1, 16, 0)))
 
67
def _StackedParentsProvider(*args, **kwargs):
 
68
    return StackedParentsProvider(*args, **kwargs)
 
69
 
 
70
class StackedParentsProvider(object):
 
71
    """A parents provider which stacks (or unions) multiple providers.
 
72
    
 
73
    The providers are queries in the order of the provided parent_providers.
 
74
    """
 
75
    
71
76
    def __init__(self, parent_providers):
72
77
        self._parent_providers = parent_providers
73
78
 
74
79
    def __repr__(self):
75
 
        return "_StackedParentsProvider(%r)" % self._parent_providers
 
80
        return "%s(%r)" % (self.__class__.__name__, self._parent_providers)
76
81
 
77
82
    def get_parent_map(self, keys):
78
83
        """Get a mapping of keys => parents
99
104
 
100
105
 
101
106
class CachingParentsProvider(object):
102
 
    """A parents provider which will cache the revision => parents in a dict.
103
 
 
104
 
    This is useful for providers that have an expensive lookup.
 
107
    """A parents provider which will cache the revision => parents as a dict.
 
108
 
 
109
    This is useful for providers which have an expensive look up.
 
110
 
 
111
    Either a ParentsProvider or a get_parent_map-like callback may be
 
112
    supplied.  If it provides extra un-asked-for parents, they will be cached,
 
113
    but filtered out of get_parent_map.
 
114
 
 
115
    The cache is enabled by default, but may be disabled and re-enabled.
105
116
    """
 
117
    def __init__(self, parent_provider=None, get_parent_map=None):
 
118
        """Constructor.
106
119
 
107
 
    def __init__(self, parent_provider):
 
120
        :param parent_provider: The ParentProvider to use.  It or
 
121
            get_parent_map must be supplied.
 
122
        :param get_parent_map: The get_parent_map callback to use.  It or
 
123
            parent_provider must be supplied.
 
124
        """
108
125
        self._real_provider = parent_provider
109
 
        # Theoretically we could use an LRUCache here
 
126
        if get_parent_map is None:
 
127
            self._get_parent_map = self._real_provider.get_parent_map
 
128
        else:
 
129
            self._get_parent_map = get_parent_map
 
130
        self._cache = None
 
131
        self.enable_cache(True)
 
132
 
 
133
    def __repr__(self):
 
134
        return "%s(%r)" % (self.__class__.__name__, self._real_provider)
 
135
 
 
136
    def enable_cache(self, cache_misses=True):
 
137
        """Enable cache."""
 
138
        if self._cache is not None:
 
139
            raise AssertionError('Cache enabled when already enabled.')
110
140
        self._cache = {}
111
 
 
112
 
    def __repr__(self):
113
 
        return "%s(%r)" % (self.__class__.__name__, self._real_provider)
 
141
        self._cache_misses = cache_misses
 
142
        self.missing_keys = set()
 
143
 
 
144
    def disable_cache(self):
 
145
        """Disable and clear the cache."""
 
146
        self._cache = None
 
147
        self._cache_misses = None
 
148
        self.missing_keys = set()
 
149
 
 
150
    def get_cached_map(self):
 
151
        """Return any cached get_parent_map values."""
 
152
        if self._cache is None:
 
153
            return None
 
154
        return dict(self._cache)
114
155
 
115
156
    def get_parent_map(self, keys):
116
 
        """See _StackedParentsProvider.get_parent_map"""
117
 
        needed = set()
118
 
        # If the _real_provider doesn't have a key, we cache a value of None,
119
 
        # which we then later use to realize we cannot provide a value for that
120
 
        # key.
121
 
        parent_map = {}
 
157
        """See StackedParentsProvider.get_parent_map."""
122
158
        cache = self._cache
 
159
        if cache is None:
 
160
            cache = self._get_parent_map(keys)
 
161
        else:
 
162
            needed_revisions = set(key for key in keys if key not in cache)
 
163
            # Do not ask for negatively cached keys
 
164
            needed_revisions.difference_update(self.missing_keys)
 
165
            if needed_revisions:
 
166
                parent_map = self._get_parent_map(needed_revisions)
 
167
                cache.update(parent_map)
 
168
                if self._cache_misses:
 
169
                    for key in needed_revisions:
 
170
                        if key not in parent_map:
 
171
                            self.note_missing_key(key)
 
172
        result = {}
123
173
        for key in keys:
124
 
            if key in cache:
125
 
                value = cache[key]
126
 
                if value is not None:
127
 
                    parent_map[key] = value
128
 
            else:
129
 
                needed.add(key)
 
174
            value = cache.get(key)
 
175
            if value is not None:
 
176
                result[key] = value
 
177
        return result
130
178
 
131
 
        if needed:
132
 
            new_parents = self._real_provider.get_parent_map(needed)
133
 
            cache.update(new_parents)
134
 
            parent_map.update(new_parents)
135
 
            needed.difference_update(new_parents)
136
 
            cache.update(dict.fromkeys(needed, None))
137
 
        return parent_map
 
179
    def note_missing_key(self, key):
 
180
        """Note that key is a missing key."""
 
181
        if self._cache_misses:
 
182
            self.missing_keys.add(key)
138
183
 
139
184
 
140
185
class Graph(object):
265
310
        # get there.
266
311
        return known_revnos[cur_tip] + num_steps
267
312
 
 
313
    def find_lefthand_distances(self, keys):
 
314
        """Find the distance to null for all the keys in keys.
 
315
 
 
316
        :param keys: keys to lookup.
 
317
        :return: A dict key->distance for all of keys.
 
318
        """
 
319
        # Optimisable by concurrent searching, but a random spread should get
 
320
        # some sort of hit rate.
 
321
        result = {}
 
322
        known_revnos = []
 
323
        ghosts = []
 
324
        for key in keys:
 
325
            try:
 
326
                known_revnos.append(
 
327
                    (key, self.find_distance_to_null(key, known_revnos)))
 
328
            except errors.GhostRevisionsHaveNoRevno:
 
329
                ghosts.append(key)
 
330
        for key in ghosts:
 
331
            known_revnos.append((key, -1))
 
332
        return dict(known_revnos)
 
333
 
268
334
    def find_unique_ancestors(self, unique_revision, common_revisions):
269
335
        """Find the unique ancestors for a revision versus others.
270
336
 
521
587
    def _refine_unique_nodes(self, unique_searcher, all_unique_searcher,
522
588
                             unique_tip_searchers, common_searcher):
523
589
        """Steps 5-8 of find_unique_ancestors.
524
 
        
 
590
 
525
591
        This function returns when common_searcher has stopped searching for
526
592
        more nodes.
527
593
        """
570
636
                                 all_unique_searcher._iterations)
571
637
            unique_tip_searchers = next_unique_searchers
572
638
 
573
 
    @symbol_versioning.deprecated_method(symbol_versioning.one_one)
574
 
    def get_parents(self, revisions):
575
 
        """Find revision ids of the parents of a list of revisions
576
 
 
577
 
        A list is returned of the same length as the input.  Each entry
578
 
        is a list of parent ids for the corresponding input revision.
579
 
 
580
 
        [NULL_REVISION] is used as the parent of the first user-committed
581
 
        revision.  Its parent list is empty.
582
 
 
583
 
        If the revision is not present (i.e. a ghost), None is used in place
584
 
        of the list of parents.
585
 
 
586
 
        Deprecated in bzr 1.2 - please see get_parent_map.
587
 
        """
588
 
        parents = self.get_parent_map(revisions)
589
 
        return [parents.get(r, None) for r in revisions]
590
 
 
591
639
    def get_parent_map(self, revisions):
592
640
        """Get a map of key:parent_list for revisions.
593
641
 
766
814
            common_walker.start_searching(new_common)
767
815
        return candidate_heads
768
816
 
 
817
    def find_merge_order(self, tip_revision_id, lca_revision_ids):
 
818
        """Find the order that each revision was merged into tip.
 
819
 
 
820
        This basically just walks backwards with a stack, and walks left-first
 
821
        until it finds a node to stop.
 
822
        """
 
823
        if len(lca_revision_ids) == 1:
 
824
            return list(lca_revision_ids)
 
825
        looking_for = set(lca_revision_ids)
 
826
        # TODO: Is there a way we could do this "faster" by batching up the
 
827
        # get_parent_map requests?
 
828
        # TODO: Should we also be culling the ancestry search right away? We
 
829
        # could add looking_for to the "stop" list, and walk their
 
830
        # ancestry in batched mode. The flip side is it might mean we walk a
 
831
        # lot of "stop" nodes, rather than only the minimum.
 
832
        # Then again, without it we may trace back into ancestry we could have
 
833
        # stopped early.
 
834
        stack = [tip_revision_id]
 
835
        found = []
 
836
        stop = set()
 
837
        while stack and looking_for:
 
838
            next = stack.pop()
 
839
            stop.add(next)
 
840
            if next in looking_for:
 
841
                found.append(next)
 
842
                looking_for.remove(next)
 
843
                if len(looking_for) == 1:
 
844
                    found.append(looking_for.pop())
 
845
                    break
 
846
                continue
 
847
            parent_ids = self.get_parent_map([next]).get(next, None)
 
848
            if not parent_ids: # Ghost, nothing to search here
 
849
                continue
 
850
            for parent_id in reversed(parent_ids):
 
851
                # TODO: (performance) We see the parent at this point, but we
 
852
                #       wait to mark it until later to make sure we get left
 
853
                #       parents before right parents. However, instead of
 
854
                #       waiting until we have traversed enough parents, we
 
855
                #       could instead note that we've found it, and once all
 
856
                #       parents are in the stack, just reverse iterate the
 
857
                #       stack for them.
 
858
                if parent_id not in stop:
 
859
                    # this will need to be searched
 
860
                    stack.append(parent_id)
 
861
                stop.add(parent_id)
 
862
        return found
 
863
 
769
864
    def find_unique_lca(self, left_revision, right_revision,
770
865
                        count_steps=False):
771
866
        """Find a unique LCA.
830
925
        An ancestor may sort after a descendant if the relationship is not
831
926
        visible in the supplied list of revisions.
832
927
        """
 
928
        from bzrlib import tsort
833
929
        sorter = tsort.TopoSorter(self.get_parent_map(revisions))
834
930
        return sorter.iter_topo_order()
835
931
 
843
939
        return set([candidate_descendant]) == self.heads(
844
940
            [candidate_ancestor, candidate_descendant])
845
941
 
 
942
    def is_between(self, revid, lower_bound_revid, upper_bound_revid):
 
943
        """Determine whether a revision is between two others.
 
944
 
 
945
        returns true if and only if:
 
946
        lower_bound_revid <= revid <= upper_bound_revid
 
947
        """
 
948
        return ((upper_bound_revid is None or
 
949
                    self.is_ancestor(revid, upper_bound_revid)) and
 
950
               (lower_bound_revid is None or
 
951
                    self.is_ancestor(lower_bound_revid, revid)))
 
952
 
846
953
    def _search_for_extra_common(self, common, searchers):
847
954
        """Make sure that unique nodes are genuinely unique.
848
955
 
1121
1228
 
1122
1229
    def get_result(self):
1123
1230
        """Get a SearchResult for the current state of this searcher.
1124
 
        
 
1231
 
1125
1232
        :return: A SearchResult for this search so far. The SearchResult is
1126
1233
            static - the search can be advanced and the search result will not
1127
1234
            be invalidated or altered.
1131
1238
            # exclude keys for them. However, while we could have a second
1132
1239
            # look-ahead result buffer and shuffle things around, this method
1133
1240
            # is typically only called once per search - when memoising the
1134
 
            # results of the search. 
 
1241
            # results of the search.
1135
1242
            found, ghosts, next, parents = self._do_query(self._next_query)
1136
1243
            # pretend we didn't query: perhaps we should tweak _do_query to be
1137
1244
            # entirely stateless?
1178
1285
 
1179
1286
    def next_with_ghosts(self):
1180
1287
        """Return the next found ancestors, with ghosts split out.
1181
 
        
 
1288
 
1182
1289
        Ancestors are returned in the order they are seen in a breadth-first
1183
1290
        traversal.  No ancestor will be returned more than once. Ancestors are
1184
1291
        returned only after asking for their parents, which allows us to detect
1243
1350
 
1244
1351
    def find_seen_ancestors(self, revisions):
1245
1352
        """Find ancestors of these revisions that have already been seen.
1246
 
        
 
1353
 
1247
1354
        This function generally makes the assumption that querying for the
1248
1355
        parents of a node that has already been queried is reasonably cheap.
1249
1356
        (eg, not a round trip to a remote host).
1284
1391
        Remove any of the specified revisions from the search list.
1285
1392
 
1286
1393
        None of the specified revisions are required to be present in the
1287
 
        search list.  In this case, the call is a no-op.
 
1394
        search list.
 
1395
 
 
1396
        It is okay to call stop_searching_any() for revisions which were seen
 
1397
        in previous iterations. It is the callers responsibility to call
 
1398
        find_seen_ancestors() to make sure that current search tips that are
 
1399
        ancestors of those revisions are also stopped.  All explicitly stopped
 
1400
        revisions will be excluded from the search result's get_keys(), though.
1288
1401
        """
1289
1402
        # TODO: does this help performance?
1290
1403
        # if not revisions:
1299
1412
                self._current_ghosts.intersection(revisions))
1300
1413
            self._current_present.difference_update(stopped)
1301
1414
            self._current_ghosts.difference_update(stopped)
1302
 
            # stopping 'x' should stop returning parents of 'x', but 
 
1415
            # stopping 'x' should stop returning parents of 'x', but
1303
1416
            # not if 'y' always references those same parents
1304
1417
            stop_rev_references = {}
1305
1418
            for rev in stopped_present:
1321
1434
                    stop_parents.add(rev_id)
1322
1435
            self._next_query.difference_update(stop_parents)
1323
1436
        self._stopped_keys.update(stopped)
 
1437
        self._stopped_keys.update(revisions)
1324
1438
        return stopped
1325
1439
 
1326
1440
    def start_searching(self, revisions):
1366
1480
            a SearchResult from a smart server, in which case the keys list is
1367
1481
            not necessarily immediately available.
1368
1482
        """
1369
 
        self._recipe = (start_keys, exclude_keys, key_count)
 
1483
        self._recipe = ('search', start_keys, exclude_keys, key_count)
1370
1484
        self._keys = frozenset(keys)
1371
1485
 
1372
1486
    def get_recipe(self):
1373
1487
        """Return a recipe that can be used to replay this search.
1374
 
        
 
1488
 
1375
1489
        The recipe allows reconstruction of the same results at a later date
1376
1490
        without knowing all the found keys. The essential elements are a list
1377
 
        of keys to start and and to stop at. In order to give reproducible
 
1491
        of keys to start and to stop at. In order to give reproducible
1378
1492
        results when ghosts are encountered by a search they are automatically
1379
1493
        added to the exclude list (or else ghost filling may alter the
1380
1494
        results).
1381
1495
 
1382
 
        :return: A tuple (start_keys_set, exclude_keys_set, revision_count). To
1383
 
            recreate the results of this search, create a breadth first
1384
 
            searcher on the same graph starting at start_keys. Then call next()
1385
 
            (or next_with_ghosts()) repeatedly, and on every result, call
1386
 
            stop_searching_any on any keys from the exclude_keys set. The
1387
 
            revision_count value acts as a trivial cross-check - the found
1388
 
            revisions of the new search should have as many elements as
 
1496
        :return: A tuple ('search', start_keys_set, exclude_keys_set,
 
1497
            revision_count). To recreate the results of this search, create a
 
1498
            breadth first searcher on the same graph starting at start_keys.
 
1499
            Then call next() (or next_with_ghosts()) repeatedly, and on every
 
1500
            result, call stop_searching_any on any keys from the exclude_keys
 
1501
            set. The revision_count value acts as a trivial cross-check - the
 
1502
            found revisions of the new search should have as many elements as
1389
1503
            revision_count. If it does not, then additional revisions have been
1390
1504
            ghosted since the search was executed the first time and the second
1391
1505
            time.
1399
1513
        """
1400
1514
        return self._keys
1401
1515
 
 
1516
    def is_empty(self):
 
1517
        """Return false if the search lists 1 or more revisions."""
 
1518
        return self._recipe[3] == 0
 
1519
 
 
1520
    def refine(self, seen, referenced):
 
1521
        """Create a new search by refining this search.
 
1522
 
 
1523
        :param seen: Revisions that have been satisfied.
 
1524
        :param referenced: Revision references observed while satisfying some
 
1525
            of this search.
 
1526
        """
 
1527
        start = self._recipe[1]
 
1528
        exclude = self._recipe[2]
 
1529
        count = self._recipe[3]
 
1530
        keys = self.get_keys()
 
1531
        # New heads = referenced + old heads - seen things - exclude
 
1532
        pending_refs = set(referenced)
 
1533
        pending_refs.update(start)
 
1534
        pending_refs.difference_update(seen)
 
1535
        pending_refs.difference_update(exclude)
 
1536
        # New exclude = old exclude + satisfied heads
 
1537
        seen_heads = start.intersection(seen)
 
1538
        exclude.update(seen_heads)
 
1539
        # keys gets seen removed
 
1540
        keys = keys - seen
 
1541
        # length is reduced by len(seen)
 
1542
        count -= len(seen)
 
1543
        return SearchResult(pending_refs, exclude, count, keys)
 
1544
 
 
1545
 
 
1546
class PendingAncestryResult(object):
 
1547
    """A search result that will reconstruct the ancestry for some graph heads.
 
1548
 
 
1549
    Unlike SearchResult, this doesn't hold the complete search result in
 
1550
    memory, it just holds a description of how to generate it.
 
1551
    """
 
1552
 
 
1553
    def __init__(self, heads, repo):
 
1554
        """Constructor.
 
1555
 
 
1556
        :param heads: an iterable of graph heads.
 
1557
        :param repo: a repository to use to generate the ancestry for the given
 
1558
            heads.
 
1559
        """
 
1560
        self.heads = frozenset(heads)
 
1561
        self.repo = repo
 
1562
 
 
1563
    def get_recipe(self):
 
1564
        """Return a recipe that can be used to replay this search.
 
1565
 
 
1566
        The recipe allows reconstruction of the same results at a later date.
 
1567
 
 
1568
        :seealso SearchResult.get_recipe:
 
1569
 
 
1570
        :return: A tuple ('proxy-search', start_keys_set, set(), -1)
 
1571
            To recreate this result, create a PendingAncestryResult with the
 
1572
            start_keys_set.
 
1573
        """
 
1574
        return ('proxy-search', self.heads, set(), -1)
 
1575
 
 
1576
    def get_keys(self):
 
1577
        """See SearchResult.get_keys.
 
1578
 
 
1579
        Returns all the keys for the ancestry of the heads, excluding
 
1580
        NULL_REVISION.
 
1581
        """
 
1582
        return self._get_keys(self.repo.get_graph())
 
1583
 
 
1584
    def _get_keys(self, graph):
 
1585
        NULL_REVISION = revision.NULL_REVISION
 
1586
        keys = [key for (key, parents) in graph.iter_ancestry(self.heads)
 
1587
                if key != NULL_REVISION and parents is not None]
 
1588
        return keys
 
1589
 
 
1590
    def is_empty(self):
 
1591
        """Return false if the search lists 1 or more revisions."""
 
1592
        if revision.NULL_REVISION in self.heads:
 
1593
            return len(self.heads) == 1
 
1594
        else:
 
1595
            return len(self.heads) == 0
 
1596
 
 
1597
    def refine(self, seen, referenced):
 
1598
        """Create a new search by refining this search.
 
1599
 
 
1600
        :param seen: Revisions that have been satisfied.
 
1601
        :param referenced: Revision references observed while satisfying some
 
1602
            of this search.
 
1603
        """
 
1604
        referenced = self.heads.union(referenced)
 
1605
        return PendingAncestryResult(referenced - seen, self.repo)
 
1606
 
 
1607
 
 
1608
def collapse_linear_regions(parent_map):
 
1609
    """Collapse regions of the graph that are 'linear'.
 
1610
 
 
1611
    For example::
 
1612
 
 
1613
      A:[B], B:[C]
 
1614
 
 
1615
    can be collapsed by removing B and getting::
 
1616
 
 
1617
      A:[C]
 
1618
 
 
1619
    :param parent_map: A dictionary mapping children to their parents
 
1620
    :return: Another dictionary with 'linear' chains collapsed
 
1621
    """
 
1622
    # Note: this isn't a strictly minimal collapse. For example:
 
1623
    #   A
 
1624
    #  / \
 
1625
    # B   C
 
1626
    #  \ /
 
1627
    #   D
 
1628
    #   |
 
1629
    #   E
 
1630
    # Will not have 'D' removed, even though 'E' could fit. Also:
 
1631
    #   A
 
1632
    #   |    A
 
1633
    #   B => |
 
1634
    #   |    C
 
1635
    #   C
 
1636
    # A and C are both kept because they are edges of the graph. We *could* get
 
1637
    # rid of A if we wanted.
 
1638
    #   A
 
1639
    #  / \
 
1640
    # B   C
 
1641
    # |   |
 
1642
    # D   E
 
1643
    #  \ /
 
1644
    #   F
 
1645
    # Will not have any nodes removed, even though you do have an
 
1646
    # 'uninteresting' linear D->B and E->C
 
1647
    children = {}
 
1648
    for child, parents in parent_map.iteritems():
 
1649
        children.setdefault(child, [])
 
1650
        for p in parents:
 
1651
            children.setdefault(p, []).append(child)
 
1652
 
 
1653
    orig_children = dict(children)
 
1654
    removed = set()
 
1655
    result = dict(parent_map)
 
1656
    for node in parent_map:
 
1657
        parents = result[node]
 
1658
        if len(parents) == 1:
 
1659
            parent_children = children[parents[0]]
 
1660
            if len(parent_children) != 1:
 
1661
                # This is not the only child
 
1662
                continue
 
1663
            node_children = children[node]
 
1664
            if len(node_children) != 1:
 
1665
                continue
 
1666
            child_parents = result.get(node_children[0], None)
 
1667
            if len(child_parents) != 1:
 
1668
                # This is not its only parent
 
1669
                continue
 
1670
            # The child of this node only points at it, and the parent only has
 
1671
            # this as a child. remove this node, and join the others together
 
1672
            result[node_children[0]] = parents
 
1673
            children[parents[0]] = node_children
 
1674
            del result[node]
 
1675
            del children[node]
 
1676
            removed.add(node)
 
1677
 
 
1678
    return result
 
1679
 
 
1680
 
 
1681
_counters = [0,0,0,0,0,0,0]
 
1682
try:
 
1683
    from bzrlib._known_graph_pyx import KnownGraph
 
1684
except ImportError:
 
1685
    from bzrlib._known_graph_py import KnownGraph