~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/graph.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2011-08-17 18:13:57 UTC
  • mfrom: (5268.7.29 transport-segments)
  • Revision ID: pqm@pqm.ubuntu.com-20110817181357-y5q5eth1hk8bl3om
(jelmer) Allow specifying the colocated branch to use in the branch URL,
 and retrieving the branch name using ControlDir._get_selected_branch.
 (Jelmer Vernooij)

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
 
from bzrlib.deprecated_graph import (node_distances, select_farthest)
28
26
 
29
27
STEP_UNIQUE_SEARCHER_EVERY = 5
30
28
 
61
59
        return 'DictParentsProvider(%r)' % self.ancestry
62
60
 
63
61
    def get_parent_map(self, keys):
64
 
        """See _StackedParentsProvider.get_parent_map"""
 
62
        """See StackedParentsProvider.get_parent_map"""
65
63
        ancestry = self.ancestry
66
64
        return dict((k, ancestry[k]) for k in keys if k in ancestry)
67
65
 
68
66
 
69
 
class _StackedParentsProvider(object):
70
 
 
 
67
class StackedParentsProvider(object):
 
68
    """A parents provider which stacks (or unions) multiple providers.
 
69
    
 
70
    The providers are queries in the order of the provided parent_providers.
 
71
    """
 
72
    
71
73
    def __init__(self, parent_providers):
72
74
        self._parent_providers = parent_providers
73
75
 
74
76
    def __repr__(self):
75
 
        return "_StackedParentsProvider(%r)" % self._parent_providers
 
77
        return "%s(%r)" % (self.__class__.__name__, self._parent_providers)
76
78
 
77
79
    def get_parent_map(self, keys):
78
80
        """Get a mapping of keys => parents
99
101
 
100
102
 
101
103
class CachingParentsProvider(object):
102
 
    """A parents provider which will cache the revision => parents in a dict.
103
 
 
104
 
    This is useful for providers that have an expensive lookup.
 
104
    """A parents provider which will cache the revision => parents as a dict.
 
105
 
 
106
    This is useful for providers which have an expensive look up.
 
107
 
 
108
    Either a ParentsProvider or a get_parent_map-like callback may be
 
109
    supplied.  If it provides extra un-asked-for parents, they will be cached,
 
110
    but filtered out of get_parent_map.
 
111
 
 
112
    The cache is enabled by default, but may be disabled and re-enabled.
105
113
    """
 
114
    def __init__(self, parent_provider=None, get_parent_map=None):
 
115
        """Constructor.
106
116
 
107
 
    def __init__(self, parent_provider):
 
117
        :param parent_provider: The ParentProvider to use.  It or
 
118
            get_parent_map must be supplied.
 
119
        :param get_parent_map: The get_parent_map callback to use.  It or
 
120
            parent_provider must be supplied.
 
121
        """
108
122
        self._real_provider = parent_provider
109
 
        # Theoretically we could use an LRUCache here
 
123
        if get_parent_map is None:
 
124
            self._get_parent_map = self._real_provider.get_parent_map
 
125
        else:
 
126
            self._get_parent_map = get_parent_map
 
127
        self._cache = None
 
128
        self.enable_cache(True)
 
129
 
 
130
    def __repr__(self):
 
131
        return "%s(%r)" % (self.__class__.__name__, self._real_provider)
 
132
 
 
133
    def enable_cache(self, cache_misses=True):
 
134
        """Enable cache."""
 
135
        if self._cache is not None:
 
136
            raise AssertionError('Cache enabled when already enabled.')
110
137
        self._cache = {}
111
 
 
112
 
    def __repr__(self):
113
 
        return "%s(%r)" % (self.__class__.__name__, self._real_provider)
 
138
        self._cache_misses = cache_misses
 
139
        self.missing_keys = set()
 
140
 
 
141
    def disable_cache(self):
 
142
        """Disable and clear the cache."""
 
143
        self._cache = None
 
144
        self._cache_misses = None
 
145
        self.missing_keys = set()
 
146
 
 
147
    def get_cached_map(self):
 
148
        """Return any cached get_parent_map values."""
 
149
        if self._cache is None:
 
150
            return None
 
151
        return dict(self._cache)
114
152
 
115
153
    def get_parent_map(self, keys):
116
 
        """See _StackedParentsProvider.get_parent_map"""
117
 
        needed = set()
118
 
        # If the _real_provider doesn't have a key, we cache a value of None,
