~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/graph.py

  • Committer: Tarmac
  • Author(s): Vincent Ladeuil
  • Date: 2017-01-30 14:42:05 UTC
  • mfrom: (6620.1.1 trunk)
  • Revision ID: tarmac-20170130144205-r8fh2xpmiuxyozpv
Merge  2.7 into trunk including fix for bug #1657238 [r=vila]

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
 
 
17
from __future__ import absolute_import
16
18
 
17
19
import time
18
20
 
19
21
from bzrlib import (
20
22
    debug,
21
23
    errors,
 
24
    osutils,
22
25
    revision,
23
 
    symbol_versioning,
24
26
    trace,
25
 
    tsort,
26
27
    )
27
 
from bzrlib.deprecated_graph import (node_distances, select_farthest)
28
28
 
29
29
STEP_UNIQUE_SEARCHER_EVERY = 5
30
30
 
60
60
    def __repr__(self):
61
61
        return 'DictParentsProvider(%r)' % self.ancestry
62
62
 
 
63
    # Note: DictParentsProvider does not implement get_cached_parent_map
 
64
    #       Arguably, the data is clearly cached in memory. However, this class
 
65
    #       is mostly used for testing, and it keeps the tests clean to not
 
66
    #       change it.
 
67
 
63
68
    def get_parent_map(self, keys):
64
 
        """See _StackedParentsProvider.get_parent_map"""
 
69
        """See StackedParentsProvider.get_parent_map"""
65
70
        ancestry = self.ancestry
66
 
        return dict((k, ancestry[k]) for k in keys if k in ancestry)
67
 
 
68
 
 
69
 
class _StackedParentsProvider(object):
 
71
        return dict([(k, ancestry[k]) for k in keys if k in ancestry])
 
72
 
 
73
 
 
74
class StackedParentsProvider(object):
 
75
    """A parents provider which stacks (or unions) multiple providers.
 
76
 
 
77
    The providers are queries in the order of the provided parent_providers.
 
78
    """
70
79
 
71
80
    def __init__(self, parent_providers):
72
81
        self._parent_providers = parent_providers
73
82
 
74
83
    def __repr__(self):
75
 
        return "_StackedParentsProvider(%r)" % self._parent_providers
 
84
        return "%s(%r)" % (self.__class__.__name__, self._parent_providers)
76
85
 
77
86
    def get_parent_map(self, keys):
78
87
        """Get a mapping of keys => parents
89
98
        """
90
99
        found = {}
91
100
        remaining = set(keys)
 
101
        # This adds getattr() overhead to each get_parent_map call. However,
 
102
        # this is StackedParentsProvider, which means we're dealing with I/O
 
103
        # (either local indexes, or remote RPCs), so CPU overhead should be
 
104
        # minimal.
 
105
        for parents_provider in self._parent_providers:
 
106
            get_cached = getattr(parents_provider, 'get_cached_parent_map',
 
107
                                 None)
 
108
            if get_cached is None:
 
109
                continue
 
110
            new_found = get_cached(remaining)
 
111
            found.update(new_found)
 
112
            remaining.difference_update(new_found)
 
113
            if not remaining:
 
114
                break
 
115
        if not remaining:
 
116
            return found
92
117
        for parents_provider in self._parent_providers:
93
118
            new_found = parents_provider.get_parent_map(remaining)
94
119
            found.update(new_found)
99
124
 
100
125
 
101
126
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.
 
127
    """A parents provider which will cache the revision => parents as a dict.
 
128
 
 
129
    This is useful for providers which have an expensive look up.
 
130
 
 
131
    Either a ParentsProvider or a get_parent_map-like callback may be
 
132
    supplied.  If it provides extra un-asked-for parents, they will be cached,
 
133
    but filtered out of get_parent_map.
 
134
 
 
135
    The cache is enabled by default, but may be disabled and re-enabled.
105
136
    """
 
137
    def __init__(self, parent_provider=None, get_parent_map=None):
 
138
        """Constructor.
106
139
 
107
 
    def __init__(self, parent_provider):
 
140
        :param parent_provider: The ParentProvider to use.  It or
 
141
            get_parent_map must be supplied.
 
142
        :param get_parent_map: The get_parent_map callback to use.  It or
 
143
            parent_provider must be supplied.
 
144
        """
