~bzr-pqm/bzr/bzr.dev

1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
1
# Copyright (C) 2005, 2006 Canonical
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1 by mbp at sourcefrog
import from baz patch-364
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1 by mbp at sourcefrog
import from baz patch-364
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1 by mbp at sourcefrog
import from baz patch-364
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
1185.16.40 by Martin Pool
todo
17
# TODO: Some kind of command-line display of revision properties: 
18
# perhaps show them in log -v and allow them as options to the commit command.
1 by mbp at sourcefrog
import from baz patch-364
19
1590.1.1 by Robert Collins
Improve common_ancestor performance.
20
21
import bzrlib.errors as errors
1594.2.15 by Robert Collins
Unfuck performance.
22
from bzrlib.graph import node_distances, select_farthest, all_descendants, Graph
1185.16.39 by Martin Pool
- constraints on revprops
23
from bzrlib.osutils import contains_whitespace
1534.9.1 by Aaron Bentley
Added progress bars to merge
24
from bzrlib.progress import DummyProgress
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
25
from bzrlib.symbol_versioning import (deprecated_function,
26
        zero_eight,
27
        )
8 by mbp at sourcefrog
store committer's timezone in revision and show
28
974.1.90 by Aaron Bentley
Switched NULL revision ID to 'null:' per robertc's suggestion
29
NULL_REVISION="null:"
974.1.89 by Aaron Bentley
Fixed merging with multiple roots, by using null as graph root.
30
1911.2.1 by John Arbash Meinel
Cache encode/decode operations, saves memory and time. Especially when committing a new kernel tree with 7.7M new lines to annotate
31
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
32
class Revision(object):
1 by mbp at sourcefrog
import from baz patch-364
33
    """Single revision on a branch.
34
35
    Revisions may know their revision_hash, but only once they've been
36
    written out.  This is not stored because you cannot write the hash
37
    into the file it describes.
38
697 by Martin Pool
- write out parent list for new revisions
39
    After bzr 0.0.5 revisions are allowed to have multiple parents.
696 by Martin Pool
- Refactor revision deserialization code
40
1313 by Martin Pool
- rename to Revision.parent_ids to avoid confusion with old usage
41
    parent_ids
42
        List of parent revision_ids
1185.16.39 by Martin Pool
- constraints on revprops
43
44
    properties
45
        Dictionary of revision properties.  These are attached to the
46
        revision as extra metadata.  The name must be a single 
47
        word; the value can be an arbitrary string.
1 by mbp at sourcefrog
import from baz patch-364
48
    """
696 by Martin Pool
- Refactor revision deserialization code
49
    
1185.16.35 by Martin Pool
- stub for revision properties
50
    def __init__(self, revision_id, properties=None, **args):
1092.2.25 by Robert Collins
support ghosts in commits
51
        self.revision_id = revision_id
1185.16.35 by Martin Pool
- stub for revision properties
52
        self.properties = properties or {}
1185.16.39 by Martin Pool
- constraints on revprops
53
        self._check_properties()
1313 by Martin Pool
- rename to Revision.parent_ids to avoid confusion with old usage
54
        self.parent_ids = []
1311 by Martin Pool
- remove RevisionReference; just hold parent ids directly
55
        self.parent_sha1s = []
1733.1.4 by Robert Collins
Cosmetic niceties for debugging, extra comments etc.
56
        """Not used anymore - legacy from for 4."""
1185.42.6 by Jelmer Vernooij
Don't clear Revision.parent_ids after it has been set from the arguments
57
        self.__dict__.update(args)
696 by Martin Pool
- Refactor revision deserialization code
58
1 by mbp at sourcefrog
import from baz patch-364
59
    def __repr__(self):
184 by mbp at sourcefrog
pychecker fixups
60
        return "<Revision id %s>" % self.revision_id
1 by mbp at sourcefrog
import from baz patch-364
61
1185 by Martin Pool
- add xml round-trip test for revisions
62
    def __eq__(self, other):
63
        if not isinstance(other, Revision):
