~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/graph.py

  • Committer: John Arbash Meinel
  • Date: 2008-09-09 15:09:12 UTC
  • mto: This revision was merged to the branch mainline in revision 3699.
  • Revision ID: john@arbash-meinel.com-20080909150912-wyttm8he1zsls2ck
Use the right timing function on win32

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2011 Canonical Ltd
 
1
# Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
import time
18
18
 
19
19
from bzrlib import (
20
20
    debug,
21
21
    errors,
22
 
    osutils,
23
22
    revision,
 
23
    symbol_versioning,
24
24
    trace,
 
25
    tsort,
25
26
    )
 
27
from bzrlib.deprecated_graph import (node_distances, select_farthest)
26
28
 
27
29
STEP_UNIQUE_SEARCHER_EVERY = 5
28
30
 
58
60
    def __repr__(self):
59
61
        return 'DictParentsProvider(%r)' % self.ancestry
60
62
 
61
 
    # Note: DictParentsProvider does not implement get_cached_parent_map
62
 
    #       Arguably, the data is clearly cached in memory. However, this class
63
 
    #       is mostly used for testing, and it keeps the tests clean to not
64
 
    #       change it.
65
 
 
66
63
    def get_parent_map(self, keys):
67
 
        """See StackedParentsProvider.get_parent_map"""
 
64
        """See _StackedParentsProvider.get_parent_map"""
68
65
        ancestry = self.ancestry
69
 
        return dict([(k, ancestry[k]) for k in keys if k in ancestry])
70
 
 
71
 
 
72
 
class StackedParentsProvider(object):
73
 
    """A parents provider which stacks (or unions) multiple providers.
74
 
 
75
 
    The providers are queries in the order of the provided parent_providers.
76
 
    """
 
66
        return dict((k, ancestry[k]) for k in keys if k in ancestry)
 
67
 
 
68
 
 
69
class _StackedParentsProvider(object):
77
70
 
78
71
    def __init__(self, parent_providers):
79
72
        self._parent_providers = parent_providers
80
73
 
81
74
    def __repr__(self):
82
 
        return "%s(%r)" % (self.__class__.__name__, self._parent_providers)
 
75
        return "_StackedParentsProvider(%r)" % self._parent_providers
83
76
 
84
77
    def get_parent_map(self, keys):
85
78
        """Get a mapping of keys => parents
96
89
        """
97
90
        found = {}
98
91
        remaining = set(keys)
99
 
        # This adds getattr() overhead to each get_parent_map call. However,
100
 
        # this is StackedParentsProvider, which means we're dealing with I/O
101
 
        # (either local indexes, or remote RPCs), so CPU overhead should be
102
 
        # minimal.
103
 
        for parents_provider in self._parent_providers:
104
 
            get_cached = getattr(parents_provider, 'get_cached_parent_map',
105
 
                                 None)
106
 
            if get_cached is None:
107
 
                continue
108
 
            new_found = get_cached(remaining)
109
 
            found.update(new_found)
110
 
            remaining.difference_update(new_found)
111
 
            if not remaining:
112
 
                break
113
 
        if not remaining:
114
 
            return found
115
92
        for parents_provider in self._parent_providers:
116
93
            new_found = parents_provider.get_parent_map(remaining)
117
94
            found.update(new_found)
122
99
 
123
100
 
124
101
class CachingParentsProvider(object):
125
 
    """A parents provider which will cache the revision => parents as a dict.
126
 
 
127
 
    This is useful for providers which have an expensive look up.
128
 
 
129
 
    Either a ParentsProvider or a get_parent_map-like callback may be
130
 
    supplied.  If it provides extra un-asked-for parents, they will be cached,
131
 
    but filtered out of get_parent_map.
132
 
 
133
 
    The cache is enabled by default, but may be disabled and re-enabled.
 
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.
134
105
    """
135
 
    def __init__(self, parent_provider=None, get_parent_map=None):
136
 
        """Constructor.
137
106
 
138
 
        :param parent_provider: The ParentProvider to use.  It or
139
 
            get_parent_map must be supplied.
140
 
        :param get_parent_map: The get_parent_map callback to use.  It or
141
 
            parent_provider must be supplied.
142
 
        """
 
107
    def __init__(self, parent_provider):
143
108
        self._real_provider = parent_provider
144
 
        if get_parent_map is None:
145
 
            self._get_parent_map = self._real_provider.get_parent_map
146
 
        else:
147
 
            self._get_parent_map = get_parent_map
148
 
        self._cache = None
149
 
        self.enable_cache(True)
 
109
        # Theoretically we could use an LRUCache here
 
110
        self._cache = {}
150
111
 
151
112
    def __repr__(self):
152
113
        return "%s(%r)" % (self.__class__.__name__, self._real_provider)
153
114
 
154
 
    def enable_cache(self, cache_misses=True):
155
 
        """Enable cache."""
156
 
        if self._cache is not None:
157
 
            raise AssertionError('Cache enabled when already enabled.')
158
 
        self._cache = {}
159
 
        self._cache_misses = cache_misses
160
 
        self.missing_keys = set()
161
 
 
162
 
    def disable_cache(self):
163
 
        """Disable and clear the cache."""
164
 
        self._cache = None
165
 
        self._cache_misses = None
166
 
        self.missing_keys = set()
167
 
 
168
 
    def get_cached_map(self):
169
 
        """Return any cached get_parent_map values."""
170
 
        if self._cache is None:
171
 
            return None
172
 
        return dict(self._cache)
173
 
 
174
 
    def get_cached_parent_map(self, keys):
