~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/graph.py

  • Committer: Patch Queue Manager
  • Date: 2016-04-21 05:06:57 UTC
  • mfrom: (6603.4.1 bzr)
  • Revision ID: pqm@pqm.ubuntu.com-20160421050657-ygnzfybewvudf1j9
(richard-wilbur) Use initial_comment as commit_message for lp_propose.(Shawn
 Wang) (Shawn Wang)

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
 
766
897
            common_walker.start_searching(new_common)
767
898
        return candidate_heads
768
899
 
 
900
    def find_merge_order(self, tip_revision_id, lca_revision_ids):
 
901
        """Find the order that each revision was merged into tip.
 
902
 
 
903
        This basically just walks backwards with a stack, and walks left-first
 
904
        until it finds a node to stop.
 
905
        """
 
906
        if len(lca_revision_ids) == 1:
 
907
            return list(lca_revision_ids)
 
908
        looking_for = set(lca_revision_ids)
 
909
        # TODO: Is there a way we could do this "faster" by batching up the
 
910
        # get_parent_map requests?
 
911
        # TODO: Should we also be culling the ancestry search right away? We
 
912
        # could add looking_for to the "stop" list, and walk their
 
913
        # ancestry in batched mode. The flip side is it might mean we walk a
 
914
        # lot of "stop" nodes, rather than only the minimum.
 
915
        # Then again, without it we may trace back into ancestry we could have
 
916
        # stopped early.
 
917
        stack = [tip_revision_id]
 
918
        found = []
 
919
        stop = set()
 
920
        while stack and looking_for:
 
921
            next = stack.pop()
 
922
            stop.add(next)
 
923
            if next in looking_for:
 
924
                found.append(next)
 
925
                looking_for.remove(next)
 
926
                if len(looking_for) == 1:
 
927
                    found.append(looking_for.pop())
 
928
                    break
 
929
                continue
 
930
            parent_ids = self.get_parent_map([next]).get(next, None)
 
931
            if not parent_ids: # Ghost, nothing to search here
 
932
                continue
 
933
            for parent_id in reversed(parent_ids):
 
934
                # TODO: (performance) We see the parent at this point, but we
 
935
                #       wait to mark it until later to make sure we get left
 
936
                #       parents before right parents. However, instead of
 
937
                #       waiting until we have traversed enough parents, we
 
938
                #       could instead note that we've found it, and once all
 
939
                #       parents are in the stack, just reverse iterate the
 
940
                #       stack for them.
 
941
                if parent_id not in stop:
 
942
                    # this will need to be searched
 
943
                    stack.append(parent_id)
 
944
                stop.add(parent_id)
 
945
        return found
 
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
 
769
967
    def find_unique_lca(self, left_revision, right_revision,
770
968
                        count_steps=False):
771
969
        """Find a unique LCA.
823
1021
                yield (ghost, None)
824
1022
            pending = next_pending
825
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
 
826
1043
    def iter_topo_order(self, revisions):
827
1044
        """Iterate through the input revisions in topological order.
828
1045
 
830
1047
        An ancestor may sort after a descendant if the relationship is not
831
1048
        visible in the supplied list of revisions.
832
1049
        """
 
1050
        from bzrlib import tsort
833
1051
        sorter = tsort.TopoSorter(self.get_parent_map(revisions))
834
1052
        return sorter.iter_topo_order()
835
1053
 
843
1061
        return set([candidate_descendant]) == self.heads(
844
1062
            [candidate_ancestor, candidate_descendant])
845
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
 
846
1075
    def _search_for_extra_common(self, common, searchers):
847
1076
        """Make sure that unique nodes are genuinely unique.
848
1077
 
1119
1348
        return ('_BreadthFirstSearcher(iterations=%d, %s,'
1120
1349
                ' seen=%r)' % (self._iterations, search, list(self.seen)))
1121
1350
 
1122
 
    def get_result(self):
1123
 
        """Get a SearchResult for the current state of this searcher.
1124
 
        
1125
 
        :return: A SearchResult for this search so far. The SearchResult is
1126
 
            static - the search can be advanced and the search result will not
1127
 
            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
1128
1355
        """
1129
1356
        if self._returning == 'next':
1130
1357
            # We have to know the current nodes children to be able to list the
1131
1358
            # exclude keys for them. However, while we could have a second
1132
1359
            # look-ahead result buffer and shuffle things around, this method
