/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2490.2.5 by Aaron Bentley
Use GraphWalker.unique_ancestor to determine merge base
1
# Copyright (C) 2007 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
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
16
2490.2.30 by Aaron Bentley
Add functionality for tsorting graphs
17
from bzrlib import (
18
    errors,
3052.1.3 by John Arbash Meinel
deprecate revision.is_ancestor, update the callers and the tests.
19
    revision,
3099.3.3 by John Arbash Meinel
Deprecate get_parents() in favor of get_parent_map()
20
    symbol_versioning,
2490.2.30 by Aaron Bentley
Add functionality for tsorting graphs
21
    tsort,
22
    )
2490.2.21 by Aaron Bentley
Rename graph to deprecated_graph
23
from bzrlib.deprecated_graph import (node_distances, select_farthest)
2490.2.1 by Aaron Bentley
Start work on GraphWalker
24
2490.2.25 by Aaron Bentley
Update from review
25
# DIAGRAM of terminology
26
#       A
27
#       /\
28
#      B  C
29
#      |  |\
30
#      D  E F
31
#      |\/| |
32
#      |/\|/
33
#      G  H
34
#
35
# In this diagram, relative to G and H:
36
# A, B, C, D, E are common ancestors.
37
# C, D and E are border ancestors, because each has a non-common descendant.
38
# D and E are least common ancestors because none of their descendants are
39
# common ancestors.
40
# C is not a least common ancestor because its descendant, E, is a common
41
# ancestor.
42
#
43
# The find_unique_lca algorithm will pick A in two steps:
44
# 1. find_lca('G', 'H') => ['D', 'E']
45
# 2. Since len(['D', 'E']) > 1, find_lca('D', 'E') => ['A']
46
47
2988.1.3 by Robert Collins
Add a new repositoy method _generate_text_key_index for use by reconcile/check.
48
class DictParentsProvider(object):
49
50
    def __init__(self, ancestry):
51
        self.ancestry = ancestry
52
53
    def __repr__(self):
54
        return 'DictParentsProvider(%r)' % self.ancestry
55
3099.3.3 by John Arbash Meinel
Deprecate get_parents() in favor of get_parent_map()
56
    @symbol_versioning.deprecated_method(symbol_versioning.one_one)
2988.1.3 by Robert Collins
Add a new repositoy method _generate_text_key_index for use by reconcile/check.
57
    def get_parents(self, revisions):
58
        return [self.ancestry.get(r, None) for r in revisions]
59
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
60
    def get_parent_map(self, keys):
61
        """See _StackedParentsProvider.get_parent_map"""
62
        ancestry = self.ancestry
63
        return dict((k, ancestry[k]) for k in keys if k in ancestry)
64
2490.2.5 by Aaron Bentley
Use GraphWalker.unique_ancestor to determine merge base
65
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
66
class _StackedParentsProvider(object):
67
68
    def __init__(self, parent_providers):
69
        self._parent_providers = parent_providers
70
2490.2.28 by Aaron Bentley
Fix handling of null revision
71
    def __repr__(self):
72
        return "_StackedParentsProvider(%r)" % self._parent_providers
73
3099.3.3 by John Arbash Meinel
Deprecate get_parents() in favor of get_parent_map()
74
    @symbol_versioning.deprecated_method(symbol_versioning.one_one)
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
75
    def get_parents(self, revision_ids):
2490.2.25 by Aaron Bentley
Update from review
76
        """Find revision ids of the parents of a list of revisions
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
77
78
        A list is returned of the same length as the input.  Each entry
79
        is a list of parent ids for the corresponding input revision.
80
81
        [NULL_REVISION] is used as the parent of the first user-committed
82
        revision.  Its parent list is empty.
83
84
        If the revision is not present (i.e. a ghost), None is used in place
85
        of the list of parents.
86
        """
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
87
        found = self.get_parent_map(revision_ids)
88
        return [found.get(r, None) for r in revision_ids]
89
90
    def get_parent_map(self, keys):
91
        """Get a mapping of keys => parents
92
93
        A dictionary is returned with an entry for each key present in this
94
        source. If this source doesn't have information about a key, it should
95
        not include an entry.
96
97
        [NULL_REVISION] is used as the parent of the first user-committed
98
        revision.  Its parent list is empty.
99
100
        :param keys: An iterable returning keys to check (eg revision_ids)
101
        :return: A dictionary mapping each key to its parents
102
        """
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
103
        found = {}
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
104
        remaining = set(keys)
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
105
        for parents_provider in self._parent_providers:
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
106
            new_found = parents_provider.get_parent_map(remaining)
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
107
            found.update(new_found)
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
108
            remaining.difference_update(new_found)