175
 
        """Return items from the cache.
176
 
 
177
 
        This returns the same info as get_parent_map, but explicitly does not
178
 
        invoke the supplied ParentsProvider to search for uncached values.
179
 
        """
180
 
        cache = self._cache
181
 
        if cache is None:
182
 
            return {}
183
 
        return dict([(key, cache[key]) for key in keys if key in cache])
184
 
 
185
115
    def get_parent_map(self, keys):
186
 
        """See StackedParentsProvider.get_parent_map."""
 
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 = {}
187
122
        cache = self._cache
188
 
        if cache is None:
189
 
            cache = self._get_parent_map(keys)
190
 
        else:
191
 
            needed_revisions = set(key for key in keys if key not in cache)
192
 
            # Do not ask for negatively cached keys
193
 
            needed_revisions.difference_update(self.missing_keys)
194
 
            if needed_revisions:
195
 
                parent_map = self._get_parent_map(needed_revisions)
196
 
                cache.update(parent_map)
197
 
                if self._cache_misses:
198
 
                    for key in needed_revisions:
199
 
                        if key not in parent_map:
200
 
                            self.note_missing_key(key)
201
 
        result = {}
202
123
        for key in keys:
203
 
            value = cache.get(key)
204
 
            if value is not None:
205
 
                result[key] = value
206
 
        return result
207
 
 
208
 
    def note_missing_key(self, key):
209
 
        """Note that key is a missing key."""
210
 
        if self._cache_misses:
211
 
            self.missing_keys.add(key)
212
 
 
213
 
 
214
 
class CallableToParentsProviderAdapter(object):
215
 
    """A parents provider that adapts any callable to the parents provider API.
216
 
 
217
 
    i.e. it accepts calls to self.get_parent_map and relays them to the
218
 
    callable it was constructed with.
219
 
    """
220
 
 
221
 
    def __init__(self, a_callable):
222
 
        self.callable = a_callable
223
 
 
224
 
    def __repr__(self):
225
 
        return "%s(%r)" % (self.__class__.__name__, self.callable)
226
 
 
227
 
    def get_parent_map(self, keys):
228
 
        return self.callable(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)
 
130
 
 
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
229
138
 
230
139
 
231
140
class Graph(object):
282
191
        common ancestor of all border ancestors, because this shows that it
283
192
        cannot be a descendant of any border ancestor.
284
193
 
285
 
        The scaling of this operation should be proportional to:
286
 
 
 
194
        The scaling of this operation should be proportional to
287
195
        1. The number of uncommon ancestors
288
196
        2. The number of border ancestors
289
197
        3. The length of the shortest path between a border ancestor and an
304
212
        right = searchers[1].seen
305
213
        return (left.difference(right), right.difference(left))
306
214
 
307
 
    def find_descendants(self, old_key, new_key):
308
 
        """Find descendants of old_key that are ancestors of new_key."""
309
 
        child_map = self.get_child_map(self._find_descendant_ancestors(
310
 
            old_key, new_key))
311
 
        graph = Graph(DictParentsProvider(child_map))
312
 
        searcher = graph._make_breadth_first_searcher([old_key])
313
 
        list(searcher)
314
 
        return searcher.seen
315
 
 
316
 
    def _find_descendant_ancestors(self, old_key, new_key):
317
 
        """Find ancestors of new_key that may be descendants of old_key."""
318
 
        stop = self._make_breadth_first_searcher([old_key])
319
 
        descendants = self._make_breadth_first_searcher([new_key])
320
 
        for revisions in descendants:
321
 
            old_stop = stop.seen.intersection(revisions)
322
 
            descendants.stop_searching_any(old_stop)
323
 
            seen_stop = descendants.find_seen_ancestors(stop.step())
324
 
            descendants.stop_searching_any(seen_stop)
325
 
        return descendants.seen.difference(stop.seen)
326
 
 
327
 
    def get_child_map(self, keys):
328
 
        """Get a mapping from parents to children of the specified keys.
329
 
 
330
 
        This is simply the inversion of get_parent_map.  Only supplied keys
331
 
        will be discovered as children.
332
 
        :return: a dict of key:child_list for keys.
333
 
        """
334
 
        parent_map = self._parents_provider.get_parent_map(keys)
335
 
        parent_child = {}
336
 
        for child, parents in sorted(parent_map.items()):
337
 
            for parent in parents:
338
 
                parent_child.setdefault(parent, []).append(child)
339
 
        return parent_child
340
 
 
341
215
    def find_distance_to_null(self, target_revision_id, known_revision_ids):
342
216
        """Find the left-hand distance to the NULL_REVISION.
343
217
 
391
265
        # get there.
392
266
        return known_revnos[cur_tip] + num_steps
393
267
 
394
 
    def find_lefthand_distances(self, keys):
395
 
        """Find the distance to null for all the keys in keys.
396
 
 
397
 
        :param keys: keys to lookup.
398
 
        :return: A dict key->distance for all of keys.
399
 
        """
400
 
        # Optimisable by concurrent searching, but a random spread should get
401
 
        # some sort of hit rate.
402
 
        result = {}
403
 
        known_revnos = []
404
 
        ghosts = []
405
 
        for key in keys:
406
 
            try:
407
 
                known_revnos.append(
408
 
                    (key, self.find_distance_to_null(key, known_revnos)))
409
 
            except errors.GhostRevisionsHaveNoRevno:
410
 
                ghosts.append(key)
411
 
        for key in ghosts:
412
 
            known_revnos.append((key, -1))
413
 
        return dict(known_revnos)
414
 
 
415
268
    def find_unique_ancestors(self, unique_revision, common_revisions):
416
269
        """Find the unique ancestors for a revision versus others.