108
145
        self._real_provider = parent_provider
109
 
        # Theoretically we could use an LRUCache here
 
146
        if get_parent_map is None:
 
147
            self._get_parent_map = self._real_provider.get_parent_map
 
148
        else:
 
149
            self._get_parent_map = get_parent_map
 
150
        self._cache = None
 
151
        self.enable_cache(True)
 
152
 
 
153
    def __repr__(self):
 
154
        return "%s(%r)" % (self.__class__.__name__, self._real_provider)
 
155
 
 
156
    def enable_cache(self, cache_misses=True):
 
157
        """Enable cache."""
 
158
        if self._cache is not None:
 
159
            raise AssertionError('Cache enabled when already enabled.')
110
160
        self._cache = {}
 
161
        self._cache_misses = cache_misses
 
162
        self.missing_keys = set()
 
163
 
 
164
    def disable_cache(self):
 
165
        """Disable and clear the cache."""
 
166
        self._cache = None
 
167
        self._cache_misses = None
 
168
        self.missing_keys = set()
 
169
 
 
170
    def get_cached_map(self):
 
171
        """Return any cached get_parent_map values."""
 
172
        if self._cache is None:
 
173
            return None
 
174
        return dict(self._cache)
 
175
 
 
176
    def get_cached_parent_map(self, keys):
 
177
        """Return items from the cache.
 
178
 
 
179
        This returns the same info as get_parent_map, but explicitly does not
 
180
        invoke the supplied ParentsProvider to search for uncached values.
 
181
        """
 
182
        cache = self._cache
 
183
        if cache is None:
 
184
            return {}
 
185
        return dict([(key, cache[key]) for key in keys if key in cache])
 
186
 
 
187
    def get_parent_map(self, keys):
 
188
        """See StackedParentsProvider.get_parent_map."""
 
189
        cache = self._cache
 
190
        if cache is None:
 
191
            cache = self._get_parent_map(keys)
 
192
        else:
 
193
            needed_revisions = set(key for key in keys if key not in cache)
 
194
            # Do not ask for negatively cached keys
 
195
            needed_revisions.difference_update(self.missing_keys)
 
196
            if needed_revisions:
 
197
                parent_map = self._get_parent_map(needed_revisions)
 
198
                cache.update(parent_map)
 
199
                if self._cache_misses:
 
200
                    for key in needed_revisions:
 
201
                        if key not in parent_map:
 
202
                            self.note_missing_key(key)
 
203
        result = {}
 
204
        for key in keys:
 
205
            value = cache.get(key)
 
206
            if value is not None:
 
207
                result[key] = value
 
208
        return result
 
209
 
 
210
    def note_missing_key(self, key):
 
211
        """Note that key is a missing key."""
 
212
        if self._cache_misses:
 
213
            self.missing_keys.add(key)
 
214
 
 
215
 
 
216
class CallableToParentsProviderAdapter(object):
 
217
    """A parents provider that adapts any callable to the parents provider API.
 
218
 
 
219
    i.e. it accepts calls to self.get_parent_map and relays them to the
 
220
    callable it was constructed with.
 
221
    """
 
222
 
 
223
    def __init__(self, a_callable):
 
224
        self.callable = a_callable
111
225
 
112
226
    def __repr__(self):
113
 
        return "%s(%r)" % (self.__class__.__name__, self._real_provider)
 
227
        return "%s(%r)" % (self.__class__.__name__, self.callable)
114
228
 
115
229
    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 = {}
122
 
        cache = self._cache
123
 
        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
 
230
        return self.callable(keys)
138
231
 
139
232
 
140
233
class Graph(object):
191
284
        common ancestor of all border ancestors, because this shows that it
192
285
        cannot be a descendant of any border ancestor.
193
286
 
194
 
        The scaling of this operation should be proportional to
 
287
        The scaling of this operation should be proportional to:
 
288
 
195
289
        1. The number of uncommon ancestors
196
290
        2. The number of border ancestors
197
291
        3. The length of the shortest path between a border ancestor and an
212
306
        right = searchers[1].seen
213
307
        return (left.difference(right), right.difference(left))