109
            if not remaining:
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
110
                break
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
111
        return found
112
113
114
class CachingParentsProvider(object):
115
    """A parents provider which will cache the revision => parents in a dict.
116
117
    This is useful for providers that have an expensive lookup.
118
    """
119
120
    def __init__(self, parent_provider):
121
        self._real_provider = parent_provider
122
        # Theoretically we could use an LRUCache here
123
        self._cache = {}
124
125
    def __repr__(self):
126
        return "%s(%r)" % (self.__class__.__name__, self._real_provider)
127
3099.3.3 by John Arbash Meinel
Deprecate get_parents() in favor of get_parent_map()
128
    @symbol_versioning.deprecated_method(symbol_versioning.one_one)
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
129
    def get_parents(self, revision_ids):
130
        """See _StackedParentsProvider.get_parents"""
131
        found = self.get_parent_map(revision_ids)
2490.2.25 by Aaron Bentley
Update from review
132
        return [found.get(r, None) for r in revision_ids]
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
133
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
134
    def get_parent_map(self, keys):
135
        """See _StackedParentsProvider.get_parent_map"""
136
        needed = set()
137
        # If the _real_provider doesn't have a key, we cache a value of None,
138
        # which we then later use to realize we cannot provide a value for that
139
        # key.
140
        parent_map = {}
141
        cache = self._cache
142
        for key in keys:
143
            if key in cache:
144
                value = cache[key]
145
                if value is not None:
146
                    parent_map[key] = value
147
            else:
148
                needed.add(key)
149
150
        if needed:
151
            new_parents = self._real_provider.get_parent_map(needed)
152
            cache.update(new_parents)
153
            parent_map.update(new_parents)
154
            needed.difference_update(new_parents)
155
            cache.update(dict.fromkeys(needed, None))
156
        return parent_map
157
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
158
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
159
class Graph(object):
2490.2.10 by Aaron Bentley
Clarify text, remove unused _get_ancestry method
160
    """Provide incremental access to revision graphs.
161
162
    This is the generic implementation; it is intended to be subclassed to
163
    specialize it for other repository types.
164
    """
2490.2.1 by Aaron Bentley
Start work on GraphWalker
165
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
166
    def __init__(self, parents_provider):
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
167
        """Construct a Graph that uses several graphs as its input
2490.2.10 by Aaron Bentley
Clarify text, remove unused _get_ancestry method
168
169
        This should not normally be invoked directly, because there may be
170
        specialized implementations for particular repository types.  See
2490.2.21 by Aaron Bentley
Rename graph to deprecated_graph
171
        Repository.get_graph()
2490.2.10 by Aaron Bentley
Clarify text, remove unused _get_ancestry method
172
2921.3.4 by Robert Collins
Review feedback.
173
        :param parents_provider: An object providing a get_parents call
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
174
            conforming to the behavior of StackedParentsProvider.get_parents
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
175
        """
176
        self.get_parents = parents_provider.get_parents
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
177
        self.get_parent_map = parents_provider.get_parent_map
2490.2.29 by Aaron Bentley
Make parents provider private
178
        self._parents_provider = parents_provider
2490.2.28 by Aaron Bentley
Fix handling of null revision
179
180
    def __repr__(self):
2490.2.29 by Aaron Bentley
Make parents provider private
181
        return 'Graph(%r)' % self._parents_provider
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
182
183
    def find_lca(self, *revisions):