64
            return False
1092.2.20 by Robert Collins
symlink and weaves, whaddya know
65
        # FIXME: rbc 20050930 parent_ids are not being compared
66
        return (
67
                self.inventory_sha1 == other.inventory_sha1
1185 by Martin Pool
- add xml round-trip test for revisions
68
                and self.revision_id == other.revision_id
69
                and self.timestamp == other.timestamp
70
                and self.message == other.message
71
                and self.timezone == other.timezone
1185.16.35 by Martin Pool
- stub for revision properties
72
                and self.committer == other.committer
73
                and self.properties == other.properties)
1185 by Martin Pool
- add xml round-trip test for revisions
74
75
    def __ne__(self, other):
76
        return not self.__eq__(other)
77
1185.16.39 by Martin Pool
- constraints on revprops
78
    def _check_properties(self):
1732.3.2 by Matthieu Moy
merge
79
        """Verify that all revision properties are OK."""
1185.16.39 by Martin Pool
- constraints on revprops
80
        for name, value in self.properties.iteritems():
81
            if not isinstance(name, basestring) or contains_whitespace(name):
82
                raise ValueError("invalid property name %r" % name)
83
            if not isinstance(value, basestring):
84
                raise ValueError("invalid property value %r for %r" % 
85
                                 (name, value))
86
1534.4.49 by Robert Collins
Provide a revision.get_history(repository) method for generating a synthetic revision history.
87
    def get_history(self, repository):
88
        """Return the canonical line-of-history for this revision.
89
90
        If ghosts are present this may differ in result from a ghost-free
91
        repository.
92
        """
93
        current_revision = self
94
        reversed_result = []
95
        while current_revision is not None:
96
            reversed_result.append(current_revision.revision_id)
97
            if not len (current_revision.parent_ids):
98
                reversed_result.append(None)
99
                current_revision = None
100
            else:
101
                next_revision_id = current_revision.parent_ids[0]
102
                current_revision = repository.get_revision(next_revision_id)
103
        reversed_result.reverse()
104
        return reversed_result
105
1740.2.5 by Aaron Bentley
Merge from bzr.dev
106
    def get_summary(self):
107
        """Get the first line of the log message for this revision.
108
        """
109
        return self.message.split('\n', 1)[0]
110
1268 by Martin Pool
- is_ancestor now works by looking at the Branch's stored ancestry
111
112
def is_ancestor(revision_id, candidate_id, branch):
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
113
    """Return true if candidate_id is an ancestor of revision_id.
1268 by Martin Pool
- is_ancestor now works by looking at the Branch's stored ancestry
114
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
115
    A false negative will be returned if any intermediate descendent of
116
    candidate_id is not present in any of the revision_sources.
810 by Martin Pool
- New validate_revision_id function
117
    
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
118
    revisions_source is an object supporting a get_revision operation that
119
    behaves like Branch's.
120
    """
1711.9.11 by John Arbash Meinel
change return foo in bar to return (foo in bar)
121
    return (candidate_id in branch.repository.get_ancestry(revision_id))
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
122
974.1.35 by aaron.bentley at utoronto
Added revision-based common-ancestor checking
123
124
def iter_ancestors(revision_id, revision_source, only_present=False):
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
125
    ancestors = (revision_id,)
974.1.35 by aaron.bentley at utoronto
Added revision-based common-ancestor checking
126
    distance = 0
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
127
    while len(ancestors) > 0:
128
        new_ancestors = []
129
        for ancestor in ancestors:
974.1.35 by aaron.bentley at utoronto
Added revision-based common-ancestor checking
130
            if not only_present:
131
                yield ancestor, distance
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
132
            try:
133
                revision = revision_source.get_revision(ancestor)
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
134
            except errors.NoSuchRevision, e:
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
135
                if e.revision == revision_id:
136
                    raise 
137
                else:
138
                    continue
974.1.35 by aaron.bentley at utoronto
Added revision-based common-ancestor checking
139
            if only_present:
140
                yield ancestor, distance