417
270
 
421
274
 
422
275
        :param unique_revision: The revision_id whose ancestry we are
423
276
            interested in.
424
 
            (XXX: Would this API be better if we allowed multiple revisions on
425
 
            to be searched here?)
 
277
            XXX: Would this API be better if we allowed multiple revisions on
 
278
                 to be searched here?
426
279
        :param common_revisions: Revision_ids of ancestries to exclude.
427
280
        :return: A set of revisions in the ancestry of unique_revision
428
281
        """
668
521
    def _refine_unique_nodes(self, unique_searcher, all_unique_searcher,
669
522
                             unique_tip_searchers, common_searcher):
670
523
        """Steps 5-8 of find_unique_ancestors.
671
 
 
 
524
        
672
525
        This function returns when common_searcher has stopped searching for
673
526
        more nodes.
674
527
        """
717
570
                                 all_unique_searcher._iterations)
718
571
            unique_tip_searchers = next_unique_searchers
719
572
 
 
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
 
720
591
    def get_parent_map(self, revisions):
721
592
        """Get a map of key:parent_list for revisions.
722
593
 
942
813
                stop.add(parent_id)
943
814
        return found
944
815
 
945
 
    def find_lefthand_merger(self, merged_key, tip_key):
946
 
        """Find the first lefthand ancestor of tip_key that merged merged_key.
947
 
 
948
 
        We do this by first finding the descendants of merged_key, then
949
 
        walking through the lefthand ancestry of tip_key until we find a key
950
 
        that doesn't descend from merged_key.  Its child is the key that
951
 
        merged merged_key.
952
 
 
953
 
        :return: The first lefthand ancestor of tip_key to merge merged_key.
954
 
            merged_key if it is a lefthand ancestor of tip_key.
955
 
            None if no ancestor of tip_key merged merged_key.
956
 
        """
957
 
        descendants = self.find_descendants(merged_key, tip_key)
958
 
        candidate_iterator = self.iter_lefthand_ancestry(tip_key)
959
 
        last_candidate = None
960
 
        for candidate in candidate_iterator:
961
 
            if candidate not in descendants:
962
 
                return last_candidate
963
 
            last_candidate = candidate
964
 
 
965
816
    def find_unique_lca(self, left_revision, right_revision,
966
817
                        count_steps=False):
967
818
        """Find a unique LCA.
1019
870
                yield (ghost, None)
1020
871
            pending = next_pending
1021
872
 
1022
 
    def iter_lefthand_ancestry(self, start_key, stop_keys=None):
1023
 
        if stop_keys is None:
1024
 
            stop_keys = ()
1025
 
        next_key = start_key
1026
 
        def get_parents(key):
1027
 
            try:
1028
 
                return self._parents_provider.get_parent_map([key])[key]
1029
 
            except KeyError:
1030
 
                raise errors.RevisionNotPresent(next_key, self)
1031
 
        while True:
1032
 
            if next_key in stop_keys:
1033
 
                return
1034
 
            parents = get_parents(next_key)
1035
 
            yield next_key
1036
 
            if len(parents) == 0:
1037
 
                return
1038
 
            else:
1039
 
                next_key = parents[0]
1040
 
 
1041
873
    def iter_topo_order(self, revisions):
1042
874
        """Iterate through the input revisions in topological order.
1043
875
 
1045
877
        An ancestor may sort after a descendant if the relationship is not
1046
878
        visible in the supplied list of revisions.
1047
879
        """
1048
 
        from bzrlib import tsort
1049
880
        sorter = tsort.TopoSorter(self.get_parent_map(revisions))
1050
881
        return sorter.iter_topo_order()
1051
882
 
1059
890
        return set([candidate_descendant]) == self.heads(
1060
891
            [candidate_ancestor, candidate_descendant])
1061
892
 
1062
 
    def is_between(self, revid, lower_bound_revid, upper_bound_revid):
1063
 
        """Determine whether a revision is between two others.
1064
 
 
1065
 
        returns true if and only if:
1066
 
        lower_bound_revid <= revid <= upper_bound_revid
1067
 
        """
1068
 
        return ((upper_bound_revid is None or
1069
 
                    self.is_ancestor(revid, upper_bound_revid)) and
1070
 
               (lower_bound_revid is None or
1071
 
                    self.is_ancestor(lower_bound_revid, revid)))
1072
 
 
1073
893
    def _search_for_extra_common(self, common, searchers):
1074
894
        """Make sure that unique nodes are genuinely unique.
1075
895
 
1348
1168
 
1349
1169
    def get_result(self):
1350
1170
        """Get a SearchResult for the current state of this searcher.
1351
 
 
 
1171
        
1352
1172
        :return: A SearchResult for this search so far. The SearchResult is
1353
1173
            static - the search can be advanced and the search result will not
1354
1174
            be invalidated or altered.
1358
1178
            # exclude keys for them. However, while we could have a second
1359
1179
            # look-ahead result buffer and shuffle things around, this method
1360
1180
            # is typically only called once per search - when memoising the
1361
 
            # results of the search.
 
1181
            # results of the search. 
1362
1182
            found, ghosts, next, parents = self._do_query(self._next_query)
1363
1183
            # pretend we didn't query: perhaps we should tweak _do_query to be
1364
1184
            # entirely stateless?
1405
1225
 
1406
1226
    def next_with_ghosts(self):
1407
1227
        """Return the next found ancestors, with ghosts split out.
