~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: 2008-10-02 17:28:44 UTC
  • mfrom: (3744.2.2 merge_reprocess)
  • Revision ID: pqm@pqm.ubuntu.com-20081002172844-d6df1l8dzpsqzyup
(jam) For 'bzr merge' enable '--reprocess' by default whenever
        '--show-base' is not set.

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
 
59
61
        return 'DictParentsProvider(%r)' % self.ancestry
60
62
 
61
63
    def get_parent_map(self, keys):
62
 
        """See StackedParentsProvider.get_parent_map"""
 
64
        """See _StackedParentsProvider.get_parent_map"""
63
65
        ancestry = self.ancestry
64
66
        return dict((k, ancestry[k]) for k in keys if k in ancestry)
65
67
 
66
68
 
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
 
    
 
69
class _StackedParentsProvider(object):
 
70
 
73
71
    def __init__(self, parent_providers):
74
72
        self._parent_providers = parent_providers
75
73
 
76
74
    def __repr__(self):
77
 
        return "%s(%r)" % (self.__class__.__name__, self._parent_providers)
 
75
        return "_StackedParentsProvider(%r)" % self._parent_providers
78
76
 
79
77
    def get_parent_map(self, keys):
80
78
        """Get a mapping of keys => parents
101
99
 
102
100
 
103
101
class CachingParentsProvider(object):
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.
 
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.
113
105
    """
114
 
    def __init__(self, parent_provider=None, get_parent_map=None):
115
 
        """Constructor.
116
106
 
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
 
        """
 
107
    def __init__(self, parent_provider):
122
108
        self._real_provider = parent_provider
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)
 
109
        # Theoretically we could use an LRUCache here
 
110
        self._cache = {}
129
111
 
130
112
    def __repr__(self):
131
113
        return "%s(%r)" % (self.__class__.__name__, self._real_provider)
132
114
 
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.')
137
 
        self._cache = {}
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)
152
 
 
153
115
    def get_parent_map(self, keys):
154
 
        """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 = {}
155
122
        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 = {}
170
123
        for key in keys:
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)
 
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
197
138
 
198
139
 
199
140
class Graph(object):
250
191
        common ancestor of all border ancestors, because this shows that it
251
192
        cannot be a descendant of any border ancestor.
252
193
 
253
 
        The scaling of this operation should be proportional to:
254
 
 
 
194
        The scaling of this operation should be proportional to
255
195
        1. The number of uncommon ancestors
256
196
        2. The number of border ancestors
257
197
        3. The length of the shortest path between a border ancestor and an
272
212
        right = searchers[1].seen
273
213
        return (left.difference(right), right.difference(left))
274
214
 
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
 
 
309
215
    def find_distance_to_null(self, target_revision_id, known_revision_ids):
310
216
        """Find the left-hand distance to the NULL_REVISION.
311
217
 
359
265
        # get there.
360
266
        return known_revnos[cur_tip] + num_steps
361
267
 
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
 
 
383
268
    def find_unique_ancestors(self, unique_revision, common_revisions):
384
269
        """Find the unique ancestors for a revision versus others.
385
270
 
389
274
 
390
275
        :param unique_revision: The revision_id whose ancestry we are
391
276
            interested in.
392
 
            (XXX: Would this API be better if we allowed multiple revisions on
393
 
            to be searched here?)
 
277
            XXX: Would this API be better if we allowed multiple revisions on
 
278
                 to be searched here?
394
279
        :param common_revisions: Revision_ids of ancestries to exclude.
395
280
        :return: A set of revisions in the ancestry of unique_revision
396
281
        """
636
521
    def _refine_unique_nodes(self, unique_searcher, all_unique_searcher,
637
522
                             unique_tip_searchers, common_searcher):
638
523
        """Steps 5-8 of find_unique_ancestors.
639
 
 
 
524
        
640
525
        This function returns when common_searcher has stopped searching for
641
526
        more nodes.
642
527
        """
685
570
                                 all_unique_searcher._iterations)
686
571
            unique_tip_searchers = next_unique_searchers
687
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
 
688
591
    def get_parent_map(self, revisions):
689
592
        """Get a map of key:parent_list for revisions.
690
593
 
910
813
                stop.add(parent_id)
911
814
        return found
912
815
 
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
 
 
933
816
    def find_unique_lca(self, left_revision, right_revision,
934
817
                        count_steps=False):
935
818
        """Find a unique LCA.
987
870
                yield (ghost, None)
988
871
            pending = next_pending
989
872
 
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
 
 
1009
873
    def iter_topo_order(self, revisions):
1010
874
        """Iterate through the input revisions in topological order.
1011
875
 
1013
877
        An ancestor may sort after a descendant if the relationship is not
1014
878
        visible in the supplied list of revisions.
1015
879
        """
1016
 
        from bzrlib import tsort
1017
880
        sorter = tsort.TopoSorter(self.get_parent_map(revisions))