119
 
        # which we then later use to realize we cannot provide a value for that
120
 
        # key.
121
 
        parent_map = {}
 
154
        """See StackedParentsProvider.get_parent_map."""
122
155
        cache = self._cache
 
156
        if cache is None:
 
157
            cache = self._get_parent_map(keys)
 
158
        else:
 
159
            needed_revisions = set(key for key in keys if key not in cache)
 
160
            # Do not ask for negatively cached keys
 
161
            needed_revisions.difference_update(self.missing_keys)
 
162
            if needed_revisions:
 
163
                parent_map = self._get_parent_map(needed_revisions)
 
164
                cache.update(parent_map)
 
165
                if self._cache_misses:
 
166
                    for key in needed_revisions:
 
167
                        if key not in parent_map:
 
168
                            self.note_missing_key(key)
 
169
        result = {}
123
170
        for key in keys:
124
 
            if key in cache:
125
 
                value = cache[key]
126
 
                if value is not None:
127
 
                    parent_map[key] = value
128
 
            else:
129
 
                needed.add(key)
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
 
171
            value = cache.get(key)
 
172
            if value is not None:
 
173
                result[key] = value
 
174
        return result
 
175
 
 
176
    def note_missing_key(self, key):
 
177
        """Note that key is a missing key."""
 
178
        if self._cache_misses:
 
179
            self.missing_keys.add(key)
 
180
 
 
181
 
 
182
class CallableToParentsProviderAdapter(object):
 
183
    """A parents provider that adapts any callable to the parents provider API.
 
184
 
 
185
    i.e. it accepts calls to self.get_parent_map and relays them to the
 
186
    callable it was constructed with.
 
187
    """
 
188
 
 
189
    def __init__(self, a_callable):
 
190
        self.callable = a_callable
 
191
 
 
192
    def __repr__(self):
 
193
        return "%s(%r)" % (self.__class__.__name__, self.callable)
 
194
 
 
195
    def get_parent_map(self, keys):
 
196
        return self.callable(keys)
138
197
 
139
198
 
140
199
class Graph(object):
191
250
        common ancestor of all border ancestors, because this shows that it
192
251
        cannot be a descendant of any border ancestor.
193
252
 
194
 
        The scaling of this operation should be proportional to
 
253
        The scaling of this operation should be proportional to:
 
254
 
195
255
        1. The number of uncommon ancestors
196
256
        2. The number of border ancestors
197
257
        3. The length of the shortest path between a border ancestor and an
212
272
        right = searchers[1].seen
213
273
        return (left.difference(right), right.difference(left))
214
274
 
 
275
    def find_descendants(self, old_key, new_key):
 
276
        """Find descendants of old_key that are ancestors of new_key."""
 
277
        child_map = self.get_child_map(self._find_descendant_ancestors(
 
278
            old_key, new_key))
 
279
        graph = Graph(DictParentsProvider(child_map))
 
280
        searcher = graph._make_breadth_first_searcher([old_key])
 
281
        list(searcher)
 
282
        return searcher.seen
 
283
 
 
284
    def _find_descendant_ancestors(self, old_key, new_key):
 
285
        """Find ancestors of new_key that may be descendants of old_key."""
 
286
        stop = self._make_breadth_first_searcher([old_key])
 
287
        descendants = self._make_breadth_first_searcher([new_key])
 
288
        for revisions in descendants:
 
289
            old_stop = stop.seen.intersection(revisions)
 
290
            descendants.stop_searching_any(old_stop)
 
291
            seen_stop = descendants.find_seen_ancestors(stop.step())
 
292
            descendants.stop_searching_any(seen_stop)
 
293
        return descendants.seen.difference(stop.seen)
 
294
 
 
295
    def get_child_map(self, keys):
 
296
        """Get a mapping from parents to children of the specified keys.
 
297
 
 
298
        This is simply the inversion of get_parent_map.  Only supplied keys
 
299
        will be discovered as children.
 
300
        :return: a dict of key:child_list for keys.
 
301
        """
 
302
        parent_map = self._parents_provider.get_parent_map(keys)
 
303
        parent_child = {}
 
304
        for child, parents in sorted(parent_map.items()):
 
305
            for parent in parents:
 
306
                parent_child.setdefault(parent, []).append(child)
 
307
        return parent_child
 
308
 
215
309
    def find_distance_to_null(self, target_revision_id, known_revision_ids):