214
308
 
 
309
    def find_descendants(self, old_key, new_key):
 
310
        """Find descendants of old_key that are ancestors of new_key."""
 
311
        child_map = self.get_child_map(self._find_descendant_ancestors(
 
312
            old_key, new_key))
 
313
        graph = Graph(DictParentsProvider(child_map))
 
314
        searcher = graph._make_breadth_first_searcher([old_key])
 
315
        list(searcher)
 
316
        return searcher.seen
 
317
 
 
318
    def _find_descendant_ancestors(self, old_key, new_key):
 
319
        """Find ancestors of new_key that may be descendants of old_key."""
 
320
        stop = self._make_breadth_first_searcher([old_key])
 
321
        descendants = self._make_breadth_first_searcher([new_key])
 
322
        for revisions in descendants:
 
323
            old_stop = stop.seen.intersection(revisions)
 
324
            descendants.stop_searching_any(old_stop)
 
325
            seen_stop = descendants.find_seen_ancestors(stop.step())
 
326
            descendants.stop_searching_any(seen_stop)
 
327
        return descendants.seen.difference(stop.seen)
 
328
 
 
329
    def get_child_map(self, keys):
 
330
        """Get a mapping from parents to children of the specified keys.
 
331
 
 
332
        This is simply the inversion of get_parent_map.  Only supplied keys
 
333
        will be discovered as children.
 
334
        :return: a dict of key:child_list for keys.
 
335
        """
 
336
        parent_map = self._parents_provider.get_parent_map(keys)
 
337
        parent_child = {}
 
338
        for child, parents in sorted(parent_map.items()):
 
339
            for parent in parents:
 
340
                parent_child.setdefault(parent, []).append(child)
 
341
        return parent_child
 
342
 
215
343
    def find_distance_to_null(self, target_revision_id, known_revision_ids):
216
344
        """Find the left-hand distance to the NULL_REVISION.
217
345
 
265
393
        # get there.
266
394
        return known_revnos[cur_tip] + num_steps
267
395
 
 
396
    def find_lefthand_distances(self, keys):
 
397
        """Find the distance to null for all the keys in keys.
 
398
 
 
399
        :param keys: keys to lookup.
 
400
        :return: A dict key->distance for all of keys.
 
401
        """
 
402
        # Optimisable by concurrent searching, but a random spread should get
 
403
        # some sort of hit rate.
 
404
        result = {}
 
405
        known_revnos = []
 
406
        ghosts = []
 
407
        for key in keys:
 
408
            try:
 
409
                known_revnos.append(
 
410
                    (key, self.find_distance_to_null(key, known_revnos)))
 
411
            except errors.GhostRevisionsHaveNoRevno:
 
412
                ghosts.append(key)
 
413
        for key in ghosts:
 
414
            known_revnos.append((key, -1))
 
415
        return dict(known_revnos)
 
416
 
268
417
    def find_unique_ancestors(self, unique_revision, common_revisions):
269
418
        """Find the unique ancestors for a revision versus others.
270
419
 
274
423
 
275
424
        :param unique_revision: The revision_id whose ancestry we are
276
425
            interested in.
277
 
            XXX: Would this API be better if we allowed multiple revisions on
278
 
                 to be searched here?
 
426
            (XXX: Would this API be better if we allowed multiple revisions on
 
427
            to be searched here?)
279
428
        :param common_revisions: Revision_ids of ancestries to exclude.
280
429
        :return: A set of revisions in the ancestry of unique_revision
281
430
        """
521
670
    def _refine_unique_nodes(self, unique_searcher, all_unique_searcher,
522
671
                             unique_tip_searchers, common_searcher):
523
672
        """Steps 5-8 of find_unique_ancestors.
524
 
        
 
673
 
525
674
        This function returns when common_searcher has stopped searching for
526
675
        more nodes.
527
676
        """
570
719
                                 all_unique_searcher._iterations)
571
720
            unique_tip_searchers = next_unique_searchers
572
721
 
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
722
    def get_parent_map(self, revisions):
592
723
        """Get a map of key:parent_list for revisions.
593
724
 
813
944
                stop.add(parent_id)
814
945
        return found
815
946
 
 
947
    def find_lefthand_merger(self, merged_key, tip_key):
 
