~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: 2009-08-27 02:27:19 UTC
  • mfrom: (4634.3.19 gc-batching)
  • Revision ID: pqm@pqm.ubuntu.com-20090827022719-bl2yoqhpj3fcfczu
(andrew) Fix #402657: 2a fetch over dumb transport reads one group at
        a time.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007 Canonical Ltd
 
1
# Copyright (C) 2007, 2008, 2009 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
 
20
20
    debug,
21
21
    errors,
22
22
    revision,
23
 
    symbol_versioning,
24
23
    trace,
25
 
    tsort,
26
24
    )
27
 
from bzrlib.deprecated_graph import (node_distances, select_farthest)
 
25
from bzrlib.symbol_versioning import deprecated_function, deprecated_in
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
 
 
69
 
class _StackedParentsProvider(object):
70
 
 
 
66
@deprecated_function(deprecated_in((1, 16, 0)))
 
67
def _StackedParentsProvider(*args, **kwargs):
 
68
    return StackedParentsProvider(*args, **kwargs)
 
69
 
 
70
class StackedParentsProvider(object):
 
71
    """A parents provider which stacks (or unions) multiple providers.
 
72
    
 
73
    The providers are queries in the order of the provided parent_providers.
 
74
    """
 
75
    
71
76
    def __init__(self, parent_providers):
72
77
        self._parent_providers = parent_providers
73
78
 
74
79
    def __repr__(self):
75
 
        return "_StackedParentsProvider(%r)" % self._parent_providers
 
80
        return "%s(%r)" % (self.__class__.__name__, self._parent_providers)
76
81
 
77
82
    def get_parent_map(self, keys):
78
83
        """Get a mapping of keys => parents
99
104
 
100
105
 
101
106
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.
 
107
    """A parents provider which will cache the revision => parents as a dict.
 
108
 
 
109
    This is useful for providers which have an expensive look up.
 
110
 
 
111
    Either a ParentsProvider or a get_parent_map-like callback may be
 
112
    supplied.  If it provides extra un-asked-for parents, they will be cached,
 
113
    but filtered out of get_parent_map.
 
114
 
 
115
    The cache is enabled by default, but may be disabled and re-enabled.
105
116
    """
 
117
    def __init__(self, parent_provider=None, get_parent_map=None):
 
118
        """Constructor.
106
119
 
107
 
    def __init__(self, parent_provider):
 
120
        :param parent_provider: The ParentProvider to use.  It or
 
121
            get_parent_map must be supplied.
 
122
        :param get_parent_map: The get_parent_map callback to use.  It or
 
123
            parent_provider must be supplied.
 
124
        """
108
125
        self._real_provider = parent_provider
109
 
        # Theoretically we could use an LRUCache here
 
126
        if get_parent_map is None:
 
127
            self._get_parent_map = self._real_provider.get_parent_map
 
128
        else:
 
129
            self._get_parent_map = get_parent_map
 
130
        self._cache = None
 
131
        self.enable_cache(True)
 
132
 
 
133
    def __repr__(self):
 
134
        return "%s(%r)" % (self.__class__.__name__, self._real_provider)
 
135
 
 
136
    def enable_cache(self, cache_misses=True):
 
137
        """Enable cache."""
 
138
        if self._cache is not None:
 
139
            raise AssertionError('Cache enabled when already enabled.')
110
140
        self._cache = {}
111
 
 
112
 
    def __repr__(self):
113
 
        return "%s(%r)" % (self.__class__.__name__, self._real_provider)
 
141
        self._cache_misses = cache_misses
 
142
        self.missing_keys = set()
 
143
 
 
144
    def disable_cache(self):
 
145
        """Disable and clear the cache."""
 
146
        self._cache = None
 
147
        self._cache_misses = None
 
148
        self.missing_keys = set()
 
149
 
 
150
    def get_cached_map(self):
 
151
        """Return any cached get_parent_map values."""
 
152
        if self._cache is None:
 
153
            return None
 
154
        return dict(self._cache)
114
155
 
115
156
    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 = {}
 
157
        """See StackedParentsProvider.get_parent_map."""
122
158
        cache = self._cache
 
159
        if cache is None:
 
160
            cache = self._get_parent_map(keys)
 
161
        else:
 
162
            needed_revisions = set(key for key in keys if key not in cache)
 
163
            # Do not ask for negatively cached keys
 
164
            needed_revisions.difference_update(self.missing_keys)
 
165
            if needed_revisions:
 
166
                parent_map = self._get_parent_map(needed_revisions)
 
167
                cache.update(parent_map)
 
168
                if self._cache_misses:
 
169
                    for key in needed_revisions:
 
170
                        if key not in parent_map:
 
171
                            self.note_missing_key(key)
 
172
        result = {}
123
173
        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)
 
174
            value = cache.get(key)
 
175
            if value is not None:
 
176
                result[key] = value
 
177
        return result
130
178
 
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
 
179
    def note_missing_key(self, key):
 
180
        """Note that key is a missing key."""
 
181
        if self._cache_misses:
 
182
            self.missing_keys.add(key)
138
183
 
139
184
 
140
185
class Graph(object):
265
310
        # get there.
266
311
        return known_revnos[cur_tip] + num_steps
267
312
 
 
313
    def find_lefthand_distances(self, keys):
 
314
        """Find the distance to null for all the keys in keys.
 