216
310
        """Find the left-hand distance to the NULL_REVISION.
217
311
 
265
359
        # get there.
266
360
        return known_revnos[cur_tip] + num_steps
267
361
 
 
362
    def find_lefthand_distances(self, keys):
 
363
        """Find the distance to null for all the keys in keys.
 
364
 
 
365
        :param keys: keys to lookup.
 
366
        :return: A dict key->distance for all of keys.
 
367
        """
 
368
        # Optimisable by concurrent searching, but a random spread should get
 
369
        # some sort of hit rate.
 
370
        result = {}
 
371
        known_revnos = []
 
372
        ghosts = []
 
373
        for key in keys:
 
374
            try:
 
375
                known_revnos.append(
 
376
                    (key, self.find_distance_to_null(key, known_revnos)))
 
377
            except errors.GhostRevisionsHaveNoRevno:
 
378
                ghosts.append(key)
 
379
        for key in ghosts:
 
380
            known_revnos.append((key, -1))
 
381
        return dict(known_revnos)
 
382
 
268
383
    def find_unique_ancestors(self, unique_revision, common_revisions):
269
384
        """Find the unique ancestors for a revision versus others.
270
385
 
274
389
 
275
390
        :param unique_revision: The revision_id whose ancestry we are
276
391
            interested in.
277
 
            XXX: Would this API be better if we allowed multiple revisions on
278
 
                 to be searched here?
 
392
            (XXX: Would this API be better if we allowed multiple revisions on
 
393
            to be searched here?)
279
394
        :param common_revisions: Revision_ids of ancestries to exclude.
280
395
        :return: A set of revisions in the ancestry of unique_revision
281
396
        """
521
636
    def _refine_unique_nodes(self, unique_searcher, all_unique_searcher,
522
637
                             unique_tip_searchers, common_searcher):
523
638
        """Steps 5-8 of find_unique_ancestors.
524
 
        
 
639
 
525
640
        This function returns when common_searcher has stopped searching for
526
641
        more nodes.
527
642
        """
570
685
                                 all_unique_searcher._iterations)
571
686
            unique_tip_searchers = next_unique_searchers
572
687
 
573
 
    @symbol_versioning.deprecated_method(symbol_versioning.one_one)
574
 
    def get_parents(self, revisions):
575
 
        """Find revision ids of the parents of a list of revisions
576
 
 
577
 
        A list is returned of the same length as the input.  Each entry
578
 
        is a list of parent ids for the corresponding input revision.
579
 
 
580
 
        [NULL_REVISION] is used as the parent of the first user-committed
581
 
        revision.  Its parent list is empty.
582
 
 
583
 
        If the revision is not present (i.e. a ghost), None is used in place
584
 
        of the list of parents.
585
 
 
586
 
        Deprecated in bzr 1.2 - please see get_parent_map.
587
 
        """
588
 
        parents = self.get_parent_map(revisions)
589
 
        return [parents.get(r, None) for r in revisions]
590
 
 
591
688
    def get_parent_map(self, revisions):
592
689
        """Get a map of key:parent_list for revisions.
593
690
 
813
910
                stop.add(parent_id)
814
911
        return found
815
912
 
 
913
    def find_lefthand_merger(self, merged_key, tip_key):
 
914
        """Find the first lefthand ancestor of tip_key that merged merged_key.
 
915
 
 
916
        We do this by first finding the descendants of merged_key, then
 
917
        walking through the lefthand ancestry of tip_key until we find a key
 
918
        that doesn't descend from merged_key.  Its child is the key that
 
919
        merged merged_key.
 
920
 
 
921
        :return: The first lefthand ancestor of tip_key to merge merged_key.
 
922
            merged_key if it is a lefthand ancestor of tip_key.
 
923
            None if no ancestor of tip_key merged merged_key.
 
924
        """
 
925
        descendants = self.find_descendants(merged_key, tip_key)
 
926
        candidate_iterator = self.iter_lefthand_ancestry(tip_key)
 
927
        last_candidate = None
 
928
        for candidate in candidate_iterator:
 
929
            if candidate not in descendants:
 
930
                return last_candidate
 
931
            last_candidate = candidate
 
932
 
816
933
    def find_unique_lca(self, left_revision, right_revision,
817
934
                        count_steps=False):