1133
1360
            # is typically only called once per search - when memoising the
1134
 
            # results of the search. 
 
1361
            # results of the search.
1135
1362
            found, ghosts, next, parents = self._do_query(self._next_query)
1136
1363
            # pretend we didn't query: perhaps we should tweak _do_query to be
1137
1364
            # entirely stateless?
1141
1368
            next_query = self._next_query
1142
1369
        excludes = self._stopped_keys.union(next_query)
1143
1370
        included_keys = self.seen.difference(excludes)
1144
 
        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),
1145
1383
            included_keys)
1146
1384
 
1147
1385
    def step(self):
1178
1416
 
1179
1417
    def next_with_ghosts(self):
1180
1418
        """Return the next found ancestors, with ghosts split out.
1181
 
        
 
1419
 
1182
1420
        Ancestors are returned in the order they are seen in a breadth-first
1183
1421
        traversal.  No ancestor will be returned more than once. Ancestors are
1184
1422
        returned only after asking for their parents, which allows us to detect
1224
1462
        parents_of_found = set()
1225
1463
        # revisions may contain nodes that point to other nodes in revisions:
1226
1464
        # we want to filter them out.
1227
 
        self.seen.update(revisions)
 
1465
        seen = self.seen
 
1466
        seen.update(revisions)
1228
1467
        parent_map = self._parents_provider.get_parent_map(revisions)
1229
1468
        found_revisions.update(parent_map)
1230
1469
        for rev_id, parents in parent_map.iteritems():
1231
1470
            if parents is None:
1232
1471
                continue
1233
 
            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]
1234
1473
            if new_found_parents:
1235
1474
                # Calling set.update() with an empty generator is actually
1236
1475
                # rather expensive.
1243
1482
 
1244
1483
    def find_seen_ancestors(self, revisions):
1245
1484
        """Find ancestors of these revisions that have already been seen.
1246
 
        
 
1485
 
1247
1486
        This function generally makes the assumption that querying for the
1248
1487
        parents of a node that has already been queried is reasonably cheap.
1249
1488
        (eg, not a round trip to a remote host).
1284
1523
        Remove any of the specified revisions from the search list.
1285
1524
 
1286
1525
        None of the specified revisions are required to be present in the
1287
 
        search list.  In this case, the call is a no-op.
 
1526
        search list.
 
1527
 
 
1528
        It is okay to call stop_searching_any() for revisions which were seen
 
1529
        in previous iterations. It is the callers responsibility to call
 
1530
        find_seen_ancestors() to make sure that current search tips that are
 
1531
        ancestors of those revisions are also stopped.  All explicitly stopped
 
1532
        revisions will be excluded from the search result's get_keys(), though.
1288
1533
        """
1289
1534
        # TODO: does this help performance?
1290
1535
        # if not revisions:
1299
1544
                self._current_ghosts.intersection(revisions))
1300
1545
            self._current_present.difference_update(stopped)
1301
1546
            self._current_ghosts.difference_update(stopped)
1302
 
            # stopping 'x' should stop returning parents of 'x', but 
 
1547
            # stopping 'x' should stop returning parents of 'x', but
1303
1548
            # not if 'y' always references those same parents
1304
1549
            stop_rev_references = {}
1305
1550
            for rev in stopped_present:
1321
1566
                    stop_parents.add(rev_id)
1322
1567
            self._next_query.difference_update(stop_parents)
1323
1568
        self._stopped_keys.update(stopped)
 
1569
        self._stopped_keys.update(revisions)
1324
1570
        return stopped
1325
1571
 
1326
1572
    def start_searching(self, revisions):
1348
1594
            return revs, ghosts
1349
1595
 
1350
1596
 
1351
 
class SearchResult(object):
1352
 
    """The result of a breadth first search.
1353
 
 
1354
 
    A SearchResult provides the ability to reconstruct the search or access a
1355
 
    set of the keys the search found.
 
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
 
1610
 
 
1611
 
 
1612
def collapse_linear_regions(parent_map):
 
1613
    """Collapse regions of the graph that are 'linear'.
 
1614
 
 
1615
    For example::
 
1616
 
 
1617
      A:[B], B:[C]
 
1618
 
 
1619
    can be collapsed by removing B and getting::
 
1620
 
 
1621
      A:[C]
 
1622
 
 
1623
    :param parent_map: A dictionary mapping children to their parents
 
1624
    :return: Another dictionary with 'linear' chains collapsed
1356
1625
    """