1313 by Martin Pool
- rename to Revision.parent_ids to avoid confusion with old usage
141
            new_ancestors.extend(revision.parent_ids)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
142
        ancestors = new_ancestors
974.1.35 by aaron.bentley at utoronto
Added revision-based common-ancestor checking
143
        distance += 1
144
145
146
def find_present_ancestors(revision_id, revision_source):
1133 by Martin Pool
doc
147
    """Return the ancestors of a revision present in a branch.
148
149
    It's possible that a branch won't have the complete ancestry of
150
    one of its revisions.  
151
152
    """
974.1.35 by aaron.bentley at utoronto
Added revision-based common-ancestor checking
153
    found_ancestors = {}
154
    anc_iter = enumerate(iter_ancestors(revision_id, revision_source,
155
                         only_present=True))
156
    for anc_order, (anc_id, anc_distance) in anc_iter:
1963.2.1 by Robey Pointer
remove usage of has_key()
157
        if anc_id not in found_ancestors:
974.1.35 by aaron.bentley at utoronto
Added revision-based common-ancestor checking
158
            found_ancestors[anc_id] = (anc_order, anc_distance)
159
    return found_ancestors
160
    
1153 by Martin Pool
- clean up some code in revision.py
161
162
def __get_closest(intersection):
163
    intersection.sort()
164
    matches = [] 
165
    for entry in intersection:
166
        if entry[0] == intersection[0][0]:
167
            matches.append(entry[2])
168
    return matches
169
974.1.35 by aaron.bentley at utoronto
Added revision-based common-ancestor checking
170
974.1.60 by aaron.bentley at utoronto
Initial import of common-ancestor detection
171
def revision_graph(revision, revision_source):
974.1.66 by Aaron Bentley
more cleanups, docs, sorting stuff
172
    """Produce a graph of the ancestry of the specified revision.
1590.1.1 by Robert Collins
Improve common_ancestor performance.
173
    
174
    :return: root, ancestors map, descendants map
974.1.66 by Aaron Bentley
more cleanups, docs, sorting stuff
175
    """
1587.1.4 by Robert Collins
Quick, urgent fix for common_ancestor performance.
176
    revision_source.lock_read()
177
    try:
178
        return _revision_graph(revision, revision_source)
179
    finally:
180
        revision_source.unlock()
181
1590.1.1 by Robert Collins
Improve common_ancestor performance.
182
1587.1.4 by Robert Collins
Quick, urgent fix for common_ancestor performance.
183
def _revision_graph(revision, revision_source):
184
    """See revision_graph."""
1590.1.1 by Robert Collins
Improve common_ancestor performance.
185
    from bzrlib.tsort import topo_sort
186
    graph = revision_source.get_revision_graph(revision)
187
    # mark all no-parent revisions as being NULL_REVISION parentage.
188
    for node, parents in graph.items():
189
        if len(parents) == 0:
190
            graph[node] = [NULL_REVISION]
191
    # add NULL_REVISION to the graph
192
    graph[NULL_REVISION] = []
193
194
    # pick a root. If there are multiple roots
195
    # this could pick a random one.
196
    topo_order = topo_sort(graph.items())
197
    root = topo_order[0]
198
974.1.60 by aaron.bentley at utoronto
Initial import of common-ancestor detection
199
    ancestors = {}
200
    descendants = {}
1590.1.1 by Robert Collins
Improve common_ancestor performance.
201
202
    # map the descendants of the graph.
203
    # and setup our set based return graph.
204
    for node in graph.keys():
205
        descendants[node] = {}
206
    for node, parents in graph.items():
207
        for parent in parents:
208
            descendants[parent][node] = 1
209
        ancestors[node] = set(parents)
210
974.1.63 by Aaron Bentley
Fixed graph-generation
211
    assert root not in descendants[root]
212
    assert root not in ancestors[root]
974.1.60 by aaron.bentley at utoronto
Initial import of common-ancestor detection
213
    return root, ancestors, descendants