818
935
        """Find a unique LCA.
870
987
                yield (ghost, None)
871
988
            pending = next_pending
872
989
 
 
990
    def iter_lefthand_ancestry(self, start_key, stop_keys=None):
 
991
        if stop_keys is None:
 
992
            stop_keys = ()
 
993
        next_key = start_key
 
994
        def get_parents(key):
 
995
            try:
 
996
                return self._parents_provider.get_parent_map([key])[key]
 
997
            except KeyError:
 
998
                raise errors.RevisionNotPresent(next_key, self)
 
999
        while True:
 
1000
            if next_key in stop_keys:
 
1001
                return
 
1002
            parents = get_parents(next_key)
 
1003
            yield next_key
 
1004
            if len(parents) == 0:
 
1005
                return
 
1006
            else:
 
1007
                next_key = parents[0]
 
1008
 
873
1009
    def iter_topo_order(self, revisions):
874
1010
        """Iterate through the input revisions in topological order.
875
1011
 
877
1013
        An ancestor may sort after a descendant if the relationship is not
878
1014
        visible in the supplied list of revisions.
879
1015
        """
 
1016
        from bzrlib import tsort
880
1017
        sorter = tsort.TopoSorter(self.get_parent_map(revisions))
881
1018
        return sorter.iter_topo_order()
882
1019
 
890
1027
        return set([candidate_descendant]) == self.heads(
891
1028
            [candidate_ancestor, candidate_descendant])
892
1029
 
 
1030
    def is_between(self, revid, lower_bound_revid, upper_bound_revid):
 
1031
        """Determine whether a revision is between two others.
 
1032
 
 
1033
        returns true if and only if:
 
1034
        lower_bound_revid <= revid <= upper_bound_revid
 
1035
        """
 
1036
        return ((upper_bound_revid is None or
 
1037
                    self.is_ancestor(revid, upper_bound_revid)) and
 
1038
               (lower_bound_revid is None or
 
1039
                    self.is_ancestor(lower_bound_revid, revid)))
 
1040
 
893
1041
    def _search_for_extra_common(self, common, searchers):
894
1042
        """Make sure that unique nodes are genuinely unique.
895
1043
 
1168
1316
 
1169
1317
    def get_result(self):
1170
1318
        """Get a SearchResult for the current state of this searcher.
1171
 
        
 
1319
 
1172
1320
        :return: A SearchResult for this search so far. The SearchResult is
1173
1321
            static - the search can be advanced and the search result will not
1174
1322
            be invalidated or altered.
1178
1326
            # exclude keys for them. However, while we could have a second
1179
1327
            # look-ahead result buffer and shuffle things around, this method
1180
1328
            # is typically only called once per search - when memoising the
1181
 
            # results of the search. 
 
1329
            # results of the search.
1182
1330
            found, ghosts, next, parents = self._do_query(self._next_query)
1183
1331
            # pretend we didn't query: perhaps we should tweak _do_query to be
1184
1332
            # entirely stateless?
1225
1373
 
1226
1374
    def next_with_ghosts(self):
1227
1375
        """Return the next found ancestors, with ghosts split out.
1228
 
        
 
1376
 
1229
1377
        Ancestors are returned in the order they are seen in a breadth-first
1230
1378
        traversal.  No ancestor will be returned more than once. Ancestors are
1231
1379
        returned only after asking for their parents, which allows us to detect
1290
1438
 
1291
1439
    def find_seen_ancestors(self, revisions):
1292
1440
        """Find ancestors of these revisions that have already been seen.
1293
 
        
 
1441
 
1294
1442
        This function generally makes the assumption that querying for the
1295
1443
        parents of a node that has already been queried is reasonably cheap.
1296
1444
        (eg, not a round trip to a remote host).
1331
1479
        Remove any of the specified revisions from the search list.
1332
1480
 
1333
1481
        None of the specified revisions are required to be present in the
1334
 
        search list.  In this case, the call is a no-op.
 
1482
        search list.
 
1483
 
 
1484
        It is okay to call stop_searching_any() for revisions which were seen
 
1485
        in previous iterations. It is the callers responsibility to call
 
1486
        find_seen_ancestors() to make sure that current search tips that are
 
1487
        ancestors of those revisions are also stopped.  All explicitly stopped
 
1488
        revisions will be excluded from the search result's get_keys(), though.
1335
1489
        """
1336
1490
        # TODO: does this help performance?
1337
1491
        # if not revisions:
1346
1500
                self._current_ghosts.intersection(revisions))
1347
1501
            self._current_present.difference_update(stopped)