948
        """Find the first lefthand ancestor of tip_key that merged merged_key.
 
949
 
 
950
        We do this by first finding the descendants of merged_key, then
 
951
        walking through the lefthand ancestry of tip_key until we find a key
 
952
        that doesn't descend from merged_key.  Its child is the key that
 
953
        merged merged_key.
 
954
 
 
955
        :return: The first lefthand ancestor of tip_key to merge merged_key.
 
956
            merged_key if it is a lefthand ancestor of tip_key.
 
957
            None if no ancestor of tip_key merged merged_key.
 
958
        """
 
959
        descendants = self.find_descendants(merged_key, tip_key)
 
960
        candidate_iterator = self.iter_lefthand_ancestry(tip_key)
 
961
        last_candidate = None
 
962
        for candidate in candidate_iterator:
 
963
            if candidate not in descendants:
 
964
                return last_candidate
 
965
            last_candidate = candidate
 
966
 
816
967
    def find_unique_lca(self, left_revision, right_revision,
817
968
                        count_steps=False):
818
969
        """Find a unique LCA.
870
1021
                yield (ghost, None)
871
1022
            pending = next_pending
872
1023
 
 
1024
    def iter_lefthand_ancestry(self, start_key, stop_keys=None):
 
1025
        if stop_keys is None:
 
1026
            stop_keys = ()
 
1027
        next_key = start_key
 
1028
        def get_parents(key):
 
1029
            try:
 
1030
                return self._parents_provider.get_parent_map([key])[key]
 
1031
            except KeyError:
 
1032
                raise errors.RevisionNotPresent(next_key, self)
 
1033
        while True:
 
1034
            if next_key in stop_keys:
 
1035
                return
 
1036
            parents = get_parents(next_key)
 
1037
            yield next_key
 
1038
            if len(parents) == 0:
 
1039
                return
 
1040
            else:
 
1041
                next_key = parents[0]
 
1042
 
873
1043
    def iter_topo_order(self, revisions):
874
1044
        """Iterate through the input revisions in topological order.
875
1045
 
877
1047
        An ancestor may sort after a descendant if the relationship is not
878
1048
        visible in the supplied list of revisions.
879
1049
        """
 
1050
        from bzrlib import tsort
880
1051
        sorter = tsort.TopoSorter(self.get_parent_map(revisions))
881
1052
        return sorter.iter_topo_order()
882
1053
 
890
1061
        return set([candidate_descendant]) == self.heads(
891
1062
            [candidate_ancestor, candidate_descendant])
892
1063
 
 
1064
    def is_between(self, revid, lower_bound_revid, upper_bound_revid):
 
1065
        """Determine whether a revision is between two others.
 
1066
 
 
1067
        returns true if and only if:
 
1068
        lower_bound_revid <= revid <= upper_bound_revid
 
1069
        """
 
1070
        return ((upper_bound_revid is None or
 
1071
                    self.is_ancestor(revid, upper_bound_revid)) and
 
1072
               (lower_bound_revid is None or
 
1073
                    self.is_ancestor(lower_bound_revid, revid)))
 
1074
 
893
1075
    def _search_for_extra_common(self, common, searchers):
894
1076
        """Make sure that unique nodes are genuinely unique.
895
1077
 
1166
1348
        return ('_BreadthFirstSearcher(iterations=%d, %s,'
1167
1349
                ' seen=%r)' % (self._iterations, search, list(self.seen)))
1168
1350
 
1169
 
    def get_result(self):
1170
 
        """Get a SearchResult for the current state of this searcher.
1171
 
        
1172
 
        :return: A SearchResult for this search so far. The SearchResult is
1173
 
            static - the search can be advanced and the search result will not
1174
 
            be invalidated or altered.
 
1351
    def get_state(self):
 
1352
        """Get the current state of this searcher.
 
1353
 
 
1354
        :return: Tuple with started keys, excludes and included keys