1018
881
        return sorter.iter_topo_order()
1019
882
 
1027
890
        return set([candidate_descendant]) == self.heads(
1028
891
            [candidate_ancestor, candidate_descendant])
1029
892
 
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
 
 
1041
893
    def _search_for_extra_common(self, common, searchers):
1042
894
        """Make sure that unique nodes are genuinely unique.
1043
895
 
1316
1168
 
1317
1169
    def get_result(self):
1318
1170
        """Get a SearchResult for the current state of this searcher.
1319
 
 
 
1171
        
1320
1172
        :return: A SearchResult for this search so far. The SearchResult is
1321
1173
            static - the search can be advanced and the search result will not
1322
1174
            be invalidated or altered.
1326
1178
            # exclude keys for them. However, while we could have a second
1327
1179
            # look-ahead result buffer and shuffle things around, this method
1328
1180
            # is typically only called once per search - when memoising the
1329
 
            # results of the search.
 
1181
            # results of the search. 
1330
1182
            found, ghosts, next, parents = self._do_query(self._next_query)
1331
1183
            # pretend we didn't query: perhaps we should tweak _do_query to be
1332
1184
            # entirely stateless?
1373
1225
 
1374
1226
    def next_with_ghosts(self):
1375
1227
        """Return the next found ancestors, with ghosts split out.
1376
 
 
 
1228
        
1377
1229
        Ancestors are returned in the order they are seen in a breadth-first
1378
1230
        traversal.  No ancestor will be returned more than once. Ancestors are
1379
1231
        returned only after asking for their parents, which allows us to detect
1438
1290
 
1439
1291
    def find_seen_ancestors(self, revisions):
1440
1292
        """Find ancestors of these revisions that have already been seen.
1441
 
 
 
1293
        
1442
1294
        This function generally makes the assumption that querying for the
1443
1295
        parents of a node that has already been queried is reasonably cheap.
1444
1296
        (eg, not a round trip to a remote host).
1479
1331
        Remove any of the specified revisions from the search list.
1480
1332
 
1481
1333
        None of the specified revisions are required to be present in the
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.
 
1334
        search list.  In this case, the call is a no-op.
1489
1335
        """
1490
1336
        # TODO: does this help performance?
1491
1337
        # if not revisions:
1500
1346
                self._current_ghosts.intersection(revisions))
1501
1347
            self._current_present.difference_update(stopped)
1502
1348
            self._current_ghosts.difference_update(stopped)
1503
 
            # stopping 'x' should stop returning parents of 'x', but
 
1349
            # stopping 'x' should stop returning parents of 'x', but 
1504
1350
            # not if 'y' always references those same parents
1505
1351
            stop_rev_references = {}
1506
1352
            for rev in stopped_present:
1522
1368
                    stop_parents.add(rev_id)
1523
1369
            self._next_query.difference_update(stop_parents)
1524
1370
        self._stopped_keys.update(stopped)
1525
 
        self._stopped_keys.update(revisions)
1526
1371
        return stopped
1527
1372
 
1528
1373
    def start_searching(self, revisions):
1550
1395
            return revs, ghosts
1551
1396
 
1552
1397
 
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):
 
1398
class SearchResult(object):
1616
1399
    """The result of a breadth first search.
1617
1400
 
1618
1401
    A SearchResult provides the ability to reconstruct the search or access a
1630
1413
            a SearchResult from a smart server, in which case the keys list is
1631
1414
            not necessarily immediately available.
1632
1415
        """
1633
 
        self._recipe = ('search', start_keys, exclude_keys, key_count)
 
1416
        self._recipe = (start_keys, exclude_keys, key_count)
1634
1417
        self._keys = frozenset(keys)
1635
1418
 
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
 
 
1649
1419
    def get_recipe(self):
1650
1420
        """Return a recipe that can be used to replay this search.
1651
 
 
 
1421
        
1652
1422
        The recipe allows reconstruction of the same results at a later date
1653
1423
        without knowing all the found keys. The essential elements are a list
1654
 
        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
1655
1425
        results when ghosts are encountered by a search they are automatically
1656
1426
        added to the exclude list (or else ghost filling may alter the
1657
1427
        results).
1658
1428
 
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
 
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
1666
1436
            revision_count. If it does not, then additional revisions have been
1667
1437
            ghosted since the search was executed the first time and the second
1668
1438
            time.
1669
1439
        """
1670
1440
        return self._recipe
1671
1441
 
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
 
 
1678
1442
    def get_keys(self):
1679
1443
        """Return the keys found in this search.
1680
1444
 
1682
1446
        """
1683
1447
        return self._keys
1684
1448
 
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
 
 
1893
1449
 
1894
1450
def collapse_linear_regions(parent_map):
1895
1451
    """Collapse regions of the graph that are 'linear'.
1962
1518
            removed.add(node)
1963
1519
 
1964
1520
    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