315
 
 
316
        :param keys: keys to lookup.
 
317
        :return: A dict key->distance for all of keys.
 
318
        """
 
319
        # Optimisable by concurrent searching, but a random spread should get
 
320
        # some sort of hit rate.
 
321
        result = {}
 
322
        known_revnos = []
 
323
        ghosts = []
 
324
        for key in keys:
 
325
            try:
 
326
                known_revnos.append(
 
327
                    (key, self.find_distance_to_null(key, known_revnos)))
 
328
            except errors.GhostRevisionsHaveNoRevno:
 
329
                ghosts.append(key)
 
330
        for key in ghosts:
 
331
            known_revnos.append((key, -1))
 
332
        return dict(known_revnos)
 
333
 
268
334
    def find_unique_ancestors(self, unique_revision, common_revisions):
269
335
        """Find the unique ancestors for a revision versus others.
270
336
 
521
587
    def _refine_unique_nodes(self, unique_searcher, all_unique_searcher,
522
588
                             unique_tip_searchers, common_searcher):
523
589
        """Steps 5-8 of find_unique_ancestors.
524
 
        
 
590
 
525
591
        This function returns when common_searcher has stopped searching for
526
592
        more nodes.
527
593
        """
570
636
                                 all_unique_searcher._iterations)
571
637
            unique_tip_searchers = next_unique_searchers
572
638
 
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
639
    def get_parent_map(self, revisions):
592
640
        """Get a map of key:parent_list for revisions.
593
641
 
877
925
        An ancestor may sort after a descendant if the relationship is not
878
926
        visible in the supplied list of revisions.
879
927
        """
 
928
        from bzrlib import tsort
880
929
        sorter = tsort.TopoSorter(self.get_parent_map(revisions))
881
930
        return sorter.iter_topo_order()
882
931
 
890
939
        return set([candidate_descendant]) == self.heads(
891
940
            [candidate_ancestor, candidate_descendant])
892
941
 
 
942
    def is_between(self, revid, lower_bound_revid, upper_bound_revid):
 
943
        """Determine whether a revision is between two others.
 
944
 
 
945
        returns true if and only if:
 
946
        lower_bound_revid <= revid <= upper_bound_revid
 
947
        """
 
948
        return ((upper_bound_revid is None or
 
949
                    self.is_ancestor(revid, upper_bound_revid)) and
 
950
               (lower_bound_revid is None or
 
951
                    self.is_ancestor(lower_bound_revid, revid)))
 
952
 
893
953
    def _search_for_extra_common(self, common, searchers):
894
954
        """Make sure that unique nodes are genuinely unique.
895
955
 
1168
1228
 
1169
1229
    def get_result(self):
1170
1230
        """Get a SearchResult for the current state of this searcher.
1171
 
        
 
1231
 
1172
1232
        :return: A SearchResult for this search so far. The SearchResult is
1173
1233
            static - the search can be advanced and the search result will not
1174
1234
            be invalidated or altered.
1178
1238
            # exclude keys for them. However, while we could have a second
1179
1239
            # look-ahead result buffer and shuffle things around, this method
1180
1240
            # is typically only called once per search - when memoising the
1181
 
            # results of the search. 
 