214
1092.3.4 by Robert Collins
update symlink branch to integration
215
974.1.60 by aaron.bentley at utoronto
Initial import of common-ancestor detection
216
def combined_graph(revision_a, revision_b, revision_source):
974.1.66 by Aaron Bentley
more cleanups, docs, sorting stuff
217
    """Produce a combined ancestry graph.
218
    Return graph root, ancestors map, descendants map, set of common nodes"""
1590.1.1 by Robert Collins
Improve common_ancestor performance.
219
    root, ancestors, descendants = revision_graph(
220
        revision_a, revision_source)
221
    root_b, ancestors_b, descendants_b = revision_graph(
222
        revision_b, revision_source)
974.1.80 by Aaron Bentley
Improved merge error handling and testing
223
    if root != root_b:
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
224
        raise errors.NoCommonRoot(revision_a, revision_b)
974.1.60 by aaron.bentley at utoronto
Initial import of common-ancestor detection
225
    common = set()
226
    for node, node_anc in ancestors_b.iteritems():
227
        if node in ancestors:
228
            common.add(node)
229
        else:
230
            ancestors[node] = set()
231
        ancestors[node].update(node_anc)
232
    for node, node_dec in descendants_b.iteritems():
233
        if node not in descendants:
1185.8.1 by Aaron Bentley
Ensured combined_graph is order-insensitive
234
            descendants[node] = {}
974.1.63 by Aaron Bentley
Fixed graph-generation
235
        descendants[node].update(node_dec)
974.1.60 by aaron.bentley at utoronto
Initial import of common-ancestor detection
236
    return root, ancestors, descendants, common
237
1092.3.4 by Robert Collins
update symlink branch to integration
238
1534.9.1 by Aaron Bentley
Added progress bars to merge
239
def common_ancestor(revision_a, revision_b, revision_source, 
240
                    pb=DummyProgress()):
1587.1.11 by Robert Collins
Local commits appear to be working properly.
241
    if None in (revision_a, revision_b):
242
        return None
1836.3.1 by Robert Collins
(robertc) Teach repository.get_revision_graph, and revision.common_ancestor, about NULL_REVISION.
243
    if NULL_REVISION in (revision_a, revision_b):
244
        return NULL_REVISION
1594.2.15 by Robert Collins
Unfuck performance.
245
    # trivial optimisation
246
    if revision_a == revision_b:
247
        return revision_a
974.1.80 by Aaron Bentley
Improved merge error handling and testing
248
    try:
1534.9.1 by Aaron Bentley
Added progress bars to merge
249
        try:
250
            pb.update('Picking ancestor', 1, 3)
1594.2.15 by Robert Collins
Unfuck performance.
251
            graph = revision_source.get_revision_graph_with_ghosts(
252
                [revision_a, revision_b])
253
            # convert to a NULL_REVISION based graph.
254
            ancestors = graph.get_ancestors()
255
            descendants = graph.get_descendants()
256
            common = set(graph.get_ancestry(revision_a)).intersection(
257
                     set(graph.get_ancestry(revision_b)))
258
            descendants[NULL_REVISION] = {}
259
            ancestors[NULL_REVISION] = []
260
            for root in graph.roots:
261
                descendants[NULL_REVISION][root] = 1
262
                ancestors[root].append(NULL_REVISION)
1607.1.12 by Robert Collins
Fix common_ancestor to still calculate a common ancestor when ghosts are
263
            for ghost in graph.ghosts:
264
                # ghosts act as roots for the purpose of finding 
265
                # the longest paths from the root: any ghost *might*
266
                # be directly attached to the root, so we treat them
267
                # as being such.
268
                # ghost now descends from NULL
269
                descendants[NULL_REVISION][ghost] = 1
270
                # that is it has an ancestor of NULL
271
                ancestors[ghost] = [NULL_REVISION]
272
                # ghost is common if any of ghosts descendants are common:
273
                for ghost_descendant in descendants[ghost]:
274
                    if ghost_descendant in common:
275
                        common.add(ghost)
276
                