1175
1355
        """
1176
1356
        if self._returning == 'next':
1177
1357
            # We have to know the current nodes children to be able to list the
1178
1358
            # exclude keys for them. However, while we could have a second
1179
1359
            # look-ahead result buffer and shuffle things around, this method
1180
1360
            # is typically only called once per search - when memoising the
1181
 
            # results of the search. 
 
1361
            # results of the search.
1182
1362
            found, ghosts, next, parents = self._do_query(self._next_query)
1183
1363
            # pretend we didn't query: perhaps we should tweak _do_query to be
1184
1364
            # entirely stateless?
1188
1368
            next_query = self._next_query
1189
1369
        excludes = self._stopped_keys.union(next_query)
1190
1370
        included_keys = self.seen.difference(excludes)
1191
 
        return SearchResult(self._started_keys, excludes, len(included_keys),
 
1371
        return self._started_keys, excludes, included_keys
 
1372
 
 
1373
    def _get_result(self):
 
1374
        """Get a SearchResult for the current state of this searcher.
 
1375
 
 
1376
        :return: A SearchResult for this search so far. The SearchResult is
 
1377
            static - the search can be advanced and the search result will not
 
1378
            be invalidated or altered.
 
1379
        """
 
1380
        from bzrlib.vf_search import SearchResult
 
1381
        (started_keys, excludes, included_keys) = self.get_state()
 
1382
        return SearchResult(started_keys, excludes, len(included_keys),
1192
1383
            included_keys)
1193
1384
 
1194
1385
    def step(self):
1225
1416
 
1226
1417
    def next_with_ghosts(self):
1227
1418
        """Return the next found ancestors, with ghosts split out.
1228
 
        
 
1419
 
1229
1420
        Ancestors are returned in the order they are seen in a breadth-first
1230
1421
        traversal.  No ancestor will be returned more than once. Ancestors are
1231
1422
        returned only after asking for their parents, which allows us to detect
1271
1462
        parents_of_found = set()
1272
1463
        # revisions may contain nodes that point to other nodes in revisions:
1273
1464
        # we want to filter them out.
1274
 
        self.seen.update(revisions)
 
1465
        seen = self.seen
 
1466
        seen.update(revisions)
1275
1467
        parent_map = self._parents_provider.get_parent_map(revisions)
1276
1468
        found_revisions.update(parent_map)
1277
1469
        for rev_id, parents in parent_map.iteritems():
1278
1470
            if parents is None:
1279
1471
                continue
1280
 
            new_found_parents = [p for p in parents if p not in self.seen]
 
1472
            new_found_parents = [p for p in parents if p not in seen]
1281
1473
            if new_found_parents:
1282
1474
                # Calling set.update() with an empty generator is actually
1283
1475
                # rather expensive.
1290
1482
 
1291
1483
    def find_seen_ancestors(self, revisions):
1292
1484
        """Find ancestors of these revisions that have already been seen.
1293
 
        
 
1485
 
1294
1486
        This function generally makes the assumption that querying for the
1295
1487
        parents of a node that has already been queried is reasonably cheap.
1296
1488
        (eg, not a round trip to a remote host).
1352
1544
                self._current_ghosts.intersection(revisions))
1353
1545
            self._current_present.difference_update(stopped)
1354
1546
            self._current_ghosts.difference_update(stopped)
1355
 
            # stopping 'x' should stop returning parents of 'x', but 
 
1547
            # stopping 'x' should stop returning parents of 'x', but
1356
1548
            # not if 'y' always references those same parents
1357
1549
            stop_rev_references = {}
1358
1550
            for rev in stopped_present:
1374
1566
                    stop_parents.add(rev_id)
1375
1567
            self._next_query.difference_update(stop_parents)
1376
1568
        self._stopped_keys.update(stopped)
1377
 
        self._stopped_keys.update(revisions - set([revision.NULL_REVISION]))
 
1569
        self._stopped_keys.update(revisions)
1378
1570
        return stopped
1379
1571
 
1380
1572
    def start_searching(self, revisions):
1402
1594
            return revs, ghosts
1403
1595
 
1404
1596
 
1405
 
class SearchResult(object):
1406
 
    """The result of a breadth first search.
1407
 
 
1408
 
    A SearchResult provides the ability to reconstruct the search or access a
1409
 
    set of the keys the search found.
1410
 
    """
1411
 
 
1412
 
    def __init__(self, start_keys, exclude_keys, key_count, keys):
1413
 
        """Create a SearchResult.