184
        """Determine the lowest common ancestors of the provided revisions
185
186
        A lowest common ancestor is a common ancestor none of whose
187
        descendants are common ancestors.  In graphs, unlike trees, there may
188
        be multiple lowest common ancestors.
2490.2.12 by Aaron Bentley
Improve documentation
189
190
        This algorithm has two phases.  Phase 1 identifies border ancestors,
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
191
        and phase 2 filters border ancestors to determine lowest common
192
        ancestors.
2490.2.12 by Aaron Bentley
Improve documentation
193
194
        In phase 1, border ancestors are identified, using a breadth-first
195
        search starting at the bottom of the graph.  Searches are stopped
196
        whenever a node or one of its descendants is determined to be common
197
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
198
        In phase 2, the border ancestors are filtered to find the least
2490.2.12 by Aaron Bentley
Improve documentation
199
        common ancestors.  This is done by searching the ancestries of each
200
        border ancestor.
201
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
202
        Phase 2 is perfomed on the principle that a border ancestor that is
203
        not an ancestor of any other border ancestor is a least common
204
        ancestor.
2490.2.12 by Aaron Bentley
Improve documentation
205
206
        Searches are stopped when they find a node that is determined to be a
207
        common ancestor of all border ancestors, because this shows that it
208
        cannot be a descendant of any border ancestor.
209
210
        The scaling of this operation should be proportional to
211
        1. The number of uncommon ancestors
212
        2. The number of border ancestors
213
        3. The length of the shortest path between a border ancestor and an
214
           ancestor of all border ancestors.
2490.2.3 by Aaron Bentley
Implement new merge base picker
215
        """
2490.2.23 by Aaron Bentley
Adapt find_borders to produce a graph difference
216
        border_common, common, sides = self._find_border_ancestors(revisions)
2776.3.1 by Robert Collins
* Deprecated method ``find_previous_heads`` on
217
        # We may have common ancestors that can be reached from each other.
218
        # - ask for the heads of them to filter it down to only ones that
219
        # cannot be reached from each other - phase 2.
220
        return self.heads(border_common)
2490.2.9 by Aaron Bentley
Fix minimal common ancestor algorithm for non-minimal perhipheral ancestors
221
2490.2.23 by Aaron Bentley
Adapt find_borders to produce a graph difference
222
    def find_difference(self, left_revision, right_revision):
2490.2.25 by Aaron Bentley
Update from review
223
        """Determine the graph difference between two revisions"""
2490.2.23 by Aaron Bentley
Adapt find_borders to produce a graph difference
224
        border, common, (left, right) = self._find_border_ancestors(
225
            [left_revision, right_revision])
226
        return (left.difference(right).difference(common),
227
                right.difference(left).difference(common))
228
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
229
    def _make_breadth_first_searcher(self, revisions):
230
        return _BreadthFirstSearcher(revisions, self)
231
2490.2.10 by Aaron Bentley
Clarify text, remove unused _get_ancestry method
232
    def _find_border_ancestors(self, revisions):
2490.2.12 by Aaron Bentley
Improve documentation
233
        """Find common ancestors with at least one uncommon descendant.
234
235
        Border ancestors are identified using a breadth-first
236
        search starting at the bottom of the graph.  Searches are stopped
237
        whenever a node or one of its descendants is determined to be common.
238
239
        This will scale with the number of uncommon ancestors.
2490.2.25 by Aaron Bentley
Update from review
240
241
        As well as the border ancestors, a set of seen common ancestors and a
242
        list of sets of seen ancestors for each input revision is returned.
243
        This allows calculation of graph difference from the results of this
244
        operation.
2490.2.12 by Aaron Bentley
Improve documentation
245
        """
2490.2.28 by Aaron Bentley
Fix handling of null revision
246
        if None in revisions:
247
            raise errors.InvalidRevisionId(None, self)
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
248
        common_searcher = self._make_breadth_first_searcher([])
2490.2.19 by Aaron Bentley
Implement common-ancestor-based culling
249
        common_ancestors = set()
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
250
        searchers = [self._make_breadth_first_searcher([r])
251
                     for r in revisions]
252
        active_searchers = searchers[:]
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
253
        border_ancestors = set()
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
254
        def update_common(searcher, revisions):
255
            w_seen_ancestors = searcher.find_seen_ancestors(
2490.2.19 by Aaron Bentley
Implement common-ancestor-based culling
256
                revision)
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
257
            stopped = searcher.stop_searching_any(w_seen_ancestors)
2490.2.19 by Aaron Bentley
Implement common-ancestor-based culling
258
            common_ancestors.update(w_seen_ancestors)
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
259
            common_searcher.start_searching(stopped)
2490.2.19 by Aaron Bentley
Implement common-ancestor-based culling
260
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
261
        while True:
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
262
            if len(active_searchers) == 0:
2490.2.23 by Aaron Bentley
Adapt find_borders to produce a graph difference
263
                return border_ancestors, common_ancestors, [s.seen for s in
264
                                                            searchers]
2490.2.19 by Aaron Bentley
Implement common-ancestor-based culling
265
            try:
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
266
                new_common = common_searcher.next()