1348
1502
            self._current_ghosts.difference_update(stopped)
1349
 
            # stopping 'x' should stop returning parents of 'x', but 
 
1503
            # stopping 'x' should stop returning parents of 'x', but
1350
1504
            # not if 'y' always references those same parents
1351
1505
            stop_rev_references = {}
1352
1506
            for rev in stopped_present:
1368
1522
                    stop_parents.add(rev_id)
1369
1523
            self._next_query.difference_update(stop_parents)
1370
1524
        self._stopped_keys.update(stopped)
 
1525
        self._stopped_keys.update(revisions)
1371
1526
        return stopped
1372
1527
 
1373
1528
    def start_searching(self, revisions):
1395
1550
            return revs, ghosts
1396
1551
 
1397
1552
 
1398
 
class SearchResult(object):
 
1553
class AbstractSearchResult(object):
 
1554
    """The result of a search, describing a set of keys.
 
1555
    
 
1556
    Search results are typically used as the 'fetch_spec' parameter when
 
1557
    fetching revisions.
 
1558
 
 
1559
    :seealso: AbstractSearch
 
1560
    """
 
1561
 
 
1562
    def get_recipe(self):
 
1563
        """Return a recipe that can be used to replay this search.
 
1564
 
 
1565
        The recipe allows reconstruction of the same results at a later date.
 
1566
 
 
1567
        :return: A tuple of `(search_kind_str, *details)`.  The details vary by
 
1568
            kind of search result.
 
1569
        """
 
1570
        raise NotImplementedError(self.get_recipe)
 
1571
 
 
1572
    def get_network_struct(self):
 
1573
        """Return a tuple that can be transmitted via the HPSS protocol."""
 
1574
        raise NotImplementedError(self.get_network_struct)
 
1575
 
 
1576
    def get_keys(self):
 
1577
        """Return the keys found in this search.
 
1578
 
 
1579
        :return: A set of keys.
 
1580
        """
 
1581
        raise NotImplementedError(self.get_keys)
 
1582
 
 
1583
    def is_empty(self):
 
1584
        """Return false if the search lists 1 or more revisions."""
 
1585
        raise NotImplementedError(self.is_empty)
 
1586
 
 
1587
    def refine(self, seen, referenced):
 
1588
        """Create a new search by refining this search.
 
1589
 
 
1590
        :param seen: Revisions that have been satisfied.
 
1591
        :param referenced: Revision references observed while satisfying some
 
1592
            of this search.
 
1593
        :return: A search result.
 
1594
        """
 
1595
        raise NotImplementedError(self.refine)
 
1596
 
 
1597
 
 
1598
class AbstractSearch(object):
 
1599
    """A search that can be executed, producing a search result.
 
1600
 
 
1601
    :seealso: AbstractSearchResult
 
1602
    """
 
1603
 
 
1604
    def execute(self):
 
1605
        """Construct a network-ready search result from this search description.
 
1606
 
 
1607
        This may take some time to search repositories, etc.
 
1608
 
 
1609
        :return: A search result (an object that implements
 
1610
            AbstractSearchResult's API).
 
1611
        """
 
1612
        raise NotImplementedError(self.execute)
 
1613
 
 
1614
 
 
1615
class SearchResult(AbstractSearchResult):
1399
1616
    """The result of a breadth first search.
1400
1617
 
1401
1618
    A SearchResult provides the ability to reconstruct the search or access a
1413
1630
            a SearchResult from a smart server, in which case the keys list is
1414
1631
            not necessarily immediately available.
1415
1632
        """
1416
 
        self._recipe = (start_keys, exclude_keys, key_count)
 
1633
        self._recipe = ('search', start_keys, exclude_keys, key_count)
1417
1634
        self._keys = frozenset(keys)
1418
1635
 
 
1636
    def __repr__(self):
 
1637
        kind, start_keys, exclude_keys, key_count = self._recipe
 
1638
        if len(start_keys) > 5:
 
1639
            start_keys_repr = repr(list(start_keys)[:5])[:-1] + ', ...]'
 
1640
        else:
 
1641
            start_keys_repr = repr(start_keys)
 
1642
        if len(exclude_keys) > 5:
 
1643
            exclude_keys_repr = repr(list(exclude_keys)[:5])[:-1] + ', ...]'
 
1644
        else:
 
1645
            exclude_keys_repr = repr(exclude_keys)
 
