~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/graph.py

(jameinel) Allow 'bzr serve' to interpret SIGHUP as a graceful shutdown.
 (bug #795025) (John A Meinel)

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
16
 
17
17
import time
18
18
 
19
19
from bzrlib import (
20
20
    debug,
21
21
    errors,
 
22
    osutils,
22
23
    revision,
23
 
    symbol_versioning,
24
24
    trace,
25
 
    tsort,
26
25
    )
27
26
 
28
27
STEP_UNIQUE_SEARCHER_EVERY = 5
59
58
    def __repr__(self):
60
59
        return 'DictParentsProvider(%r)' % self.ancestry
61
60
 
 
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
 
62
66
    def get_parent_map(self, keys):
63
 
        """See _StackedParentsProvider.get_parent_map"""
 
67
        """See StackedParentsProvider.get_parent_map"""
64
68
        ancestry = self.ancestry
65
 
        return dict((k, ancestry[k]) for k in keys if k in ancestry)
66
 
 
67
 
 
68
 
class _StackedParentsProvider(object):
 
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
    """
69
77
 
70
78
    def __init__(self, parent_providers):
71
79
        self._parent_providers = parent_providers
72
80
 
73
81
    def __repr__(self):
74
 
        return "_StackedParentsProvider(%r)" % self._parent_providers
 
82
        return "%s(%r)" % (self.__class__.__name__, self._parent_providers)
75
83
 
76
84
    def get_parent_map(self, keys):
77
85
        """Get a mapping of keys => parents
88
96
        """
89
97
        found = {}
90
98
        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
91
115
        for parents_provider in self._parent_providers:
92
116
            new_found = parents_provider.get_parent_map(remaining)
93
117
            found.update(new_found)
121
145
            self._get_parent_map = self._real_provider.get_parent_map
122
146
        else:
123
147
            self._get_parent_map = get_parent_map
124
 
        self._cache = {}
125
 
        self._cache_misses = True
 
148
        self._cache = None
 
149
        self.enable_cache(True)
126
150
 
127
151
    def __repr__(self):
128
152
        return "%s(%r)" % (self.__class__.__name__, self._real_provider)
133
157
            raise AssertionError('Cache enabled when already enabled.')
134
158
        self._cache = {}
135
159
        self._cache_misses = cache_misses
 
160
        self.missing_keys = set()
136
161
 
137
162
    def disable_cache(self):
138
163
        """Disable and clear the cache."""
139
164
        self._cache = None
 
165
        self._cache_misses = None
 
166
        self.missing_keys = set()
140
167
 
141
168
    def get_cached_map(self):
142
169
        """Return any cached get_parent_map values."""
143
170
        if self._cache is None:
144
171
            return None
145
 
        return dict((k, v) for k, v in self._cache.items()
146
 
                    if v is not 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])
147
184
 
148
185
    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 = {}
 
186
        """See StackedParentsProvider.get_parent_map."""
 
187
        cache = self._cache
 
188
        if cache is None:
 
189
            cache = self._get_parent_map(keys)
156
190
        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)
 
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
        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)
168
229
 
169
230
 
170
231
class Graph(object):
221
282
        common ancestor of all border ancestors, because this shows that it
222
283
        cannot be a descendant of any border ancestor.
223
284
 
224
 
        The scaling of this operation should be proportional to
 
285
        The scaling of this operation should be proportional to:
 
286
 
225
287
        1. The number of uncommon ancestors
226
288
        2. The number of border ancestors
227
289
        3. The length of the shortest path between a border ancestor and an
242
304
        right = searchers[1].seen
243
305
        return (left.difference(right), right.difference(left))
244
306
 
 
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
 
245
341
    def find_distance_to_null(self, target_revision_id, known_revision_ids):
246
342
        """Find the left-hand distance to the NULL_REVISION.
247
343
 
295
391
        # get there.
296
392
        return known_revnos[cur_tip] + num_steps
297
393
 
 
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
 
298
415
    def find_unique_ancestors(self, unique_revision, common_revisions):
299
416
        """Find the unique ancestors for a revision versus others.
300
417
 
304
421
 
305
422
        :param unique_revision: The revision_id whose ancestry we are
306
423
            interested in.
307
 
            XXX: Would this API be better if we allowed multiple revisions on
308
 
                 to be searched here?
 
424
            (XXX: Would this API be better if we allowed multiple revisions on
 
425
            to be searched here?)
309
426
        :param common_revisions: Revision_ids of ancestries to exclude.
310
427
        :return: A set of revisions in the ancestry of unique_revision
311
428
        """
551
668
    def _refine_unique_nodes(self, unique_searcher, all_unique_searcher,
552
669
                             unique_tip_searchers, common_searcher):
553
670
        """Steps 5-8 of find_unique_ancestors.
554
 
        
 
671
 
555
672
        This function returns when common_searcher has stopped searching for
556
673
        more nodes.
557
674
        """
600
717
                                 all_unique_searcher._iterations)
601
718
            unique_tip_searchers = next_unique_searchers
602
719
 
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
720
    def get_parent_map(self, revisions):
622
721
        """Get a map of key:parent_list for revisions.
623
722
 
843
942
                stop.add(parent_id)
844
943
        return found
845
944
 
 
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
 
846
965
    def find_unique_lca(self, left_revision, right_revision,
847
966
                        count_steps=False):
848
967
        """Find a unique LCA.