1414
 
 
1415
 
        :param start_keys: The keys the search started at.
1416
 
        :param exclude_keys: The keys the search excludes.
1417
 
        :param key_count: The total number of keys (from start to but not
1418
 
            including exclude).
1419
 
        :param keys: The keys the search found. Note that in future we may get
1420
 
            a SearchResult from a smart server, in which case the keys list is
1421
 
            not necessarily immediately available.
1422
 
        """
1423
 
        self._recipe = (start_keys, exclude_keys, key_count)
1424
 
        self._keys = frozenset(keys)
1425
 
 
1426
 
    def get_recipe(self):
1427
 
        """Return a recipe that can be used to replay this search.
1428
 
        
1429
 
        The recipe allows reconstruction of the same results at a later date
1430
 
        without knowing all the found keys. The essential elements are a list
1431
 
        of keys to start and and to stop at. In order to give reproducible
1432
 
        results when ghosts are encountered by a search they are automatically
1433
 
        added to the exclude list (or else ghost filling may alter the
1434
 
        results).
1435
 
 
1436
 
        :return: A tuple (start_keys_set, exclude_keys_set, revision_count). To
1437
 
            recreate the results of this search, create a breadth first
1438
 
            searcher on the same graph starting at start_keys. Then call next()
1439
 
            (or next_with_ghosts()) repeatedly, and on every result, call
1440
 
            stop_searching_any on any keys from the exclude_keys set. The
1441
 
            revision_count value acts as a trivial cross-check - the found
1442
 
            revisions of the new search should have as many elements as
1443
 
            revision_count. If it does not, then additional revisions have been
1444
 
            ghosted since the search was executed the first time and the second
1445
 
            time.
1446
 
        """
1447
 
        return self._recipe
1448
 
 
1449
 
    def get_keys(self):
1450
 
        """Return the keys found in this search.
1451
 
 
1452
 
        :return: A set of keys.
1453
 
        """
1454
 
        return self._keys
 
1597
def invert_parent_map(parent_map):
 
1598
    """Given a map from child => parents, create a map of parent=>children"""
 
1599
    child_map = {}
 
1600
    for child, parents in parent_map.iteritems():
 
1601
        for p in parents:
 
1602
            # Any given parent is likely to have only a small handful
 
1603
            # of children, many will have only one. So we avoid mem overhead of
 
1604
            # a list, in exchange for extra copying of tuples
 
1605
            if p not in child_map:
 
1606
                child_map[p] = (child,)
 
1607
            else:
 
1608
                child_map[p] = child_map[p] + (child,)
 
1609
    return child_map
1455
1610
 
1456
1611
 
1457
1612
def collapse_linear_regions(parent_map):
1525
1680
            removed.add(node)
1526
1681
 
1527
1682
    return result
 
1683
 
 
1684
 
 
1685
class GraphThunkIdsToKeys(object):
 
1686
    """Forwards calls about 'ids' to be about keys internally."""
 
1687
 
 
1688
    def __init__(self, graph):
 
1689
        self._graph = graph
 
1690
 
 
1691
    def topo_sort(self):
 
1692
        return [r for (r,) in self._graph.topo_sort()]
 
1693
 
 
1694
    def heads(self, ids):
 
1695
        """See Graph.heads()"""
 
1696
        as_keys = [(i,) for i in ids]
 
1697
        head_keys = self._graph.heads(as_keys)
 
1698
        return set([h[0] for h in head_keys])
 
1699
 
 
1700
    def merge_sort(self, tip_revision):
 
1701
        nodes = self._graph.merge_sort((tip_revision,))
 
1702
        for node in nodes:
 
1703
            node.key = node.key[0]
 
1704
        return nodes
 
1705
 
 
1706
    def add_node(self, revision, parents):
 
1707
        self._graph.add_node((revision,), [(p,) for p in parents])
 
1708
 
 
1709
 
 
1710
_counters = [0,0,0,0,0,0,0]
 
1711
try:
 
1712
    from bzrlib._known_graph_pyx import KnownGraph
 
1713
except ImportError, e:
 
1714
    osutils.failed_to_load_extension(e)
 
1715
    from bzrlib._known_graph_py import KnownGraph