1646
        return '<%s %s:(%s, %s, %d)>' % (self.__class__.__name__,
 
1647
            kind, start_keys_repr, exclude_keys_repr, key_count)
 
1648
 
1419
1649
    def get_recipe(self):
1420
1650
        """Return a recipe that can be used to replay this search.
1421
 
        
 
1651
 
1422
1652
        The recipe allows reconstruction of the same results at a later date
1423
1653
        without knowing all the found keys. The essential elements are a list
1424
 
        of keys to start and and to stop at. In order to give reproducible
 
1654
        of keys to start and to stop at. In order to give reproducible
1425
1655
        results when ghosts are encountered by a search they are automatically
1426
1656
        added to the exclude list (or else ghost filling may alter the
1427
1657
        results).
1428
1658
 
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
 
1659
        :return: A tuple ('search', start_keys_set, exclude_keys_set,
 
1660
            revision_count). To recreate the results of this search, create a
 
1661
            breadth first searcher on the same graph starting at start_keys.
 
1662
            Then call next() (or next_with_ghosts()) repeatedly, and on every
 
1663
            result, call stop_searching_any on any keys from the exclude_keys
 
1664
            set. The revision_count value acts as a trivial cross-check - the
 
1665
            found revisions of the new search should have as many elements as
1436
1666
            revision_count. If it does not, then additional revisions have been
1437
1667
            ghosted since the search was executed the first time and the second
1438
1668
            time.
1439
1669
        """
1440
1670
        return self._recipe
1441
1671
 
 
1672
    def get_network_struct(self):
 
1673
        start_keys = ' '.join(self._recipe[1])
 
1674
        stop_keys = ' '.join(self._recipe[2])
 
1675
        count = str(self._recipe[3])
 
1676
        return (self._recipe[0], '\n'.join((start_keys, stop_keys, count)))
 
1677
 
1442
1678
    def get_keys(self):
1443
1679
        """Return the keys found in this search.
1444
1680
 
1446
1682
        """
1447
1683
        return self._keys
1448
1684
 
 
1685
    def is_empty(self):
 
1686
        """Return false if the search lists 1 or more revisions."""
 
1687
        return self._recipe[3] == 0
 
1688
 
 
1689
    def refine(self, seen, referenced):
 
1690
        """Create a new search by refining this search.
 
1691
 
 
1692
        :param seen: Revisions that have been satisfied.
 
1693
        :param referenced: Revision references observed while satisfying some
 
1694
            of this search.
 
1695
        """
 
1696
        start = self._recipe[1]
 
1697
        exclude = self._recipe[2]
 
1698
        count = self._recipe[3]
 
1699
        keys = self.get_keys()
 
1700
        # New heads = referenced + old heads - seen things - exclude
 
1701
        pending_refs = set(referenced)
 
1702
        pending_refs.update(start)
 
1703
        pending_refs.difference_update(seen)
 
1704
        pending_refs.difference_update(exclude)
 
1705
        # New exclude = old exclude + satisfied heads
 
1706
        seen_heads = start.intersection(seen)
 
1707
        exclude.update(seen_heads)
 
1708
        # keys gets seen removed
 
1709
        keys = keys - seen
 
1710
        # length is reduced by len(seen)
 
1711
        count -= len(seen)
 
1712
        return SearchResult(pending_refs, exclude, count, keys)
 
1713
 
 
1714
 
 
1715
class PendingAncestryResult(AbstractSearchResult):
 
1716
    """A search result that will reconstruct the ancestry for some graph heads.
 
1717
 
 
1718
    Unlike SearchResult, this doesn't hold the complete search result in
 
1719
    memory, it just holds a description of how to generate it.
 
1720
    """
 
1721
 
 
1722
    def __init__(self, heads, repo):
 
1723
        """Constructor.
 
1724
 
 
1725
        :param heads: an iterable of graph heads.
 
1726
        :param repo: a repository to use to generate the ancestry for the given
 
1727
            heads.
 
1728
        """
 
1729
        self.heads = frozenset(heads)
 
1730
        self.repo = repo
 
1731
 
 
1732
    def __repr__(self):
 
1733
        if len(self.heads) > 5:
 
1734
            heads_repr = repr(list(self.heads)[:5])[:-1]
 
1735
            heads_repr += ', <%d more>...]' % (len(self.heads) - 5,)
 
1736
        else:
 
1737
            heads_repr = repr(self.heads)
 