1241
            # results of the search.
1182
1242
            found, ghosts, next, parents = self._do_query(self._next_query)
1183
1243
            # pretend we didn't query: perhaps we should tweak _do_query to be
1184
1244
            # entirely stateless?
1225
1285
 
1226
1286
    def next_with_ghosts(self):
1227
1287
        """Return the next found ancestors, with ghosts split out.
1228
 
        
 
1288
 
1229
1289
        Ancestors are returned in the order they are seen in a breadth-first
1230
1290
        traversal.  No ancestor will be returned more than once. Ancestors are
1231
1291
        returned only after asking for their parents, which allows us to detect
1290
1350
 
1291
1351
    def find_seen_ancestors(self, revisions):
1292
1352
        """Find ancestors of these revisions that have already been seen.
1293
 
        
 
1353
 
1294
1354
        This function generally makes the assumption that querying for the
1295
1355
        parents of a node that has already been queried is reasonably cheap.
1296
1356
        (eg, not a round trip to a remote host).
1331
1391
        Remove any of the specified revisions from the search list.
1332
1392
 
1333
1393
        None of the specified revisions are required to be present in the
1334
 
        search list.  In this case, the call is a no-op.
 
1394
        search list.
 
1395
 
 
1396
        It is okay to call stop_searching_any() for revisions which were seen
 
1397
        in previous iterations. It is the callers responsibility to call
 
1398
        find_seen_ancestors() to make sure that current search tips that are
 
1399
        ancestors of those revisions are also stopped.  All explicitly stopped
 
1400
        revisions will be excluded from the search result's get_keys(), though.
1335
1401
        """
1336
1402
        # TODO: does this help performance?
1337
1403
        # if not revisions:
1346
1412
                self._current_ghosts.intersection(revisions))
1347
1413
            self._current_present.difference_update(stopped)
1348
1414
            self._current_ghosts.difference_update(stopped)
1349
 
            # stopping 'x' should stop returning parents of 'x', but 
 
1415
            # stopping 'x' should stop returning parents of 'x', but
1350
1416
            # not if 'y' always references those same parents
1351
1417
            stop_rev_references = {}
1352
1418
            for rev in stopped_present:
1368
1434
                    stop_parents.add(rev_id)
1369
1435
            self._next_query.difference_update(stop_parents)
1370
1436
        self._stopped_keys.update(stopped)
 
1437
        self._stopped_keys.update(revisions)
1371
1438
        return stopped
1372
1439
 
1373
1440
    def start_searching(self, revisions):
1413
1480
            a SearchResult from a smart server, in which case the keys list is
1414
1481
            not necessarily immediately available.
1415
1482
        """
1416
 
        self._recipe = (start_keys, exclude_keys, key_count)
 
1483
        self._recipe = ('search', start_keys, exclude_keys, key_count)
1417
1484
        self._keys = frozenset(keys)
1418
1485
 
1419
1486
    def get_recipe(self):
1420
1487
        """Return a recipe that can be used to replay this search.
1421
 
        
 
1488
 
1422
1489
        The recipe allows reconstruction of the same results at a later date
1423
1490
        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
 
1491
        of keys to start and to stop at. In order to give reproducible
1425
1492
        results when ghosts are encountered by a search they are automatically
1426
1493
        added to the exclude list (or else ghost filling may alter the
1427
1494
        results).
1428
1495
 
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
 
1496
        :return: A tuple ('search', start_keys_set, exclude_keys_set,
 
1497
            revision_count). To recreate the results of this search, create a
 
1498
            breadth first searcher on the same graph starting at start_keys.
 
1499
            Then call next() (or next_with_ghosts()) repeatedly, and on every
 
1500
            result, call stop_searching_any on any keys from the exclude_keys
 
1501
            set. The revision_count value acts as a trivial cross-check - the
 
1502
            found revisions of the new search should have as many elements as
1436
1503
            revision_count. If it does not, then additional revisions have been
1437
1504
            ghosted since the search was executed the first time and the second
1438
1505
            time.
1446
1513
        """
1447
1514
        return self._keys
1448
1515
 
 
1516
    def is_empty(self):
 
1517
        """Return false if the search lists 1 or more revisions."""
 
1518
        return self._recipe[3] == 0
 
1519
 
 
1520
    def refine(self, seen, referenced):
 