2490.2.19 by Aaron Bentley
Implement common-ancestor-based culling
267
                common_ancestors.update(new_common)
268
            except StopIteration:
269
                pass
270
            else:
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
271
                for searcher in active_searchers:
272
                    for revision in new_common.intersection(searcher.seen):
273
                        update_common(searcher, revision)
2490.2.19 by Aaron Bentley
Implement common-ancestor-based culling
274
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
275
            newly_seen = set()
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
276
            new_active_searchers = []
277
            for searcher in active_searchers:
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
278
                try:
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
279
                    newly_seen.update(searcher.next())
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
280
                except StopIteration:
281
                    pass
282
                else:
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
283
                    new_active_searchers.append(searcher)
284
            active_searchers = new_active_searchers
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
285
            for revision in newly_seen:
2490.2.19 by Aaron Bentley
Implement common-ancestor-based culling
286
                if revision in common_ancestors:
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
287
                    for searcher in searchers:
288
                        update_common(searcher, revision)
2490.2.19 by Aaron Bentley
Implement common-ancestor-based culling
289
                    continue
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
290
                for searcher in searchers:
291
                    if revision not in searcher.seen:
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
292
                        break
293
                else:
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
294
                    border_ancestors.add(revision)
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
295
                    for searcher in searchers:
296
                        update_common(searcher, revision)
2490.2.9 by Aaron Bentley
Fix minimal common ancestor algorithm for non-minimal perhipheral ancestors
297
2776.3.1 by Robert Collins
* Deprecated method ``find_previous_heads`` on
298
    def heads(self, keys):
299
        """Return the heads from amongst keys.
300
301
        This is done by searching the ancestries of each key.  Any key that is
302
        reachable from another key is not returned; all the others are.
303
304
        This operation scales with the relative depth between any two keys. If
305
        any two keys are completely disconnected all ancestry of both sides
306
        will be retrieved.
307
308
        :param keys: An iterable of keys.
2776.1.4 by Robert Collins
Trivial review feedback changes.
309
        :return: A set of the heads. Note that as a set there is no ordering
310
            information. Callers will need to filter their input to create
311
            order if they need it.
2490.2.12 by Aaron Bentley
Improve documentation
312
        """
2776.1.4 by Robert Collins
Trivial review feedback changes.
313
        candidate_heads = set(keys)
3052.5.5 by John Arbash Meinel
Special case Graph.heads() for NULL_REVISION rather than is_ancestor.
314
        if revision.NULL_REVISION in candidate_heads:
315
            # NULL_REVISION is only a head if it is the only entry
316
            candidate_heads.remove(revision.NULL_REVISION)
317
            if not candidate_heads:
318
                return set([revision.NULL_REVISION])
2850.2.1 by Robert Collins
(robertc) Special case the zero-or-no-heads case for Graph.heads(). (Robert Collins)
319
        if len(candidate_heads) < 2:
320
            return candidate_heads
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
321
        searchers = dict((c, self._make_breadth_first_searcher([c]))
2776.1.4 by Robert Collins
Trivial review feedback changes.
322
                          for c in candidate_heads)
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
323
        active_searchers = dict(searchers)
324
        # skip over the actual candidate for each searcher
325
        for searcher in active_searchers.itervalues():
1551.15.81 by Aaron Bentley
Remove testing code
326
            searcher.next()
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
327
        # The common walker finds nodes that are common to two or more of the
328
        # input keys, so that we don't access all history when a currently
329
        # uncommon search point actually meets up with something behind a
330
        # common search point. Common search points do not keep searches
331
        # active; they just allow us to make searches inactive without
332
        # accessing all history.
333
        common_walker = self._make_breadth_first_searcher([])
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
334
        while len(active_searchers) > 0:
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
335
            ancestors = set()
336
            # advance searches
337
            try:
338
                common_walker.next()
339
            except StopIteration:
2921.3.4 by Robert Collins
Review feedback.
340
                # No common points being searched at this time.
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
341
                pass
1551.15.78 by Aaron Bentley
Fix KeyError in filter_candidate_lca
342
            for candidate in active_searchers.keys():
343
                try:
344
                    searcher = active_searchers[candidate]
345
                except KeyError:
346
                    # rare case: we deleted candidate in a previous iteration
347
                    # through this for loop, because it was determined to be
348
                    # a descendant of another candidate.