1738
        return '<%s heads:%s repo:%r>' % (
 
1739
            self.__class__.__name__, heads_repr, self.repo)
 
1740
 
 
1741
    def get_recipe(self):
 
1742
        """Return a recipe that can be used to replay this search.
 
1743
 
 
1744
        The recipe allows reconstruction of the same results at a later date.
 
1745
 
 
1746
        :seealso SearchResult.get_recipe:
 
1747
 
 
1748
        :return: A tuple ('proxy-search', start_keys_set, set(), -1)
 
1749
            To recreate this result, create a PendingAncestryResult with the
 
1750
            start_keys_set.
 
1751
        """
 
1752
        return ('proxy-search', self.heads, set(), -1)
 
1753
 
 
1754
    def get_network_struct(self):
 
1755
        parts = ['ancestry-of']
 
1756
        parts.extend(self.heads)
 
1757
        return parts
 
1758
 
 
1759
    def get_keys(self):
 
1760
        """See SearchResult.get_keys.
 
1761
 
 
1762
        Returns all the keys for the ancestry of the heads, excluding
 
1763
        NULL_REVISION.
 
1764
        """
 
1765
        return self._get_keys(self.repo.get_graph())
 
1766
 
 
1767
    def _get_keys(self, graph):
 
1768
        NULL_REVISION = revision.NULL_REVISION
 
1769
        keys = [key for (key, parents) in graph.iter_ancestry(self.heads)
 
1770
                if key != NULL_REVISION and parents is not None]
 
1771
        return keys
 
1772
 
 
1773
    def is_empty(self):
 
1774
        """Return false if the search lists 1 or more revisions."""
 
1775
        if revision.NULL_REVISION in self.heads:
 
1776
            return len(self.heads) == 1
 
1777
        else:
 
1778
            return len(self.heads) == 0
 
1779
 
 
1780
    def refine(self, seen, referenced):
 
1781
        """Create a new search by refining this search.
 
1782
 
 
1783
        :param seen: Revisions that have been satisfied.
 
1784
        :param referenced: Revision references observed while satisfying some
 
1785
            of this search.
 
1786
        """
 
1787
        referenced = self.heads.union(referenced)
 
1788
        return PendingAncestryResult(referenced - seen, self.repo)
 
1789
 
 
1790
 
 
1791
class EmptySearchResult(AbstractSearchResult):
 
1792
    """An empty search result."""
 
1793
 
 
1794
    def is_empty(self):
 
1795
        return True
 
1796
    
 
1797
 
 
1798
class EverythingResult(AbstractSearchResult):
 
1799
    """A search result that simply requests everything in the repository."""
 
1800
 
 
1801
    def __init__(self, repo):
 
1802
        self._repo = repo
 
1803
 
 
1804
    def __repr__(self):
 
1805
        return '%s(%r)' % (self.__class__.__name__, self._repo)
 
1806
 
 
1807
    def get_recipe(self):
 
1808
        raise NotImplementedError(self.get_recipe)
 
1809
 
 
1810
    def get_network_struct(self):
 
1811
        return ('everything',)
 
1812
 
 
1813
    def get_keys(self):
 
1814
        if 'evil' in debug.debug_flags:
 
1815
            from bzrlib import remote
 
1816
            if isinstance(self._repo, remote.RemoteRepository):
 
1817
                # warn developers (not users) not to do this
 
1818
                trace.mutter_callsite(
 
1819
                    2, "EverythingResult(RemoteRepository).get_keys() is slow.")
 
1820
        return self._repo.all_revision_ids()
 
1821
 
 
1822
    def is_empty(self):
 
1823
        # It's ok for this to wrongly return False: the worst that can happen
 
1824
        # is that RemoteStreamSource will initiate a get_stream on an empty
 
1825
        # repository.  And almost all repositories are non-empty.
 
1826
        return False
 
1827
 
 
1828
    def refine(self, seen, referenced):
 
1829
        heads = set(self._repo.all_revision_ids())
 
1830
        heads.difference_update(seen)
 
1831
        heads.update(referenced)
 
1832
        return PendingAncestryResult(heads, self._repo)
 
1833
 
 
1834
 
 
1835
class EverythingNotInOther(AbstractSearch):
 
1836
    """Find all revisions in that are in one repo but not the other."""
 
1837
 
 
1838
    def __init__(self, to_repo, from_repo, find_ghosts=False):
 
1839
        self.to_repo = to_repo
 