1408
 
 
 
1228
        
1409
1229
        Ancestors are returned in the order they are seen in a breadth-first
1410
1230
        traversal.  No ancestor will be returned more than once. Ancestors are
1411
1231
        returned only after asking for their parents, which allows us to detect
1451
1271
        parents_of_found = set()
1452
1272
        # revisions may contain nodes that point to other nodes in revisions:
1453
1273
        # we want to filter them out.
1454
 
        seen = self.seen
1455
 
        seen.update(revisions)
 
1274
        self.seen.update(revisions)
1456
1275
        parent_map = self._parents_provider.get_parent_map(revisions)
1457
1276
        found_revisions.update(parent_map)
1458
1277
        for rev_id, parents in parent_map.iteritems():
1459
1278
            if parents is None:
1460
1279
                continue
1461
 
            new_found_parents = [p for p in parents if p not in seen]
 
1280
            new_found_parents = [p for p in parents if p not in self.seen]
1462
1281
            if new_found_parents:
1463
1282
                # Calling set.update() with an empty generator is actually
1464
1283
                # rather expensive.
1471
1290
 
1472
1291
    def find_seen_ancestors(self, revisions):
1473
1292
        """Find ancestors of these revisions that have already been seen.
1474
 
 
 
1293
        
1475
1294
        This function generally makes the assumption that querying for the
1476
1295
        parents of a node that has already been queried is reasonably cheap.
1477
1296
        (eg, not a round trip to a remote host).
1512
1331
        Remove any of the specified revisions from the search list.
1513
1332
 
1514
1333
        None of the specified revisions are required to be present in the
1515
 
        search list.
1516
 
 
1517
 
        It is okay to call stop_searching_any() for revisions which were seen
1518
 
        in previous iterations. It is the callers responsibility to call
1519
 
        find_seen_ancestors() to make sure that current search tips that are
1520
 
        ancestors of those revisions are also stopped.  All explicitly stopped
1521
 
        revisions will be excluded from the search result's get_keys(), though.
 
1334
        search list.  In this case, the call is a no-op.
1522
1335
        """
1523
1336
        # TODO: does this help performance?
1524
1337
        # if not revisions:
1533
1346
                self._current_ghosts.intersection(revisions))
1534
1347
            self._current_present.difference_update(stopped)
1535
1348
            self._current_ghosts.difference_update(stopped)
1536
 
            # stopping 'x' should stop returning parents of 'x', but
 
1349
            # stopping 'x' should stop returning parents of 'x', but 
1537
1350
            # not if 'y' always references those same parents
1538
1351
            stop_rev_references = {}
1539
1352
            for rev in stopped_present:
1555
1368
                    stop_parents.add(rev_id)
1556
1369
            self._next_query.difference_update(stop_parents)
1557
1370
        self._stopped_keys.update(stopped)
1558
 
        self._stopped_keys.update(revisions)
1559
1371
        return stopped
1560
1372
 
1561
1373
    def start_searching(self, revisions):
1583
1395
            return revs, ghosts
1584
1396
 
1585
1397
 
1586
 
class AbstractSearchResult(object):
1587
 
    """The result of a search, describing a set of keys.
1588
 
    
1589
 
    Search results are typically used as the 'fetch_spec' parameter when
1590
 
    fetching revisions.
1591
 
 
1592
 
    :seealso: AbstractSearch
1593
 
    """
1594
 
 
1595
 
    def get_recipe(self):
1596
 
        """Return a recipe that can be used to replay this search.
1597
 
 
1598
 
        The recipe allows reconstruction of the same results at a later date.
1599
 
 
1600
 
        :return: A tuple of `(search_kind_str, *details)`.  The details vary by
1601
 
            kind of search result.
1602
 
        """
1603
 
        raise NotImplementedError(self.get_recipe)
1604
 
 
1605
 
    def get_network_struct(self):
1606
 
        """Return a tuple that can be transmitted via the HPSS protocol."""
1607
 
        raise NotImplementedError(self.get_network_struct)
1608
 
 
1609
 
    def get_keys(self):
1610
 
        """Return the keys found in this search.
1611
 
 
1612
 
        :return: A set of keys.
1613
 
        """
1614
 
        raise NotImplementedError(self.get_keys)
1615
 
 
1616
 
    def is_empty(self):
1617
 
        """Return false if the search lists 1 or more revisions."""
1618
 
        raise NotImplementedError(self.is_empty)
1619
 
 
1620
 
    def refine(self, seen, referenced):
1621
 
        """Create a new search by refining this search.
1622
 
 
1623
 
        :param seen: Revisions that have been satisfied.
1624
 
        :param referenced: Revision references observed while satisfying some
1625
 
            of this search.
1626
 
        :return: A search result.
1627
 
        """
1628
 
        raise NotImplementedError(self.refine)
1629
 
 
1630
 
 
1631
 
class AbstractSearch(object):
1632
 
    """A search that can be executed, producing a search result.
1633
 
 
1634
 
    :seealso: AbstractSearchResult
1635
 
    """
1636
 
 
1637
 
    def execute(self):
1638
 
        """Construct a network-ready search result from this search description.
1639
 
 
1640
 
        This may take some time to search repositories, etc.
1641
 
 
1642
 
        :return: A search result (an object that implements
1643
 
            AbstractSearchResult's API).
1644
 
        """
1645
 
        raise NotImplementedError(self.execute)
1646
 
 
1647
 
 
1648
 
class SearchResult(AbstractSearchResult):
 