1521
        """Create a new search by refining this search.
 
1522
 
 
1523
        :param seen: Revisions that have been satisfied.
 
1524
        :param referenced: Revision references observed while satisfying some
 
1525
            of this search.
 
1526
        """
 
1527
        start = self._recipe[1]
 
1528
        exclude = self._recipe[2]
 
1529
        count = self._recipe[3]
 
1530
        keys = self.get_keys()
 
1531
        # New heads = referenced + old heads - seen things - exclude
 
1532
        pending_refs = set(referenced)
 
1533
        pending_refs.update(start)
 
1534
        pending_refs.difference_update(seen)
 
1535
        pending_refs.difference_update(exclude)
 
1536
        # New exclude = old exclude + satisfied heads
 
1537
        seen_heads = start.intersection(seen)
 
1538
        exclude.update(seen_heads)
 
1539
        # keys gets seen removed
 
1540
        keys = keys - seen
 
1541
        # length is reduced by len(seen)
 
1542
        count -= len(seen)
 
1543
        return SearchResult(pending_refs, exclude, count, keys)
 
1544
 
 
1545
 
 
1546
class PendingAncestryResult(object):
 
1547
    """A search result that will reconstruct the ancestry for some graph heads.
 
1548
 
 
1549
    Unlike SearchResult, this doesn't hold the complete search result in
 
1550
    memory, it just holds a description of how to generate it.
 
1551
    """
 
1552
 
 
1553
    def __init__(self, heads, repo):
 
1554
        """Constructor.
 
1555
 
 
1556
        :param heads: an iterable of graph heads.
 
1557
        :param repo: a repository to use to generate the ancestry for the given
 
1558
            heads.
 
1559
        """
 
1560
        self.heads = frozenset(heads)
 
1561
        self.repo = repo
 
1562
 
 
1563
    def get_recipe(self):
 
1564
        """Return a recipe that can be used to replay this search.
 
1565
 
 
1566
        The recipe allows reconstruction of the same results at a later date.
 
1567
 
 
1568
        :seealso SearchResult.get_recipe:
 
1569
 
 
1570
        :return: A tuple ('proxy-search', start_keys_set, set(), -1)
 
1571
            To recreate this result, create a PendingAncestryResult with the
 
1572
            start_keys_set.
 
1573
        """
 
1574
        return ('proxy-search', self.heads, set(), -1)
 
1575
 
 
1576
    def get_keys(self):
 
1577
        """See SearchResult.get_keys.
 
1578
 
 
1579
        Returns all the keys for the ancestry of the heads, excluding
 
1580
        NULL_REVISION.
 
1581
        """
 
1582
        return self._get_keys(self.repo.get_graph())
 
1583
 
 
1584
    def _get_keys(self, graph):
 
1585
        NULL_REVISION = revision.NULL_REVISION
 
1586
        keys = [key for (key, parents) in graph.iter_ancestry(self.heads)
 
1587
                if key != NULL_REVISION and parents is not None]
 
1588
        return keys
 
1589
 
 
1590
    def is_empty(self):
 
1591
        """Return false if the search lists 1 or more revisions."""
 
1592
        if revision.NULL_REVISION in self.heads:
 
1593
            return len(self.heads) == 1
 
1594
        else:
 
1595
            return len(self.heads) == 0
 
1596
 
 
1597
    def refine(self, seen, referenced):
 
1598
        """Create a new search by refining this search.
 
1599
 
 
1600
        :param seen: Revisions that have been satisfied.
 
1601
        :param referenced: Revision references observed while satisfying some
 
1602
            of this search.
 
1603
        """
 
1604
        referenced = self.heads.union(referenced)
 
1605
        return PendingAncestryResult(referenced - seen, self.repo)
 
1606
 
1449
1607
 
1450
1608
def collapse_linear_regions(parent_map):
1451
1609
    """Collapse regions of the graph that are 'linear'.
1518
1676
            removed.add(node)
1519
1677
 
1520
1678
    return result
 
1679
 
 
1680
 
 
1681
_counters = [0,0,0,0,0,0,0]
 
1682
try:
 
1683
    from bzrlib._known_graph_pyx import KnownGraph
 
1684
except ImportError:
 
1685
    from bzrlib._known_graph_py import KnownGraph