349
                    continue
2490.2.9 by Aaron Bentley
Fix minimal common ancestor algorithm for non-minimal perhipheral ancestors
350
                try:
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
351
                    ancestors.update(searcher.next())
2490.2.9 by Aaron Bentley
Fix minimal common ancestor algorithm for non-minimal perhipheral ancestors
352
                except StopIteration:
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
353
                    del active_searchers[candidate]
2490.2.9 by Aaron Bentley
Fix minimal common ancestor algorithm for non-minimal perhipheral ancestors
354
                    continue
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
355
            # process found nodes
356
            new_common = set()
357
            for ancestor in ancestors:
358
                if ancestor in candidate_heads:
359
                    candidate_heads.remove(ancestor)
360
                    del searchers[ancestor]
361
                    if ancestor in active_searchers:
362
                        del active_searchers[ancestor]
363
                # it may meet up with a known common node
2921.3.4 by Robert Collins
Review feedback.
364
                if ancestor in common_walker.seen:
365
                    # some searcher has encountered our known common nodes:
366
                    # just stop it
367
                    ancestor_set = set([ancestor])
368
                    for searcher in searchers.itervalues():
369
                        searcher.stop_searching_any(ancestor_set)
370
                else:
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
371
                    # or it may have been just reached by all the searchers:
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
372
                    for searcher in searchers.itervalues():
373
                        if ancestor not in searcher.seen:
2490.2.9 by Aaron Bentley
Fix minimal common ancestor algorithm for non-minimal perhipheral ancestors
374
                            break
375
                    else:
2921.3.4 by Robert Collins
Review feedback.
376
                        # The final active searcher has just reached this node,
377
                        # making it be known as a descendant of all candidates,
378
                        # so we can stop searching it, and any seen ancestors
379
                        new_common.add(ancestor)
380
                        for searcher in searchers.itervalues():
381
                            seen_ancestors =\
382
                                searcher.find_seen_ancestors(ancestor)
383
                            searcher.stop_searching_any(seen_ancestors)
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
384
            common_walker.start_searching(new_common)
2776.1.4 by Robert Collins
Trivial review feedback changes.
385
        return candidate_heads
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
386
1551.19.10 by Aaron Bentley
Merge now warns when it encounters a criss-cross
387
    def find_unique_lca(self, left_revision, right_revision,
388
                        count_steps=False):
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
389
        """Find a unique LCA.
390
391
        Find lowest common ancestors.  If there is no unique  common
392
        ancestor, find the lowest common ancestors of those ancestors.
393
394
        Iteration stops when a unique lowest common ancestor is found.
395
        The graph origin is necessarily a unique lowest common ancestor.
2490.2.5 by Aaron Bentley
Use GraphWalker.unique_ancestor to determine merge base
396
397
        Note that None is not an acceptable substitute for NULL_REVISION.
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
398
        in the input for this method.
1551.19.12 by Aaron Bentley
Add documentation for the count_steps parameter of Graph.find_unique_lca
399
400
        :param count_steps: If True, the return value will be a tuple of
401
            (unique_lca, steps) where steps is the number of times that
402
            find_lca was run.  If False, only unique_lca is returned.
2490.2.3 by Aaron Bentley
Implement new merge base picker
403
        """
404
        revisions = [left_revision, right_revision]
1551.19.10 by Aaron Bentley
Merge now warns when it encounters a criss-cross
405
        steps = 0
2490.2.3 by Aaron Bentley
Implement new merge base picker
406
        while True:
1551.19.10 by Aaron Bentley
Merge now warns when it encounters a criss-cross
407
            steps += 1
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
408
            lca = self.find_lca(*revisions)
409
            if len(lca) == 1:
1551.19.10 by Aaron Bentley
Merge now warns when it encounters a criss-cross
410
                result = lca.pop()
411
                if count_steps:
412
                    return result, steps
413
                else:
414
                    return result
2520.4.104 by Aaron Bentley
Avoid infinite loop when there is no unique lca
415
            if len(lca) == 0:
416
                raise errors.NoCommonAncestor(left_revision, right_revision)
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
417
            revisions = lca
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
418
2490.2.31 by Aaron Bentley
Fix iter_topo_order to permit un-included parents
419
    def iter_topo_order(self, revisions):