1840
        self.from_repo = from_repo
 
1841
        self.find_ghosts = find_ghosts
 
1842
 
 
1843
    def execute(self):
 
1844
        return self.to_repo.search_missing_revision_ids(
 
1845
            self.from_repo, find_ghosts=self.find_ghosts)
 
1846
 
 
1847
 
 
1848
class NotInOtherForRevs(AbstractSearch):
 
1849
    """Find all revisions missing in one repo for a some specific heads."""
 
1850
 
 
1851
    def __init__(self, to_repo, from_repo, required_ids, if_present_ids=None,
 
1852
            find_ghosts=False, limit=None):
 
1853
        """Constructor.
 
1854
 
 
1855
        :param required_ids: revision IDs of heads that must be found, or else
 
1856
            the search will fail with NoSuchRevision.  All revisions in their
 
1857
            ancestry not already in the other repository will be included in
 
1858
            the search result.
 
1859
        :param if_present_ids: revision IDs of heads that may be absent in the
 
1860
            source repository.  If present, then their ancestry not already
 
1861
            found in other will be included in the search result.
 
1862
        :param limit: maximum number of revisions to fetch
 
1863
        """
 
1864
        self.to_repo = to_repo
 
1865
        self.from_repo = from_repo
 
1866
        self.find_ghosts = find_ghosts
 
1867
        self.required_ids = required_ids
 
1868
        self.if_present_ids = if_present_ids
 
1869
        self.limit = limit
 
1870
 
 
1871
    def __repr__(self):
 
1872
        if len(self.required_ids) > 5:
 
1873
            reqd_revs_repr = repr(list(self.required_ids)[:5])[:-1] + ', ...]'
 
1874
        else:
 
1875
            reqd_revs_repr = repr(self.required_ids)
 
1876
        if self.if_present_ids and len(self.if_present_ids) > 5:
 
1877
            ifp_revs_repr = repr(list(self.if_present_ids)[:5])[:-1] + ', ...]'
 
1878
        else:
 
1879
            ifp_revs_repr = repr(self.if_present_ids)
 
1880
 
 
1881
        return ("<%s from:%r to:%r find_ghosts:%r req'd:%r if-present:%r"
 
1882
                "limit:%r>") % (
 
1883
                self.__class__.__name__, self.from_repo, self.to_repo,
 
1884
                self.find_ghosts, reqd_revs_repr, ifp_revs_repr,
 
1885
                self.limit)
 
1886
 
 
1887
    def execute(self):
 
1888
        return self.to_repo.search_missing_revision_ids(
 
1889
            self.from_repo, revision_ids=self.required_ids,
 
1890
            if_present_ids=self.if_present_ids, find_ghosts=self.find_ghosts,
 
1891
            limit=self.limit)
 
1892
 
1449
1893
 
1450
1894
def collapse_linear_regions(parent_map):
1451
1895
    """Collapse regions of the graph that are 'linear'.
1518
1962
            removed.add(node)
1519
1963
 
1520
1964
    return result
 
1965
 
 
1966
 
 
1967
class GraphThunkIdsToKeys(object):
 
1968
    """Forwards calls about 'ids' to be about keys internally."""
 
1969
 
 
1970
    def __init__(self, graph):
 
1971
        self._graph = graph
 
1972
 
 
1973
    def topo_sort(self):
 
1974
        return [r for (r,) in self._graph.topo_sort()]
 
1975
 
 
1976
    def heads(self, ids):
 
1977
        """See Graph.heads()"""
 
1978
        as_keys = [(i,) for i in ids]
 
1979
        head_keys = self._graph.heads(as_keys)
 
1980
        return set([h[0] for h in head_keys])
 
1981
 
 
1982
    def merge_sort(self, tip_revision):
 
1983
        nodes = self._graph.merge_sort((tip_revision,))
 
1984
        for node in nodes:
 
1985
            node.key = node.key[0]
 
1986
        return nodes
 
1987
 
 
1988
    def add_node(self, revision, parents):
 
1989
        self._graph.add_node((revision,), [(p,) for p in parents])
 
1990
 
 
1991
 
 
1992
_counters = [0,0,0,0,0,0,0]
 
1993
try:
 
1994
    from bzrlib._known_graph_pyx import KnownGraph
 
1995
except ImportError, e:
 
1996
    osutils.failed_to_load_extension(e)
 
1997
    from bzrlib._known_graph_py import KnownGraph