~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/graph.py

(vila) Bzr config should save the changes explicitly when needed (Vincent
 Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007 Canonical Ltd
 
1
# Copyright (C) 2007-2011 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
 
 
17
from __future__ import absolute_import
16
18
 
17
19
import time
18
20
 
19
21
from bzrlib import (
20
22
    debug,
21
23
    errors,
 
24
    osutils,
22
25
    revision,
23
 
    symbol_versioning,
24
26
    trace,
25
 
    tsort,
26
27
    )
27
28
 
28
29
STEP_UNIQUE_SEARCHER_EVERY = 5
59
60
    def __repr__(self):
60
61
        return 'DictParentsProvider(%r)' % self.ancestry
61
62
 
 
63
    # Note: DictParentsProvider does not implement get_cached_parent_map
 
64
    #       Arguably, the data is clearly cached in memory. However, this class
 
65
    #       is mostly used for testing, and it keeps the tests clean to not
 
66
    #       change it.
 
67
 
62
68
    def get_parent_map(self, keys):
63
 
        """See _StackedParentsProvider.get_parent_map"""
 
69
        """See StackedParentsProvider.get_parent_map"""
64
70
        ancestry = self.ancestry
65
 
        return dict((k, ancestry[k]) for k in keys if k in ancestry)
66
 
 
67
 
 
68
 
class _StackedParentsProvider(object):
 
71
        return dict([(k, ancestry[k]) for k in keys if k in ancestry])
 
72
 
 
73
 
 
74
class StackedParentsProvider(object):
 
75
    """A parents provider which stacks (or unions) multiple providers.
 
76
 
 
77
    The providers are queries in the order of the provided parent_providers.
 
78
    """
69
79
 
70
80
    def __init__(self, parent_providers):
71
81
        self._parent_providers = parent_providers
72
82
 
73
83
    def __repr__(self):
74
 
        return "_StackedParentsProvider(%r)" % self._parent_providers
 
84
        return "%s(%r)" % (self.__class__.__name__, self._parent_providers)
75
85
 
76
86
    def get_parent_map(self, keys):
77
87
        """Get a mapping of keys => parents
88
98
        """
89
99
        found = {}
90
100
        remaining = set(keys)
 
101
        # This adds getattr() overhead to each get_parent_map call. However,
 
102
        # this is StackedParentsProvider, which means we're dealing with I/O
 
103
        # (either local indexes, or remote RPCs), so CPU overhead should be
 
104
        # minimal.
 
105
        for parents_provider in self._parent_providers:
 
106
            get_cached = getattr(parents_provider, 'get_cached_parent_map',
 
107
                                 None)
 
108
            if get_cached is None:
 
109
                continue
 
110
            new_found = get_cached(remaining)
 
111
            found.update(new_found)
 
112
            remaining.difference_update(new_found)
 
113
            if not remaining:
 
114
                break
 
115
        if not remaining:
 
116
            return found
91
117
        for parents_provider in self._parent_providers:
92
118
            new_found = parents_provider.get_parent_map(remaining)
93
119
            found.update(new_found)
121
147
            self._get_parent_map = self._real_provider.get_parent_map
122
148
        else:
123
149
            self._get_parent_map = get_parent_map
124
 
        self._cache = {}
125
 
        self._cache_misses = True
 
150
        self._cache = None
 
151
        self.enable_cache(True)
126
152
 
127
153
    def __repr__(self):
128
154
        return "%s(%r)" % (self.__class__.__name__, self._real_provider)
133
159
            raise AssertionError('Cache enabled when already enabled.')
134
160
        self._cache = {}
135
161
        self._cache_misses = cache_misses
 
162
        self.missing_keys = set()
136
163
 
137
164
    def disable_cache(self):
138
165
        """Disable and clear the cache."""
139
166
        self._cache = None
 
167
        self._cache_misses = None
 
168
        self.missing_keys = set()
140
169
 
141
170
    def get_cached_map(self):
142
171
        """Return any cached get_parent_map values."""
143
172
        if self._cache is None:
144
173
            return None
145
 
        return dict((k, v) for k, v in self._cache.items()
146
 
                    if v is not None)
 
174
        return dict(self._cache)
 
175
 
 
176
    def get_cached_parent_map(self, keys):
 
177
        """Return items from the cache.
 
178
 
 
179
        This returns the same info as get_parent_map, but explicitly does not
 
180
        invoke the supplied ParentsProvider to search for uncached values.
 
181
        """
 
182
        cache = self._cache
 
183
        if cache is None:
 
184
            return {}
 
185
        return dict([(key, cache[key]) for key in keys if key in cache])
147
186
 
148
187
    def get_parent_map(self, keys):
149
 
        """See _StackedParentsProvider.get_parent_map."""
150
 
        # Hack to build up the caching logic.
151
 
        ancestry = self._cache
152
 
        if ancestry is None:
153
 
            # Caching is disabled.
154
 
            missing_revisions = set(keys)
155
 
            ancestry = {}
 
188
        """See StackedParentsProvider.get_parent_map."""
 
189
        cache = self._cache
 
190
        if cache is None:
 
191
            cache = self._get_parent_map(keys)
156
192
        else:
157
 
            missing_revisions = set(key for key in keys if key not in ancestry)
158
 
        if missing_revisions:
159
 
            parent_map = self._get_parent_map(missing_revisions)
160
 
            ancestry.update(parent_map)
161
 
            if self._cache_misses:
162
 
                # None is never a valid parents list, so it can be used to
163
 
                # record misses.
164
 
                ancestry.update(dict((k, None) for k in missing_revisions
165
 
                                     if k not in parent_map))
166
 
        present_keys = [k for k in keys if ancestry.get(k) is not None]
167
 
        return dict((k, ancestry[k]) for k in present_keys)
 
193
            needed_revisions = set(key for key in keys if key not in cache)
 
194
            # Do not ask for negatively cached keys
 
195
            needed_revisions.difference_update(self.missing_keys)
 
196
            if needed_revisions:
 
197
                parent_map = self._get_parent_map(needed_revisions)
 
198
                cache.update(parent_map)
 
199
                if self._cache_misses:
 
200
                    for key in needed_revisions:
 
201
                        if key not in parent_map:
 
202
                            self.note_missing_key(key)
 
203
        result = {}
 
204
        for key in keys:
 
205
            value = cache.get(key)
 
206
            if value is not None:
 
207
                result[key] = value
 
208
        return result
 
209
 
 
210
    def note_missing_key(self, key):
 
211
        """Note that key is a missing key."""
 
212
        if self._cache_misses:
 
213
            self.missing_keys.add(key)
 
214
 
 
215
 
 
216
class CallableToParentsProviderAdapter(object):
 
217
    """A parents provider that adapts any callable to the parents provider API.
 
218
 
 
219
    i.e. it accepts calls to self.get_parent_map and relays them to the
 
220
    callable it was constructed with.
 
221
    """
 
222
 
 
223
    def __init__(self, a_callable):
 
224
        self.callable = a_callable
 
225
 
 
226
    def __repr__(self):
 
227
        return "%s(%r)" % (self.__class__.__name__, self.callable)
 
228
 
 
229
    def get_parent_map(self, keys):
 
230
        return self.callable(keys)
168
231
 
169
232
 
170
233
class Graph(object):
221
284
        common ancestor of all border ancestors, because this shows that it
222
285
        cannot be a descendant of any border ancestor.
223
286
 
224
 
        The scaling of this operation should be proportional to
 
287
        The scaling of this operation should be proportional to:
 
288
 
225
289
        1. The number of uncommon ancestors
226
290
        2. The number of border ancestors
227
291
        3. The length of the shortest path between a border ancestor and an
242
306
        right = searchers[1].seen
243
307
        return (left.difference(right), right.difference(left))
244
308
 
 
309
    def find_descendants(self, old_key, new_key):
 
310
        """Find descendants of old_key that are ancestors of new_key."""
 
311
        child_map = self.get_child_map(self._find_descendant_ancestors(
 
312
            old_key, new_key))
 
313
        graph = Graph(DictParentsProvider(child_map))
 
314
        searcher = graph._make_breadth_first_searcher([old_key])
 
315
        list(searcher)
 
316
        return searcher.seen
 
317
 
 
318
    def _find_descendant_ancestors(self, old_key, new_key):
 
319
        """Find ancestors of new_key that may be descendants of old_key."""
 
320
        stop = self._make_breadth_first_searcher([old_key])
 
321
        descendants = self._make_breadth_first_searcher([new_key])
 
322
        for revisions in descendants:
 
323
            old_stop = stop.seen.intersection(revisions)
 
324
            descendants.stop_searching_any(old_stop)
 
325
            seen_stop = descendants.find_seen_ancestors(stop.step())
 
326
            descendants.stop_searching_any(seen_stop)
 
327
        return descendants.seen.difference(stop.seen)
 
328
 
 
329
    def get_child_map(self, keys):
 
330
        """Get a mapping from parents to children of the specified keys.
 
331
 
 
332
        This is simply the inversion of get_parent_map.  Only supplied keys
 
333
        will be discovered as children.
 
334
        :return: a dict of key:child_list for keys.
 
335
        """
 
336
        parent_map = self._parents_provider.get_parent_map(keys)
 
337
        parent_child = {}
 
338
        for child, parents in sorted(parent_map.items()):
 
339
            for parent in parents:
 
340
                parent_child.setdefault(parent, []).append(child)
 
341
        return parent_child
 
342
 
245
343
    def find_distance_to_null(self, target_revision_id, known_revision_ids):
246
344
        """Find the left-hand distance to the NULL_REVISION.
247
345
 
295
393
        # get there.
296
394
        return known_revnos[cur_tip] + num_steps
297
395
 
 
396
    def find_lefthand_distances(self, keys):
 
397
        """Find the distance to null for all the keys in keys.
 
398
 
 
399
        :param keys: keys to lookup.
 
400
        :return: A dict key->distance for all of keys.
 
401
        """
 
402
        # Optimisable by concurrent searching, but a random spread should get
 
403
        # some sort of hit rate.
 
404
        result = {}
 
405
        known_revnos = []
 
406
        ghosts = []
 
407
        for key in keys:
 
408
            try:
 
409
                known_revnos.append(
 
410
                    (key, self.find_distance_to_null(key, known_revnos)))
 
411
            except errors.GhostRevisionsHaveNoRevno:
 
412
                ghosts.append(key)
 
413
        for key in ghosts:
 
414
            known_revnos.append((key, -1))
 
415
        return dict(known_revnos)
 
416
 
298
417
    def find_unique_ancestors(self, unique_revision, common_revisions):
299
418
        """Find the unique ancestors for a revision versus others.
300
419
 
304
423
 
305
424
        :param unique_revision: The revision_id whose ancestry we are
306
425
            interested in.
307
 
            XXX: Would this API be better if we allowed multiple revisions on
308
 
                 to be searched here?
 
426
            (XXX: Would this API be better if we allowed multiple revisions on
 
427
            to be searched here?)
309
428
        :param common_revisions: Revision_ids of ancestries to exclude.
310
429
        :return: A set of revisions in the ancestry of unique_revision
311
430
        """
551
670
    def _refine_unique_nodes(self, unique_searcher, all_unique_searcher,
552
671
                             unique_tip_searchers, common_searcher):
553
672
        """Steps 5-8 of find_unique_ancestors.
554
 
        
 
673
 
555
674
        This function returns when common_searcher has stopped searching for
556
675
        more nodes.
557
676
        """
600
719
                                 all_unique_searcher._iterations)
601
720
            unique_tip_searchers = next_unique_searchers
602
721
 
603
 
    @symbol_versioning.deprecated_method(symbol_versioning.one_one)
604
 
    def get_parents(self, revisions):
605
 
        """Find revision ids of the parents of a list of revisions
606
 
 
607
 
        A list is returned of the same length as the input.  Each entry
608
 
        is a list of parent ids for the corresponding input revision.
609
 
 
610
 
        [NULL_REVISION] is used as the parent of the first user-committed
611
 
        revision.  Its parent list is empty.
612
 
 
613
 
        If the revision is not present (i.e. a ghost), None is used in place
614
 
        of the list of parents.
615
 
 
616
 
        Deprecated in bzr 1.2 - please see get_parent_map.
617
 
        """
618
 
        parents = self.get_parent_map(revisions)
619
 
        return [parents.get(r, None) for r in revisions]
620
 
 
621
722
    def get_parent_map(self, revisions):
622
723
        """Get a map of key:parent_list for revisions.
623
724
 
843
944
                stop.add(parent_id)
844
945
        return found
845
946
 
 
947
    def find_lefthand_merger(self, merged_key, tip_key):
 
948
        """Find the first lefthand ancestor of tip_key that merged merged_key.
 
949
 
 
950
        We do this by first finding the descendants of merged_key, then
 
951
        walking through the lefthand ancestry of tip_key until we find a key
 
952
        that doesn't descend from merged_key.  Its child is the key that
 
953
        merged merged_key.
 
954
 
 
955
        :return: The first lefthand ancestor of tip_key to merge merged_key.
 
956
            merged_key if it is a lefthand ancestor of tip_key.
 
957
            None if no ancestor of tip_key merged merged_key.
 
958
        """
 
959
        descendants = self.find_descendants(merged_key, tip_key)
 
960
        candidate_iterator = self.iter_lefthand_ancestry(tip_key)
 
961
        last_candidate = None
 
962
        for candidate in candidate_iterator:
 
963
            if candidate not in descendants:
 
964
                return last_candidate
 
965
            last_candidate = candidate
 
966
 
846
967
    def find_unique_lca(self, left_revision, right_revision,
847
968
                        count_steps=False):
848
969
        """Find a unique LCA.
900
1021
                yield (ghost, None)
901
1022
            pending = next_pending
902
1023
 
 
1024
    def iter_lefthand_ancestry(self, start_key, stop_keys=None):
 
1025
        if stop_keys is None:
 
1026
            stop_keys = ()
 
1027
        next_key = start_key
 
1028
        def get_parents(key):
 
1029
            try:
 
1030
                return self._parents_provider.get_parent_map([key])[key]
 
1031
            except KeyError:
 
1032
                raise errors.RevisionNotPresent(next_key, self)
 
1033
        while True:
 
1034
            if next_key in stop_keys:
 
1035
                return
 
1036
            parents = get_parents(next_key)
 
1037
            yield next_key
 
1038
            if len(parents) == 0:
 
1039
                return
 
1040
            else:
 
1041
                next_key = parents[0]
 
1042
 
903
1043
    def iter_topo_order(self, revisions):
904
1044
        """Iterate through the input revisions in topological order.
905
1045
 
907
1047
        An ancestor may sort after a descendant if the relationship is not
908
1048
        visible in the supplied list of revisions.
909
1049
        """
 
1050
        from bzrlib import tsort
910
1051
        sorter = tsort.TopoSorter(self.get_parent_map(revisions))
911
1052
        return sorter.iter_topo_order()
912
1053
 
1207
1348
        return ('_BreadthFirstSearcher(iterations=%d, %s,'
1208
1349
                ' seen=%r)' % (self._iterations, search, list(self.seen)))
1209
1350
 
1210
 
    def get_result(self):
1211
 
        """Get a SearchResult for the current state of this searcher.
1212
 
        
1213
 
        :return: A SearchResult for this search so far. The SearchResult is
1214
 
            static - the search can be advanced and the search result will not
1215
 
            be invalidated or altered.
 
1351
    def get_state(self):
 
1352
        """Get the current state of this searcher.
 
1353
 
 
1354
        :return: Tuple with started keys, excludes and included keys
1216
1355
        """
1217
1356
        if self._returning == 'next':
1218
1357
            # We have to know the current nodes children to be able to list the
1219
1358
            # exclude keys for them. However, while we could have a second
1220
1359
            # look-ahead result buffer and shuffle things around, this method
1221
1360
            # is typically only called once per search - when memoising the
1222
 
            # results of the search. 
 
1361
            # results of the search.
1223
1362
            found, ghosts, next, parents = self._do_query(self._next_query)
1224
1363
            # pretend we didn't query: perhaps we should tweak _do_query to be
1225
1364
            # entirely stateless?
1229
1368
            next_query = self._next_query
1230
1369
        excludes = self._stopped_keys.union(next_query)
1231
1370
        included_keys = self.seen.difference(excludes)
1232
 
        return SearchResult(self._started_keys, excludes, len(included_keys),
 
1371
        return self._started_keys, excludes, included_keys
 
1372
 
 
1373
    def _get_result(self):
 
1374
        """Get a SearchResult for the current state of this searcher.
 
1375
 
 
1376
        :return: A SearchResult for this search so far. The SearchResult is
 
1377
            static - the search can be advanced and the search result will not
 
1378
            be invalidated or altered.
 
1379
        """
 
1380
        from bzrlib.vf_search import SearchResult
 
1381
        (started_keys, excludes, included_keys) = self.get_state()
 
1382
        return SearchResult(started_keys, excludes, len(included_keys),
1233
1383
            included_keys)
1234
1384
 
1235
1385
    def step(self):
1266
1416
 
1267
1417
    def next_with_ghosts(self):
1268
1418
        """Return the next found ancestors, with ghosts split out.
1269
 
        
 
1419
 
1270
1420
        Ancestors are returned in the order they are seen in a breadth-first
1271
1421
        traversal.  No ancestor will be returned more than once. Ancestors are
1272
1422
        returned only after asking for their parents, which allows us to detect
1312
1462
        parents_of_found = set()
1313
1463
        # revisions may contain nodes that point to other nodes in revisions:
1314
1464
        # we want to filter them out.
1315
 
        self.seen.update(revisions)
 
1465
        seen = self.seen
 
1466
        seen.update(revisions)
1316
1467
        parent_map = self._parents_provider.get_parent_map(revisions)
1317
1468
        found_revisions.update(parent_map)
1318
1469
        for rev_id, parents in parent_map.iteritems():
1319
1470
            if parents is None:
1320
1471
                continue
1321
 
            new_found_parents = [p for p in parents if p not in self.seen]
 
1472
            new_found_parents = [p for p in parents if p not in seen]
1322
1473
            if new_found_parents:
1323
1474
                # Calling set.update() with an empty generator is actually
1324
1475
                # rather expensive.
1331
1482
 
1332
1483
    def find_seen_ancestors(self, revisions):
1333
1484
        """Find ancestors of these revisions that have already been seen.
1334
 
        
 
1485
 
1335
1486
        This function generally makes the assumption that querying for the
1336
1487
        parents of a node that has already been queried is reasonably cheap.
1337
1488
        (eg, not a round trip to a remote host).
1393
1544
                self._current_ghosts.intersection(revisions))
1394
1545
            self._current_present.difference_update(stopped)
1395
1546
            self._current_ghosts.difference_update(stopped)
1396
 
            # stopping 'x' should stop returning parents of 'x', but 
 
1547
            # stopping 'x' should stop returning parents of 'x', but
1397
1548
            # not if 'y' always references those same parents
1398
1549
            stop_rev_references = {}
1399
1550
            for rev in stopped_present:
1415
1566
                    stop_parents.add(rev_id)
1416
1567
            self._next_query.difference_update(stop_parents)
1417
1568
        self._stopped_keys.update(stopped)
1418
 
        self._stopped_keys.update(revisions - set([revision.NULL_REVISION]))
 
1569
        self._stopped_keys.update(revisions)
1419
1570
        return stopped
1420
1571
 
1421
1572
    def start_searching(self, revisions):
1443
1594
            return revs, ghosts
1444
1595
 
1445
1596
 
1446
 
class SearchResult(object):
1447
 
    """The result of a breadth first search.
1448
 
 
1449
 
    A SearchResult provides the ability to reconstruct the search or access a
1450
 
    set of the keys the search found.
1451
 
    """
1452
 
 
1453
 
    def __init__(self, start_keys, exclude_keys, key_count, keys):
1454
 
        """Create a SearchResult.
1455
 
 
1456
 
        :param start_keys: The keys the search started at.
1457
 
        :param exclude_keys: The keys the search excludes.
1458
 
        :param key_count: The total number of keys (from start to but not
1459
 
            including exclude).
1460
 
        :param keys: The keys the search found. Note that in future we may get
1461
 
            a SearchResult from a smart server, in which case the keys list is
1462
 
            not necessarily immediately available.
1463
 
        """
1464
 
        self._recipe = (start_keys, exclude_keys, key_count)
1465
 
        self._keys = frozenset(keys)
1466
 
 
1467
 
    def get_recipe(self):
1468
 
        """Return a recipe that can be used to replay this search.
1469
 
        
1470
 
        The recipe allows reconstruction of the same results at a later date
1471
 
        without knowing all the found keys. The essential elements are a list
1472
 
        of keys to start and and to stop at. In order to give reproducible
1473
 
        results when ghosts are encountered by a search they are automatically
1474
 
        added to the exclude list (or else ghost filling may alter the
1475
 
        results).
1476
 
 
1477
 
        :return: A tuple (start_keys_set, exclude_keys_set, revision_count). To
1478
 
            recreate the results of this search, create a breadth first
1479
 
            searcher on the same graph starting at start_keys. Then call next()
1480
 
            (or next_with_ghosts()) repeatedly, and on every result, call
1481
 
            stop_searching_any on any keys from the exclude_keys set. The
1482
 
            revision_count value acts as a trivial cross-check - the found
1483
 
            revisions of the new search should have as many elements as
1484
 
            revision_count. If it does not, then additional revisions have been
1485
 
            ghosted since the search was executed the first time and the second
1486
 
            time.
1487
 
        """
1488
 
        return self._recipe
1489
 
 
1490
 
    def get_keys(self):
1491
 
        """Return the keys found in this search.
1492
 
 
1493
 
        :return: A set of keys.
1494
 
        """
1495
 
        return self._keys
 
1597
def invert_parent_map(parent_map):
 
1598
    """Given a map from child => parents, create a map of parent=>children"""
 
1599
    child_map = {}
 
1600
    for child, parents in parent_map.iteritems():
 
1601
        for p in parents:
 
1602
            # Any given parent is likely to have only a small handful
 
1603
            # of children, many will have only one. So we avoid mem overhead of
 
1604
            # a list, in exchange for extra copying of tuples
 
1605
            if p not in child_map:
 
1606
                child_map[p] = (child,)
 
1607
            else:
 
1608
                child_map[p] = child_map[p] + (child,)
 
1609
    return child_map
1496
1610
 
1497
1611
 
1498
1612
def collapse_linear_regions(parent_map):
1566
1680
            removed.add(node)
1567
1681
 
1568
1682
    return result
 
1683
 
 
1684
 
 
1685
class GraphThunkIdsToKeys(object):
 
1686
    """Forwards calls about 'ids' to be about keys internally."""
 
1687
 
 
1688
    def __init__(self, graph):
 
1689
        self._graph = graph
 
1690
 
 
1691
    def topo_sort(self):
 
1692
        return [r for (r,) in self._graph.topo_sort()]
 
1693
 
 
1694
    def heads(self, ids):
 
1695
        """See Graph.heads()"""
 
1696
        as_keys = [(i,) for i in ids]
 
1697
        head_keys = self._graph.heads(as_keys)
 
1698
        return set([h[0] for h in head_keys])
 
1699
 
 
1700
    def merge_sort(self, tip_revision):
 
1701
        nodes = self._graph.merge_sort((tip_revision,))
 
1702
        for node in nodes:
 
1703
            node.key = node.key[0]
 
1704
        return nodes
 
1705
 
 
1706
    def add_node(self, revision, parents):
 
1707
        self._graph.add_node((revision,), [(p,) for p in parents])
 
1708
 
 
1709
 
 
1710
_counters = [0,0,0,0,0,0,0]
 
1711
try:
 
1712
    from bzrlib._known_graph_pyx import KnownGraph
 
1713
except ImportError, e:
 
1714
    osutils.failed_to_load_extension(e)
 
1715
    from bzrlib._known_graph_py import KnownGraph