2490.2.30 by Aaron Bentley
Add functionality for tsorting graphs
420
        """Iterate through the input revisions in topological order.
421
422
        This sorting only ensures that parents come before their children.
423
        An ancestor may sort after a descendant if the relationship is not
424
        visible in the supplied list of revisions.
425
        """
3099.3.3 by John Arbash Meinel
Deprecate get_parents() in favor of get_parent_map()
426
        sorter = tsort.TopoSorter(self.get_parent_map(revisions))
2490.2.34 by Aaron Bentley
Update NEWS and change implementation to return an iterator
427
        return sorter.iter_topo_order()
2490.2.30 by Aaron Bentley
Add functionality for tsorting graphs
428
2653.2.1 by Aaron Bentley
Implement Graph.is_ancestor
429
    def is_ancestor(self, candidate_ancestor, candidate_descendant):
2653.2.5 by Aaron Bentley
Update to clarify algorithm
430
        """Determine whether a revision is an ancestor of another.
431
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
432
        We answer this using heads() as heads() has the logic to perform the
3078.2.6 by Ian Clatworthy
fix efficiency of local commit detection as recommended by jameinel's review
433
        smallest number of parent lookups to determine the ancestral
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
434
        relationship between N revisions.
2653.2.5 by Aaron Bentley
Update to clarify algorithm
435
        """
2921.3.1 by Robert Collins
* Graph ``heads()`` queries have been bugfixed to no longer access all
436
        return set([candidate_descendant]) == self.heads(
437
            [candidate_ancestor, candidate_descendant])
2653.2.1 by Aaron Bentley
Implement Graph.is_ancestor
438
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
439
2911.4.1 by Robert Collins
Factor out the Graph.heads() cache from _RevisionTextVersionCache for reuse, and use it in commit.
440
class HeadsCache(object):
441
    """A cache of results for graph heads calls."""
442
443
    def __init__(self, graph):
444
        self.graph = graph
445
        self._heads = {}
446
447
    def heads(self, keys):
448
        """Return the heads of keys.
449
2911.4.3 by Robert Collins
Make the contract of HeadsCache.heads() more clear.
450
        This matches the API of Graph.heads(), specifically the return value is
451
        a set which can be mutated, and ordering of the input is not preserved
452
        in the output.
453
2911.4.1 by Robert Collins
Factor out the Graph.heads() cache from _RevisionTextVersionCache for reuse, and use it in commit.
454
        :see also: Graph.heads.
455
        :param keys: The keys to calculate heads for.
456
        :return: A set containing the heads, which may be mutated without
457
            affecting future lookups.
458
        """
2911.4.2 by Robert Collins
Make HeadsCache actually work.
459
        keys = frozenset(keys)
2911.4.1 by Robert Collins
Factor out the Graph.heads() cache from _RevisionTextVersionCache for reuse, and use it in commit.
460
        try:
461
            return set(self._heads[keys])
462
        except KeyError:
463
            heads = self.graph.heads(keys)
464
            self._heads[keys] = heads
465
            return set(heads)
466
467
2592.3.223 by Robert Collins
Merge graph history-access bugfix.
468
class HeadsCache(object):
469
    """A cache of results for graph heads calls."""
470
471
    def __init__(self, graph):
472
        self.graph = graph
473
        self._heads = {}
474
475
    def heads(self, keys):
476
        """Return the heads of keys.
477
478
        :see also: Graph.heads.
479
        :param keys: The keys to calculate heads for.
480
        :return: A set containing the heads, which may be mutated without
481
            affecting future lookups.
482
        """
483
        keys = frozenset(keys)
484
        try:
485
            return set(self._heads[keys])
486
        except KeyError:
487
            heads = self.graph.heads(keys)
488
            self._heads[keys] = heads
489
            return set(heads)
490
491
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
492
class _BreadthFirstSearcher(object):
2921.3.4 by Robert Collins
Review feedback.
493
    """Parallel search breadth-first the ancestry of revisions.
2490.2.10 by Aaron Bentley
Clarify text, remove unused _get_ancestry method
494
495
    This class implements the iterator protocol, but additionally
496
    1. provides a set of seen ancestors, and
497
    2. allows some ancestries to be unsearched, via stop_searching_any
498
    """
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
499
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
500
    def __init__(self, revisions, parents_provider):
2490.2.18 by Aaron Bentley
Change AncestryWalker to take a set of ancestors
501
        self._start = set(revisions)
2490.2.10 by Aaron Bentley
Clarify text, remove unused _get_ancestry method
502
        self._search_revisions = None