1398
class SearchResult(object):
1649
1399
    """The result of a breadth first search.
1650
1400
 
1651
1401
    A SearchResult provides the ability to reconstruct the search or access a
1663
1413
            a SearchResult from a smart server, in which case the keys list is
1664
1414
            not necessarily immediately available.
1665
1415
        """
1666
 
        self._recipe = ('search', start_keys, exclude_keys, key_count)
 
1416
        self._recipe = (start_keys, exclude_keys, key_count)
1667
1417
        self._keys = frozenset(keys)
1668
1418
 
1669
 
    def __repr__(self):
1670
 
        kind, start_keys, exclude_keys, key_count = self._recipe
1671
 
        if len(start_keys) > 5:
1672
 
            start_keys_repr = repr(list(start_keys)[:5])[:-1] + ', ...]'
1673
 
        else:
1674
 
            start_keys_repr = repr(start_keys)
1675
 
        if len(exclude_keys) > 5:
1676
 
            exclude_keys_repr = repr(list(exclude_keys)[:5])[:-1] + ', ...]'
1677
 
        else:
1678
 
            exclude_keys_repr = repr(exclude_keys)
1679
 
        return '<%s %s:(%s, %s, %d)>' % (self.__class__.__name__,
1680
 
            kind, start_keys_repr, exclude_keys_repr, key_count)
1681
 
 
1682
1419
    def get_recipe(self):
1683
1420
        """Return a recipe that can be used to replay this search.
1684
 
 
 
1421
        
1685
1422
        The recipe allows reconstruction of the same results at a later date
1686
1423
        without knowing all the found keys. The essential elements are a list
1687
 
        of keys to start and to stop at. In order to give reproducible
 
1424
        of keys to start and and to stop at. In order to give reproducible
1688
1425
        results when ghosts are encountered by a search they are automatically
1689
1426
        added to the exclude list (or else ghost filling may alter the
1690
1427
        results).
1691
1428
 
1692
 
        :return: A tuple ('search', start_keys_set, exclude_keys_set,
1693
 
            revision_count). To recreate the results of this search, create a
1694
 
            breadth first searcher on the same graph starting at start_keys.
1695
 
            Then call next() (or next_with_ghosts()) repeatedly, and on every
1696
 
            result, call stop_searching_any on any keys from the exclude_keys
1697
 
            set. The revision_count value acts as a trivial cross-check - the
1698
 
            found revisions of the new search should have as many elements as
 
1429
        :return: A tuple (start_keys_set, exclude_keys_set, revision_count). To
 
1430
            recreate the results of this search, create a breadth first
 
1431
            searcher on the same graph starting at start_keys. Then call next()
 
1432
            (or next_with_ghosts()) repeatedly, and on every result, call
 
1433
            stop_searching_any on any keys from the exclude_keys set. The
 
1434
            revision_count value acts as a trivial cross-check - the found
 
1435
            revisions of the new search should have as many elements as
1699
1436
            revision_count. If it does not, then additional revisions have been
1700
1437
            ghosted since the search was executed the first time and the second
1701
1438
            time.
1702
1439
        """
1703
1440
        return self._recipe
1704
1441
 
1705
 
    def get_network_struct(self):
1706
 
        start_keys = ' '.join(self._recipe[1])
1707
 
        stop_keys = ' '.join(self._recipe[2])
1708
 
        count = str(self._recipe[3])
1709
 
        return (self._recipe[0], '\n'.join((start_keys, stop_keys, count)))
1710
 
 
1711
1442
    def get_keys(self):
1712
1443
        """Return the keys found in this search.
1713
1444
 
1715
1446
        """
1716
1447
        return self._keys
1717
1448
 
1718
 
    def is_empty(self):
1719
 
        """Return false if the search lists 1 or more revisions."""
1720
 
        return self._recipe[3] == 0
1721
 
 
1722
 
    def refine(self, seen, referenced):
1723
 
        """Create a new search by refining this search.
1724
 
 
1725
 
        :param seen: Revisions that have been satisfied.
1726
 
        :param referenced: Revision references observed while satisfying some
1727
 
            of this search.
1728
 
        """
1729
 
        start = self._recipe[1]
1730
 
        exclude = self._recipe[2]
1731
 
        count = self._recipe[3]
1732
 
        keys = self.get_keys()
1733
 
        # New heads = referenced + old heads - seen things - exclude
1734
 
        pending_refs = set(referenced)
1735
 
        pending_refs.update(start)
1736
 
        pending_refs.difference_update(seen)
1737
 
        pending_refs.difference_update(exclude)
1738
 
        # New exclude = old exclude + satisfied heads
1739
 
        seen_heads = start.intersection(seen)
1740
 
        exclude.update(seen_heads)
1741
 
        # keys gets seen removed
1742
 
        keys = keys - seen
1743
 
        # length is reduced by len(seen)
1744
 
        count -= len(seen)
1745
 
        return SearchResult(pending_refs, exclude, count, keys)
1746
 
 
1747
 
 
1748
 
class PendingAncestryResult(AbstractSearchResult):
1749
 
    """A search result that will reconstruct the ancestry for some graph heads.
1750
 
 
1751
 
    Unlike SearchResult, this doesn't hold the complete search result in
1752
 
    memory, it just holds a description of how to generate it.
1753
 
    """
1754
 
 
1755
 
    def __init__(self, heads, repo):
1756
 
        """Constructor.
1757
 
 
1758
 
        :param heads: an iterable of graph heads.
1759
 
        :param repo: a repository to use to generate the ancestry for the given
1760
 
            heads.
1761
 
        """
1762
 
        self.heads = frozenset(heads)