1594.2.15 by Robert Collins
Unfuck performance.
277
            root = NULL_REVISION
278
            common.add(NULL_REVISION)
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
279
        except errors.NoCommonRoot:
280
            raise errors.NoCommonAncestor(revision_a, revision_b)
1534.9.1 by Aaron Bentley
Added progress bars to merge
281
            
282
        pb.update('Picking ancestor', 2, 3)
283
        distances = node_distances (descendants, ancestors, root)
284
        pb.update('Picking ancestor', 3, 2)
285
        farthest = select_farthest(distances, common)
286
        if farthest is None or farthest == NULL_REVISION:
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
287
            raise errors.NoCommonAncestor(revision_a, revision_b)
1534.9.1 by Aaron Bentley
Added progress bars to merge
288
    finally:
289
        pb.clear()
974.1.87 by Aaron Bentley
Refactored and documented graph stuff
290
    return farthest
974.1.60 by aaron.bentley at utoronto
Initial import of common-ancestor detection
291
292
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
293
class MultipleRevisionSources(object):
1153 by Martin Pool
- clean up some code in revision.py
294
    """Proxy that looks in multiple branches for revisions."""
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
295
    def __init__(self, *args):
296
        object.__init__(self)
297
        assert len(args) != 0
298
        self._revision_sources = args
299
1590.1.1 by Robert Collins
Improve common_ancestor performance.
300
    def revision_parents(self, revision_id):
301
        for source in self._revision_sources:
302
            try:
303
                return source.revision_parents(revision_id)
304
            except (errors.WeaveRevisionNotPresent, errors.NoSuchRevision), e:
305
                pass
306
        raise e
307
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
308
    def get_revision(self, revision_id):
309
        for source in self._revision_sources:
310
            try:
311
                return source.get_revision(revision_id)
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
312
            except errors.NoSuchRevision, e:
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
313
                pass
314
        raise e
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
315
1590.1.1 by Robert Collins
Improve common_ancestor performance.
316
    def get_revision_graph(self, revision_id):
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
317
        # we could probe incrementally until the pending
318
        # ghosts list stop growing, but its cheaper for now
319
        # to just ask for the complete graph for each repository.
320
        graphs = []
321
        for source in self._revision_sources:
322
            ghost_graph = source.get_revision_graph_with_ghosts()
323
            graphs.append(ghost_graph)
324
        absent = 0
325
        for graph in graphs:
326
            if not revision_id in graph.get_ancestors():
327
                absent += 1
328
        if absent == len(graphs):
329
            raise errors.NoSuchRevision(self._revision_sources[0], revision_id)
330
331
        # combine the graphs
1590.1.1 by Robert Collins
Improve common_ancestor performance.
332
        result = {}
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
333
        pending = set([revision_id])
334
        def find_parents(node_id):
335
            """find the parents for node_id."""
336
            for graph in graphs:
337
                ancestors = graph.get_ancestors()
338
                try:
339
                    return ancestors[node_id]
340
                except KeyError:
341
                    pass
342
            raise errors.NoSuchRevision(self._revision_sources[0], node_id)
343
        while len(pending):
344
            # all the graphs should have identical parent lists
345
            node_id = pending.pop()
1590.1.1 by Robert Collins
Improve common_ancestor performance.
346
            try:
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
347
                result[node_id] = find_parents(node_id)
348
                for parent_node in result[node_id]:
349
                    if not parent_node in result:
350
                        pending.add(parent_node)
351
            except errors.NoSuchRevision:
352
                # ghost, ignore it.
1590.1.1 by Robert Collins
Improve common_ancestor performance.
353
                pass
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
354
        return result
1590.1.1 by Robert Collins
Improve common_ancestor performance.
355
1594.2.15 by Robert Collins
Unfuck performance.
356
    def get_revision_graph_with_ghosts(self, revision_ids):
357
        # query all the sources for their entire graphs 
358
        # and then build a combined graph for just
359
        # revision_ids.
360
        graphs = []
361
        for source in self._revision_sources:
362
            ghost_graph = source.get_revision_graph_with_ghosts()
363
            graphs.append(ghost_graph.get_ancestors())
364
        for revision_id in revision_ids:
365
            absent = 0
366
            for graph in graphs:
367
                    if not revision_id in graph:
368
                        absent += 1
369
            if absent == len(graphs):
370
                raise errors.NoSuchRevision(self._revision_sources[0],
371
                                            revision_id)
372
373
        # combine the graphs
374
        result = Graph()
375
        pending = set(revision_ids)
376
        done = set()
377
        def find_parents(node_id):
378
            """find the parents for node_id."""
379
            for graph in graphs:
380
                try:
381
                    return graph[node_id]
382
                except KeyError:
383
                    pass
384
            raise errors.NoSuchRevision(self._revision_sources[0], node_id)
385
        while len(pending):
386
            # all the graphs should have identical parent lists
387
            node_id = pending.pop()
388
            try:
389
                parents = find_parents(node_id)
390
                for parent_node in parents:
391
                    # queued or done? 
392
                    if (parent_node not in pending and
393
                        parent_node not in done):
394
                        # no, queue
395
                        pending.add(parent_node)
396
                result.add_node(node_id, parents)
397
                done.add(node_id)
398
            except errors.NoSuchRevision:
399
                # ghost
400
                result.add_ghost(node_id)
401
                continue
402
        return result
403
1587.1.4 by Robert Collins
Quick, urgent fix for common_ancestor performance.
404
    def lock_read(self):
405
        for source in self._revision_sources:
406
            source.lock_read()
407
408
    def unlock(self):
409
        for source in self._revision_sources:
410
            source.unlock()
411
1590.1.1 by Robert Collins
Improve common_ancestor performance.
412
1685.1.65 by Martin Pool
[broken] Use deprecated_function not deprecated_method
413
@deprecated_function(zero_eight)
414
def get_intervening_revisions(ancestor_id, rev_id, rev_source,
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
415
                              revision_history=None):
416
    """Find the longest line of descent from maybe_ancestor to revision.
417
    Revision history is followed where possible.
418
419
    If ancestor_id == rev_id, list will be empty.
420
    Otherwise, rev_id will be the last entry.  ancestor_id will never appear.
421
    If ancestor_id is not an ancestor, NotAncestor will be thrown
422
    """
974.1.73 by Aaron Bentley
Reimplemented get_intervening_revisions for better scalability
423
    root, ancestors, descendants = revision_graph(rev_id, rev_source)
424
    if len(descendants) == 0:
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
425
        raise errors.NoSuchRevision(rev_source, rev_id)
974.1.73 by Aaron Bentley
Reimplemented get_intervening_revisions for better scalability
426
    if ancestor_id not in descendants:
427
        rev_source.get_revision(ancestor_id)
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
428
        raise errors.NotAncestor(rev_id, ancestor_id)
974.1.73 by Aaron Bentley
Reimplemented get_intervening_revisions for better scalability
429
    root_descendants = all_descendants(descendants, ancestor_id)
430
    root_descendants.add(ancestor_id)
431
    if rev_id not in root_descendants:
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
432
        raise errors.NotAncestor(rev_id, ancestor_id)
974.1.73 by Aaron Bentley
Reimplemented get_intervening_revisions for better scalability
433
    distances = node_distances(descendants, ancestors, ancestor_id,
434
                               root_descendants=root_descendants)
435
436
    def best_ancestor(rev_id):
437
        best = None
438
        for anc_id in ancestors[rev_id]:
439
            try:
440
                distance = distances[anc_id]
441
            except KeyError:
442
                continue
443
            if revision_history is not None and anc_id in revision_history:
444
                return anc_id
445
            elif best is None or distance > best[1]:
446
                best = (anc_id, distance)
447
        return best[0]
448
449
    next = rev_id
450
    path = []
451
    while next != ancestor_id:
452
        path.append(next)
453
        next = best_ancestor(next)
454
    path.reverse()
455
    return path