900
1019
                yield (ghost, None)
901
1020
            pending = next_pending
902
1021
 
 
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
 
903
1041
    def iter_topo_order(self, revisions):
904
1042
        """Iterate through the input revisions in topological order.
905
1043
 
907
1045
        An ancestor may sort after a descendant if the relationship is not
908
1046
        visible in the supplied list of revisions.
909
1047
        """
 
1048
        from bzrlib import tsort
910
1049
        sorter = tsort.TopoSorter(self.get_parent_map(revisions))
911
1050
        return sorter.iter_topo_order()
912
1051
 
1209
1348
 
1210
1349
    def get_result(self):
1211
1350
        """Get a SearchResult for the current state of this searcher.
1212
 
        
 
1351
 
1213
1352
        :return: A SearchResult for this search so far. The SearchResult is
1214
1353
            static - the search can be advanced and the search result will not
1215
1354
            be invalidated or altered.
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?
1266
1405
 
1267
1406
    def next_with_ghosts(self):
1268
1407
        """Return the next found ancestors, with ghosts split out.
1269
 
        
 
1408
 
1270
1409
        Ancestors are returned in the order they are seen in a breadth-first
1271
1410
        traversal.  No ancestor will be returned more than once. Ancestors are
1272
1411
        returned only after asking for their parents, which allows us to detect
1312
1451
        parents_of_found = set()
1313
1452
        # revisions may contain nodes that point to other nodes in revisions:
1314
1453
        # we want to filter them out.
1315
 
        self.seen.update(revisions)
 
1454
        seen = self.seen
 
1455
        seen.update(revisions)
1316
1456
        parent_map = self._parents_provider.get_parent_map(revisions)
1317
1457
        found_revisions.update(parent_map)
1318
1458
        for rev_id, parents in parent_map.iteritems():
1319
1459
            if parents is None:
1320
1460
                continue
1321
 
            new_found_parents = [p for p in parents if p not in self.seen]
 
1461
            new_found_parents = [p for p in parents if p not in seen]
1322
1462
            if new_found_parents:
1323
1463
                # Calling set.update() with an empty generator is actually
1324
1464
                # rather expensive.
1331
1471
 
1332
1472
    def find_seen_ancestors(self, revisions):
1333
1473
        """Find ancestors of these revisions that have already been seen.
1334
 
        
 
1474
 
1335
1475
        This function generally makes the assumption that querying for the
1336
1476
        parents of a node that has already been queried is reasonably cheap.
1337
1477
        (eg, not a round trip to a remote host).
1393
1533
                self._current_ghosts.intersection(revisions))
1394
1534
            self._current_present.difference_update(stopped)
1395
1535
            self._current_ghosts.difference_update(stopped)
1396
 
            # stopping 'x' should stop returning parents of 'x', but 
 
1536
            # stopping 'x' should stop returning parents of 'x', but
1397
1537
            # not if 'y' always references those same parents
1398
1538
            stop_rev_references = {}
1399
1539
            for rev in stopped_present:
1415
1555
                    stop_parents.add(rev_id)
1416
1556
            self._next_query.difference_update(stop_parents)
1417
1557
        self._stopped_keys.update(stopped)
1418
 
        self._stopped_keys.update(revisions - set([revision.NULL_REVISION]))
 
1558
        self._stopped_keys.update(revisions)
1419
1559
        return stopped
1420
1560
 
1421
1561
    def start_searching(self, revisions):
1443
1583
            return revs, ghosts
1444
1584
 
1445
1585
 
1446
 
class SearchResult(object):
 
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):
1447
1649
    """The result of a breadth first search.
1448
1650
 
1449
1651
    A SearchResult provides the ability to reconstruct the search or access a
1461
1663
            a SearchResult from a smart server, in which case the keys list is
1462
1664
            not necessarily immediately available.
1463
1665
        """
1464
 
        self._recipe = (start_keys, exclude_keys, key_count)
 
1666
        self._recipe = ('search', start_keys, exclude_keys, key_count)
1465
1667
        self._keys = frozenset(keys)
1466
1668
 
 
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
 
1467
1682
    def get_recipe(self):
1468
1683
        """Return a recipe that can be used to replay this search.
1469
 
        
 
1684
 
1470
1685
        The recipe allows reconstruction of the same results at a later date
1471
1686
        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
 
1687
        of keys to start and to stop at. In order to give reproducible
1473
1688
        results when ghosts are encountered by a search they are automatically
1474
1689
        added to the exclude list (or else ghost filling may alter the
1475
1690
        results).
1476
1691
 
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
 
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
1484
1699
            revision_count. If it does not, then additional revisions have been
1485
1700
            ghosted since the search was executed the first time and the second
1486
1701
            time.
1487
1702
        """
1488
1703
        return self._recipe
1489
1704
 
 
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
 
1490
1711
    def get_keys(self):
1491
1712
        """Return the keys found in this search.
1492
1713
 
1494
1715
        """
1495
1716
        return self._keys
1496
1717
 
 
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
 
1497
2080
 
1498
2081
def collapse_linear_regions(parent_map):
1499
2082
    """Collapse regions of the graph that are 'linear'.
1566
2149
            removed.add(node)
1567
2150
 
1568
2151
    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