1763
 
        self.repo = repo
1764
 
 
1765
 
    def __repr__(self):
1766
 
        if len(self.heads) > 5:
1767
 
            heads_repr = repr(list(self.heads)[:5])[:-1]
1768
 
            heads_repr += ', <%d more>...]' % (len(self.heads) - 5,)
1769
 
        else:
1770
 
            heads_repr = repr(self.heads)
1771
 
        return '<%s heads:%s repo:%r>' % (
1772
 
            self.__class__.__name__, heads_repr, self.repo)
1773
 
 
1774
 
    def get_recipe(self):
1775
 
        """Return a recipe that can be used to replay this search.
1776
 
 
1777
 
        The recipe allows reconstruction of the same results at a later date.
1778
 
 
1779
 
        :seealso SearchResult.get_recipe:
1780
 
 
1781
 
        :return: A tuple ('proxy-search', start_keys_set, set(), -1)
1782
 
            To recreate this result, create a PendingAncestryResult with the
1783
 
            start_keys_set.
1784
 
        """
1785
 
        return ('proxy-search', self.heads, set(), -1)
1786
 
 
1787
 
    def get_network_struct(self):
1788
 
        parts = ['ancestry-of']
1789
 
        parts.extend(self.heads)
1790
 
        return parts
1791
 
 
1792
 
    def get_keys(self):
1793
 
        """See SearchResult.get_keys.
1794
 
 
1795
 
        Returns all the keys for the ancestry of the heads, excluding
1796
 
        NULL_REVISION.
1797
 
        """
1798
 
        return self._get_keys(self.repo.get_graph())
1799
 
 
1800
 
    def _get_keys(self, graph):
1801
 
        NULL_REVISION = revision.NULL_REVISION
1802
 
        keys = [key for (key, parents) in graph.iter_ancestry(self.heads)
1803
 
                if key != NULL_REVISION and parents is not None]
1804
 
        return keys
1805
 
 
1806
 
    def is_empty(self):
1807
 
        """Return false if the search lists 1 or more revisions."""
1808
 
        if revision.NULL_REVISION in self.heads:
1809
 
            return len(self.heads) == 1
1810
 
        else:
1811
 
            return len(self.heads) == 0
1812
 
 
1813
 
    def refine(self, seen, referenced):
1814
 
        """Create a new search by refining this search.
1815
 
 
1816
 
        :param seen: Revisions that have been satisfied.
1817
 
        :param referenced: Revision references observed while satisfying some
1818
 
            of this search.
1819
 
        """
1820
 
        referenced = self.heads.union(referenced)
1821
 
        return PendingAncestryResult(referenced - seen, self.repo)
1822
 
 
1823
 
 
1824
 
class EmptySearchResult(AbstractSearchResult):
1825
 
    """An empty search result."""
1826
 
 
1827
 
    def is_empty(self):
1828
 
        return True
1829
 
    
1830
 
 
1831
 
class EverythingResult(AbstractSearchResult):
1832
 
    """A search result that simply requests everything in the repository."""
1833
 
 
1834
 
    def __init__(self, repo):
1835
 
        self._repo = repo
1836
 
 
1837
 
    def __repr__(self):
1838
 
        return '%s(%r)' % (self.__class__.__name__, self._repo)
1839
 
 
1840
 
    def get_recipe(self):
1841
 
        raise NotImplementedError(self.get_recipe)
1842
 
 
1843
 
    def get_network_struct(self):
1844
 
        return ('everything',)
1845
 
 
1846
 
    def get_keys(self):
1847
 
        if 'evil' in debug.debug_flags:
1848
 
            from bzrlib import remote
1849
 
            if isinstance(self._repo, remote.RemoteRepository):
1850
 
                # warn developers (not users) not to do this
1851
 
                trace.mutter_callsite(
1852
 
                    2, "EverythingResult(RemoteRepository).get_keys() is slow.")
1853
 
        return self._repo.all_revision_ids()
1854
 
 
1855
 
    def is_empty(self):
1856
 
        # It's ok for this to wrongly return False: the worst that can happen
1857
 
        # is that RemoteStreamSource will initiate a get_stream on an empty
1858
 
        # repository.  And almost all repositories are non-empty.
1859
 
        return False
1860
 
 
1861
 
    def refine(self, seen, referenced):
1862
 
        heads = set(self._repo.all_revision_ids())
1863
 
        heads.difference_update(seen)
1864
 
        heads.update(referenced)
1865
 
        return PendingAncestryResult(heads, self._repo)
1866
 
 
1867
 
 
1868
 
class EverythingNotInOther(AbstractSearch):
1869
 
    """Find all revisions in that are in one repo but not the other."""
1870
 
 
1871
 
    def __init__(self, to_repo, from_repo, find_ghosts=False):
1872
 
        self.to_repo = to_repo
1873
 
        self.from_repo = from_repo
1874
 
        self.find_ghosts = find_ghosts
1875
 
 
1876
 
    def execute(self):
1877
 
        return self.to_repo.search_missing_revision_ids(
1878
 
            self.from_repo, find_ghosts=self.find_ghosts)
1879
 
 
1880
 
 
1881
 
class NotInOtherForRevs(AbstractSearch):
1882
 
    """Find all revisions missing in one repo for a some specific heads."""
1883
 
 
1884
 
    def __init__(self, to_repo, from_repo, required_ids, if_present_ids=None,
1885
 
            find_ghosts=False, limit=None):