2490.2.18 by Aaron Bentley
Change AncestryWalker to take a set of ancestors
503
        self.seen = set(revisions)
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
504
        self._parents_provider = parents_provider
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
505
506
    def __repr__(self):
3099.3.1 by John Arbash Meinel
Implement get_parent_map for ParentProviders
507
        if self._search_revisions is not None:
508
            search = 'searching=%r' % (list(self._search_revisions),)
509
        else:
510
            search = 'starting=%r' % (list(self._start),)
511
        return ('_BreadthFirstSearcher(%s,'
512
                ' seen=%r)' % (search, list(self.seen)))
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
513
514
    def next(self):
2490.2.10 by Aaron Bentley
Clarify text, remove unused _get_ancestry method
515
        """Return the next ancestors of this revision.
516
2490.2.12 by Aaron Bentley
Improve documentation
517
        Ancestors are returned in the order they are seen in a breadth-first
518
        traversal.  No ancestor will be returned more than once.
2490.2.10 by Aaron Bentley
Clarify text, remove unused _get_ancestry method
519
        """
520
        if self._search_revisions is None:
521
            self._search_revisions = self._start
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
522
        else:
2490.2.10 by Aaron Bentley
Clarify text, remove unused _get_ancestry method
523
            new_search_revisions = set()
3099.3.3 by John Arbash Meinel
Deprecate get_parents() in favor of get_parent_map()
524
            parent_map = self._parents_provider.get_parent_map(
525
                            self._search_revisions)
526
            for parents in parent_map.itervalues():
2490.2.13 by Aaron Bentley
Update distinct -> lowest, refactor, add ParentsProvider concept
527
                new_search_revisions.update(p for p in parents if
528
                                            p not in self.seen)
2490.2.10 by Aaron Bentley
Clarify text, remove unused _get_ancestry method
529
            self._search_revisions = new_search_revisions
530
        if len(self._search_revisions) == 0:
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
531
            raise StopIteration()
2490.2.10 by Aaron Bentley
Clarify text, remove unused _get_ancestry method
532
        self.seen.update(self._search_revisions)
533
        return self._search_revisions
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
534
2490.2.8 by Aaron Bentley
fix iteration stuff
535
    def __iter__(self):
536
        return self
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
537
538
    def find_seen_ancestors(self, revision):
2490.2.25 by Aaron Bentley
Update from review
539
        """Find ancestors of this revision that have already been seen."""
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
540
        searcher = _BreadthFirstSearcher([revision], self._parents_provider)
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
541
        seen_ancestors = set()
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
542
        for ancestors in searcher:
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
543
            for ancestor in ancestors:
544
                if ancestor not in self.seen:
2490.2.22 by Aaron Bentley
Rename GraphWalker -> Graph, _AncestryWalker -> _BreadthFirstSearcher
545
                    searcher.stop_searching_any([ancestor])
2490.2.7 by Aaron Bentley
Start implementing mca that scales with number of uncommon ancestors
546
                else:
547
                    seen_ancestors.add(ancestor)
548
        return seen_ancestors
549
2490.2.10 by Aaron Bentley
Clarify text, remove unused _get_ancestry method
550
    def stop_searching_any(self, revisions):
551
        """
552
        Remove any of the specified revisions from the search list.
553
554
        None of the specified revisions are required to be present in the
2490.2.12 by Aaron Bentley
Improve documentation
555
        search list.  In this case, the call is a no-op.
2490.2.10 by Aaron Bentley
Clarify text, remove unused _get_ancestry method
556
        """
2490.2.25 by Aaron Bentley
Update from review
557
        stopped = self._search_revisions.intersection(revisions)
558
        self._search_revisions = self._search_revisions.difference(revisions)
559
        return stopped
2490.2.17 by Aaron Bentley
Add start_searching, tweak stop_searching_any
560
561
    def start_searching(self, revisions):
562
        if self._search_revisions is None:
2490.2.19 by Aaron Bentley
Implement common-ancestor-based culling
563
            self._start = set(revisions)
2490.2.17 by Aaron Bentley
Add start_searching, tweak stop_searching_any
564
        else:
2490.2.25 by Aaron Bentley
Update from review
565
            self._search_revisions.update(revisions.difference(self.seen))
2490.2.19 by Aaron Bentley
Implement common-ancestor-based culling
566
        self.seen.update(revisions)