1357
 
 
1358
 
    def __init__(self, start_keys, exclude_keys, key_count, keys):
1359
 
        """Create a SearchResult.
1360
 
 
1361
 
        :param start_keys: The keys the search started at.
1362
 
        :param exclude_keys: The keys the search excludes.
1363
 
        :param key_count: The total number of keys (from start to but not
1364
 
            including exclude).
1365
 
        :param keys: The keys the search found. Note that in future we may get
1366
 
            a SearchResult from a smart server, in which case the keys list is
1367
 
            not necessarily immediately available.
1368
 
        """
1369
 
        self._recipe = (start_keys, exclude_keys, key_count)
1370
 
        self._keys = frozenset(keys)
1371
 
 
1372
 
    def get_recipe(self):
1373
 
        """Return a recipe that can be used to replay this search.
1374
 
        
1375
 
        The recipe allows reconstruction of the same results at a later date
1376
 
        without knowing all the found keys. The essential elements are a list
1377
 
        of keys to start and and to stop at. In order to give reproducible
1378
 
        results when ghosts are encountered by a search they are automatically
1379
 
        added to the exclude list (or else ghost filling may alter the
1380
 
        results).
1381
 
 
1382
 
        :return: A tuple (start_keys_set, exclude_keys_set, revision_count). To
1383
 
            recreate the results of this search, create a breadth first
1384
 
            searcher on the same graph starting at start_keys. Then call next()
1385
 
            (or next_with_ghosts()) repeatedly, and on every result, call
1386
 
            stop_searching_any on any keys from the exclude_keys set. The
1387
 
            revision_count value acts as a trivial cross-check - the found
1388
 
            revisions of the new search should have as many elements as
1389
 
            revision_count. If it does not, then additional revisions have been
1390
 
            ghosted since the search was executed the first time and the second
1391
 
            time.
1392
 
        """
1393
 
        return self._recipe
1394
 
 
1395
 
    def get_keys(self):
1396
 
        """Return the keys found in this search.
1397
 
 
1398
 
        :return: A set of keys.
1399
 
        """
1400
 
        return self._keys
1401
 
 
 
1626
    # Note: this isn't a strictly minimal collapse. For example:
 
1627
    #   A
 
1628
    #  / \
 
1629
    # B   C
 
1630
    #  \ /
 
1631
    #   D
 
1632
    #   |
 
1633
    #   E
 
1634
    # Will not have 'D' removed, even though 'E' could fit. Also:
 
1635
    #   A
 
1636
    #   |    A
 
1637
    #   B => |
 
1638
    #   |    C
 
1639
    #   C
 
1640
    # A and C are both kept because they are edges of the graph. We *could* get
 
1641
    # rid of A if we wanted.
 
1642
    #   A
 
1643
    #  / \
 
1644
    # B   C
 
1645
    # |   |
 
1646
    # D   E
 
1647
    #  \ /
 
1648
    #   F
 
1649
    # Will not have any nodes removed, even though you do have an
 
1650
    # 'uninteresting' linear D->B and E->C
 
1651
    children = {}
 
1652
    for child, parents in parent_map.iteritems():
 
1653
        children.setdefault(child, [])
 
1654
        for p in parents:
 
1655
            children.setdefault(p, []).append(child)
 
1656
 
 
1657
    orig_children = dict(children)
 
1658
    removed = set()
 
1659
    result = dict(parent_map)
 
1660
    for node in parent_map:
 
1661
        parents = result[node]
 
1662
        if len(parents) == 1:
 
1663
            parent_children = children[parents[0]]
 
1664
            if len(parent_children) != 1:
 
1665
                # This is not the only child
 
1666
                continue
 
1667
            node_children = children[node]
 
1668
            if len(node_children) != 1:
 
1669
                continue
 
1670
            child_parents = result.get(node_children[0], None)
 
1671
            if len(child_parents) != 1:
 
1672
                # This is not its only parent
 
1673
                continue
 
1674
            # The child of this node only points at it, and the parent only has
 
1675
            # this as a child. remove this node, and join the others together
 
1676
            result[node_children[0]] = parents
 
1677
            children[parents[0]] = node_children
 
1678
            del result[node]
 
1679
            del children[node]
 
1680
            removed.add(node)
 
1681
 
 
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