1886
 
        """Constructor.
1887
 
 
1888
 
        :param required_ids: revision IDs of heads that must be found, or else
1889
 
            the search will fail with NoSuchRevision.  All revisions in their
1890
 
            ancestry not already in the other repository will be included in
1891
 
            the search result.
1892
 
        :param if_present_ids: revision IDs of heads that may be absent in the
1893
 
            source repository.  If present, then their ancestry not already
1894
 
            found in other will be included in the search result.
1895
 
        :param limit: maximum number of revisions to fetch
1896
 
        """
1897
 
        self.to_repo = to_repo
1898
 
        self.from_repo = from_repo
1899
 
        self.find_ghosts = find_ghosts
1900
 
        self.required_ids = required_ids
1901
 
        self.if_present_ids = if_present_ids
1902
 
        self.limit = limit
1903
 
 
1904
 
    def __repr__(self):
1905
 
        if len(self.required_ids) > 5:
1906
 
            reqd_revs_repr = repr(list(self.required_ids)[:5])[:-1] + ', ...]'
1907
 
        else:
1908
 
            reqd_revs_repr = repr(self.required_ids)
1909
 
        if self.if_present_ids and len(self.if_present_ids) > 5:
1910
 
            ifp_revs_repr = repr(list(self.if_present_ids)[:5])[:-1] + ', ...]'
1911
 
        else:
1912
 
            ifp_revs_repr = repr(self.if_present_ids)
1913
 
 
1914
 
        return ("<%s from:%r to:%r find_ghosts:%r req'd:%r if-present:%r"
1915
 
                "limit:%r>") % (
1916
 
                self.__class__.__name__, self.from_repo, self.to_repo,
1917
 
                self.find_ghosts, reqd_revs_repr, ifp_revs_repr,
1918
 
                self.limit)
1919
 
 
1920
 
    def execute(self):
1921
 
        return self.to_repo.search_missing_revision_ids(
1922
 
            self.from_repo, revision_ids=self.required_ids,
1923
 
            if_present_ids=self.if_present_ids, find_ghosts=self.find_ghosts,
1924
 
            limit=self.limit)
1925
 
 
1926
 
 
1927
 
def invert_parent_map(parent_map):
1928
 
    """Given a map from child => parents, create a map of parent=>children"""
1929
 
    child_map = {}
1930
 
    for child, parents in parent_map.iteritems():
1931
 
        for p in parents:
1932
 
            # Any given parent is likely to have only a small handful
1933
 
            # of children, many will have only one. So we avoid mem overhead of
1934
 
            # a list, in exchange for extra copying of tuples
1935
 
            if p not in child_map:
1936
 
                child_map[p] = (child,)
1937
 
            else:
1938
 
                child_map[p] = child_map[p] + (child,)
1939
 
    return child_map
1940
 
 
1941
 
 
1942
 
def _find_possible_heads(parent_map, tip_keys, depth):
1943
 
    """Walk backwards (towards children) through the parent_map.
1944
 
 
1945
 
    This finds 'heads' that will hopefully succinctly describe our search
1946
 
    graph.
1947
 
    """
1948
 
    child_map = invert_parent_map(parent_map)
1949
 
    heads = set()
1950
 
    current_roots = tip_keys
1951
 
    walked = set(current_roots)
1952
 
    while current_roots and depth > 0:
1953
 
        depth -= 1
1954
 
        children = set()
1955
 
        children_update = children.update
1956
 
        for p in current_roots:
1957
 
            # Is it better to pre- or post- filter the children?
1958
 
            try:
1959
 
                children_update(child_map[p])
1960
 
            except KeyError:
1961
 
                heads.add(p)
1962
 
        # If we've seen a key before, we don't want to walk it again. Note that
1963
 
        # 'children' stays relatively small while 'walked' grows large. So
1964
 
        # don't use 'difference_update' here which has to walk all of 'walked'.
1965
 
        # '.difference' is smart enough to walk only children and compare it to
1966
 
        # walked.
1967
 
        children = children.difference(walked)
1968
 
        walked.update(children)
1969
 
        current_roots = children
1970
 
    if current_roots:
1971
 
        # We walked to the end of depth, so these are the new tips.
1972
 
        heads.update(current_roots)
1973
 
    return heads
1974
 
 
1975
 
 
1976
 
def _run_search(parent_map, heads, exclude_keys):
1977
 
    """Given a parent map, run a _BreadthFirstSearcher on it.
1978
 
 
1979
 
    Start at heads, walk until you hit exclude_keys. As a further improvement,
1980
 
    watch for any heads that you encounter while walking, which means they were
1981
 
    not heads of the search.
1982
 
 
1983
 
    This is mostly used to generate a succinct recipe for how to walk through
1984
 
    most of parent_map.
1985
 
 
1986
 
    :return: (_BreadthFirstSearcher, set(heads_encountered_by_walking))
1987
 
    """
1988
 
    g = Graph(DictParentsProvider(parent_map))
1989
 
    s = g._make_breadth_first_searcher(heads)
1990
 
    found_heads = set()
1991
 
    while True:
1992
 
        try:
1993
 
            next_revs = s.next()
1994
 
        except StopIteration:
1995
 
            break
1996
 
        for parents in s._current_parents.itervalues():
1997
 
            f_heads = heads.intersection(parents)
1998
 
            if f_heads:
1999
 
                found_heads.update(f_heads)
2000
 
        stop_keys = exclude_keys.intersection(next_revs)
2001
 
        if stop_keys:
2002
 
            s.stop_searching_any(stop_keys)
2003
 
    for parents in s._current_parents.itervalues():
2004
 
        f_heads = heads.intersection(parents)
2005
 
        if f_heads:
2006
 
            found_heads.update(f_heads)
2007
 
    return s, found_heads
2008
 
 
2009
 
 
2010
 
def limited_search_result_from_parent_map(parent_map, missing_keys, tip_keys,
2011
 
                                          depth):
2012
 
    """Transform a parent_map that is searching 'tip_keys' into an
2013
 
    approximate SearchResult.
2014
 
 
2015
 
    We should be able to generate a SearchResult from a given set of starting
2016
 
    keys, that covers a subset of parent_map that has the last step pointing at
2017
 
    tip_keys. This is to handle the case that really-long-searches shouldn't be
2018
 
    started from scratch on each get_parent_map request, but we *do* want to
2019
 
    filter out some of the keys that we've already seen, so we don't get
2020
 
    information that we already know about on every request.
2021
 
 
2022
 
    The server will validate the search (that starting at start_keys and
2023
 
    stopping at stop_keys yields the exact key_count), so we have to be careful
2024
 
    to give an exact recipe.
2025
 
 
2026
 
    Basic algorithm is:
2027
 
        1) Invert parent_map to get child_map (todo: have it cached and pass it
2028
 
           in)
2029
 
        2) Starting at tip_keys, walk towards children for 'depth' steps.
2030
 
        3) At that point, we have the 'start' keys.
2031
 
        4) Start walking parent_map from 'start' keys, counting how many keys
2032
 
           are seen, and generating stop_keys for anything that would walk
2033
 
           outside of the parent_map.
2034
 
 
2035
 
    :param parent_map: A map from {child_id: (parent_ids,)}
2036
 
    :param missing_keys: parent_ids that we know are unavailable
2037
 
    :param tip_keys: the revision_ids that we are searching
2038
 
    :param depth: How far back to walk.
2039
 
    """
2040
 
    if not parent_map:
2041
 
        # No search to send, because we haven't done any searching yet.
2042
 
        return [], [], 0
2043
 
    heads = _find_possible_heads(parent_map, tip_keys, depth)
2044
 
    s, found_heads = _run_search(parent_map, heads, set(tip_keys))
2045
 
    _, start_keys, exclude_keys, key_count = s.get_result().get_recipe()
2046
 
    if found_heads:
2047
 
        # Anything in found_heads are redundant start_keys, we hit them while
2048
 
        # walking, so we can exclude them from the start list.
2049
 
        start_keys = set(start_keys).difference(found_heads)
2050
 
    return start_keys, exclude_keys, key_count
2051
 
 
2052
 
 
2053
 
def search_result_from_parent_map(parent_map, missing_keys):
2054
 
    """Transform a parent_map into SearchResult information."""
2055
 
    if not parent_map:
2056
 
        # parent_map is empty or None, simple search result
2057
 
        return [], [], 0
2058
 
    # start_set is all the keys in the cache
2059
 
    start_set = set(parent_map)
2060
 
    # result set is all the references to keys in the cache
2061
 
    result_parents = set()
2062
 
    for parents in parent_map.itervalues():
2063
 
        result_parents.update(parents)
2064
 
    stop_keys = result_parents.difference(start_set)
2065
 
    # We don't need to send ghosts back to the server as a position to
2066
 
    # stop either.
2067
 
    stop_keys.difference_update(missing_keys)
2068
 
    key_count = len(parent_map)
2069
 
    if (revision.NULL_REVISION in result_parents
2070
 
        and revision.NULL_REVISION in missing_keys):
2071
 
        # If we pruned NULL_REVISION from the stop_keys because it's also
2072
 
        # in our cache of "missing" keys we need to increment our key count
2073
 
        # by 1, because the reconsitituted SearchResult on the server will
2074
 
        # still consider NULL_REVISION to be an included key.
2075
 
        key_count += 1
2076
 
    included_keys = start_set.intersection(result_parents)
2077
 
    start_set.difference_update(included_keys)
2078
 
    return start_set, stop_keys, key_count
2079
 
 
2080
1449
 
2081
1450
def collapse_linear_regions(parent_map):
2082
1451
    """Collapse regions of the graph that are 'linear'.
2149
1518
            removed.add(node)
2150
1519
 
2151
1520
    return result
2152
 
 
2153
 
 
2154
 
class GraphThunkIdsToKeys(object):
2155
 
    """Forwards calls about 'ids' to be about keys internally."""
2156
 
 
2157
 
    def __init__(self, graph):
2158
 
        self._graph = graph
2159
 
 
2160
 
    def topo_sort(self):
2161
 
        return [r for (r,) in self._graph.topo_sort()]
2162
 
 
2163
 
    def heads(self, ids):
2164
 
        """See Graph.heads()"""
2165
 
        as_keys = [(i,) for i in ids]
2166
 
        head_keys = self._graph.heads(as_keys)
2167
 
        return set([h[0] for h in head_keys])
2168
 
 
2169
 
    def merge_sort(self, tip_revision):
2170
 
        nodes = self._graph.merge_sort((tip_revision,))
2171
 
        for node in nodes:
2172
 
            node.key = node.key[0]
2173
 
        return nodes
2174
 
 
2175
 
    def add_node(self, revision, parents):
2176
 
        self._graph.add_node((revision,), [(p,) for p in parents])
2177
 
 
2178
 
 
2179
 
_counters = [0,0,0,0,0,0,0]
2180
 
try:
2181
 
    from bzrlib._known_graph_pyx import KnownGraph
2182
 
except ImportError, e:
2183
 
    osutils.failed_to_load_extension(e)
2184
 
    from bzrlib._known_graph_py import KnownGraph