/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.436.2 by Jelmer Vernooij
Add stubs for testsuite, rebase-continue and rebase-abort commands.
1
# Copyright (C) 2006-2007 by Jelmer Vernooij
0.436.177 by Jelmer Vernooij
Remove trailing whitespace.
2
#
0.436.2 by Jelmer Vernooij
Add stubs for testsuite, rebase-continue and rebase-abort commands.
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
0.436.131 by Jelmer Vernooij
Change license back to GPLv2+
5
# the Free Software Foundation; either version 2 of the License, or
0.436.2 by Jelmer Vernooij
Add stubs for testsuite, rebase-continue and rebase-abort commands.
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
0.436.178 by Jelmer Vernooij
Finish rename to 'bzr-rewrite'.
16
0.436.16 by Jelmer Vernooij
Some more work on maptree.
17
"""Rebase."""
0.436.3 by Jelmer Vernooij
Fill in commands.
18
0.436.157 by Jelmer Vernooij
use absolute imports everywhere.
19
import os
20
21
from bzrlib import (
22
    osutils,
23
    )
0.436.5 by Jelmer Vernooij
Import change_revision_parent from bzr-svn.
24
from bzrlib.config import Config
0.436.157 by Jelmer Vernooij
use absolute imports everywhere.
25
from bzrlib.errors import (
26
    BzrError,
27
    NoSuchFile,
28
    UnknownFormatError,
29
    NoCommonAncestor,
30
    NoSuchId,
31
    UnrelatedBranches,
32
    )
0.436.4 by Jelmer Vernooij
Add some tests.
33
from bzrlib.generate_ids import gen_revision_id
0.441.4 by Robert Collins
* Fixed O(history) access during plan creation (Robert Collins, lp:#249823).
34
from bzrlib.graph import FrozenHeadsCache
0.438.2 by Jelmer Vernooij
More work using proper merge bases.
35
from bzrlib.merge import Merger
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
36
from bzrlib.revision import NULL_REVISION
0.436.4 by Jelmer Vernooij
Add some tests.
37
from bzrlib.trace import mutter
0.441.4 by Robert Collins
* Fixed O(history) access during plan creation (Robert Collins, lp:#249823).
38
from bzrlib.tsort import topo_sort
0.436.5 by Jelmer Vernooij
Import change_revision_parent from bzr-svn.
39
import bzrlib.ui as ui
0.436.4 by Jelmer Vernooij
Add some tests.
40
0.436.178 by Jelmer Vernooij
Finish rename to 'bzr-rewrite'.
41
from bzrlib.plugins.rewrite.maptree import (
0.436.157 by Jelmer Vernooij
use absolute imports everywhere.
42
    MapTree,
43
    map_file_ids,
44
    )
0.436.17 by Jelmer Vernooij
Move maptree code to separate files.
45
0.436.3 by Jelmer Vernooij
Fill in commands.
46
REBASE_PLAN_FILENAME = 'rebase-plan'
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
47
REBASE_CURRENT_REVID_FILENAME = 'rebase-current'
0.436.4 by Jelmer Vernooij
Add some tests.
48
REBASE_PLAN_VERSION = 1
0.436.27 by Jelmer Vernooij
Note revision property 'rebase-of', add explanation of use of pregenerated revision ids.
49
REVPROP_REBASE_OF = 'rebase-of'
0.436.3 by Jelmer Vernooij
Fill in commands.
50
0.436.191 by Jelmer Vernooij
Move rebase state to a separate object.
51
class RebaseState(object):
52
0.436.192 by Jelmer Vernooij
Avoid reopening state when possible, allow subclasses.
53
    def has_plan(self):
54
        """Check whether there is a rebase plan present.
55
56
        :return: boolean
57
        """
58
        raise NotImplementedError(self.has_plan)
59
60
    def read_plan(self):
61
        """Read a rebase plan file.
62
63
        :return: Tuple with last revision info and replace map.
64
        """
65
        raise NotImplementedError(self.read_plan)
66
67
    def write_plan(self, replace_map):
68
        """Write a rebase plan file.
69
70
        :param replace_map: Replace map (old revid -> (new revid, new parents))
71
        """
72
        raise NotImplementedError(self.write_plan)
73
74
    def remove_plan(self):
75
        """Remove a rebase plan file.
76
        """
77
        raise NotImplementedError(self.remove_plan)
78
79
    def write_active_revid(self, revid):
80
        """Write the id of the revision that is currently being rebased.
81
82
        :param revid: Revision id to write
83
        """
84
        raise NotImplementedError(self.write_active_revid)
85
86
    def read_active_revid(self):
87
        """Read the id of the revision that is currently being rebased.
88
89
        :return: Id of the revision that is being rebased.
90
        """
91
        raise NotImplementedError(self.read_active_revid)
92
93
94
class RebaseState1(RebaseState):
95
0.436.191 by Jelmer Vernooij
Move rebase state to a separate object.
96
    def __init__(self, wt):
97
        self.wt = wt
98
        self.transport = wt._transport
99
100
    def has_plan(self):
0.436.192 by Jelmer Vernooij
Avoid reopening state when possible, allow subclasses.
101
        """See `RebaseState`."""
0.436.191 by Jelmer Vernooij
Move rebase state to a separate object.
102
        try:
103
            return self.transport.get_bytes(REBASE_PLAN_FILENAME) != ''
104
        except NoSuchFile:
105
            return False
106
107
    def read_plan(self):
0.436.192 by Jelmer Vernooij
Avoid reopening state when possible, allow subclasses.
108
        """See `RebaseState`."""
0.436.191 by Jelmer Vernooij
Move rebase state to a separate object.
109
        text = self.transport.get_bytes(REBASE_PLAN_FILENAME)
110
        if text == '':
111
            raise NoSuchFile(REBASE_PLAN_FILENAME)
112
        return unmarshall_rebase_plan(text)
113
114
    def write_plan(self, replace_map):
0.436.192 by Jelmer Vernooij
Avoid reopening state when possible, allow subclasses.
115
        """See `RebaseState`."""
0.436.191 by Jelmer Vernooij
Move rebase state to a separate object.
116
        content = marshall_rebase_plan(self.wt.branch.last_revision_info(),
117
            replace_map)
118
        assert type(content) == str
119
        self.transport.put_bytes(REBASE_PLAN_FILENAME, content)
120
121
    def remove_plan(self):
0.436.192 by Jelmer Vernooij
Avoid reopening state when possible, allow subclasses.
122
        """See `RebaseState`."""
0.436.191 by Jelmer Vernooij
Move rebase state to a separate object.
123
        self.transport.put_bytes(REBASE_PLAN_FILENAME, '')
124
125
    def write_active_revid(self, revid):
0.436.192 by Jelmer Vernooij
Avoid reopening state when possible, allow subclasses.
126
        """See `RebaseState`."""
0.436.191 by Jelmer Vernooij
Move rebase state to a separate object.
127
        if revid is None:
128
            revid = NULL_REVISION
129
        assert type(revid) == str
130
        self.transport.put_bytes(REBASE_CURRENT_REVID_FILENAME, revid)
131
132
    def read_active_revid(self):
0.436.192 by Jelmer Vernooij
Avoid reopening state when possible, allow subclasses.
133
        """See `RebaseState`."""
0.436.191 by Jelmer Vernooij
Move rebase state to a separate object.
134
        try:
135
            text = self.transport.get_bytes(REBASE_CURRENT_REVID_FILENAME).rstrip("\n")
136
            if text == NULL_REVISION:
137
                return None
138
            return text
139
        except NoSuchFile:
140
            return None
0.436.3 by Jelmer Vernooij
Fill in commands.
141
142
0.436.4 by Jelmer Vernooij
Add some tests.
143
def marshall_rebase_plan(last_rev_info, replace_map):
0.436.3 by Jelmer Vernooij
Fill in commands.
144
    """Marshall a rebase plan.
145
146
    :param last_rev_info: Last revision info tuple.
0.436.4 by Jelmer Vernooij
Add some tests.
147
    :param replace_map: Replace map (old revid -> (new revid, new parents))
0.436.3 by Jelmer Vernooij
Fill in commands.
148
    :return: string
149
    """
0.436.4 by Jelmer Vernooij
Add some tests.
150
    ret = "# Bazaar rebase plan %d\n" % REBASE_PLAN_VERSION
151
    ret += "%d %s\n" % last_rev_info
152
    for oldrev in replace_map:
153
        (newrev, newparents) = replace_map[oldrev]
154
        ret += "%s %s" % (oldrev, newrev) + \
155
            "".join([" %s" % p for p in newparents]) + "\n"
156
    return ret
157
158
159
def unmarshall_rebase_plan(text):
0.436.3 by Jelmer Vernooij
Fill in commands.
160
    """Unmarshall a rebase plan.
161
162
    :param text: Text to parse
0.436.4 by Jelmer Vernooij
Add some tests.
163
    :return: Tuple with last revision info, replace map.
0.436.3 by Jelmer Vernooij
Fill in commands.
164
    """
0.436.4 by Jelmer Vernooij
Add some tests.
165
    lines = text.split('\n')
166
    # Make sure header is there
167
    if lines[0] != "# Bazaar rebase plan %d" % REBASE_PLAN_VERSION:
168
        raise UnknownFormatError(lines[0])
169
170
    pts = lines[1].split(" ", 1)
171
    last_revision_info = (int(pts[0]), pts[1])
172
    replace_map = {}
173
    for l in lines[2:]:
174
        if l == "":
175
            # Skip empty lines
176
            continue
177
        pts = l.split(" ")
0.436.95 by Jelmer Vernooij
Use tuples more consistently.
178
        replace_map[pts[0]] = (pts[1], tuple(pts[2:]))
0.436.4 by Jelmer Vernooij
Add some tests.
179
    return (last_revision_info, replace_map)
180
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
181
0.436.39 by Jelmer Vernooij
Some more refactoring, add test that demonstrates #126743.
182
def regenerate_default_revid(repository, revid):
0.436.52 by Jelmer Vernooij
Add docstrings, update NEWS.
183
    """Generate a revision id for the rebase of an existing revision.
0.436.177 by Jelmer Vernooij
Remove trailing whitespace.
184
0.436.52 by Jelmer Vernooij
Add docstrings, update NEWS.
185
    :param repository: Repository in which the revision is present.
186
    :param revid: Revision id of the revision that is being rebased.
187
    :return: new revision id."""
0.436.180 by Jelmer Vernooij
remove unused import
188
    if revid == NULL_REVISION:
189
        return NULL_REVISION
0.436.39 by Jelmer Vernooij
Some more refactoring, add test that demonstrates #126743.
190
    rev = repository.get_revision(revid)
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
191
    return gen_revision_id(rev.committer, rev.timestamp)
192
193
0.441.4 by Robert Collins
* Fixed O(history) access during plan creation (Robert Collins, lp:#249823).
194
def generate_simple_plan(todo_set, start_revid, stop_revid, onto_revid, graph,
195
    generate_revid, skip_full_merged=False):
0.436.177 by Jelmer Vernooij
Remove trailing whitespace.
196
    """Create a simple rebase plan that replays history based
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
197
    on one revision being replayed on top of another.
0.436.3 by Jelmer Vernooij
Fill in commands.
198
0.443.2 by Max Bowsher
generate_simple_plan: Do not rebase only a leftmost path. Rebase all revisions topologically between the endpoints.
199
    :param todo_set: A set of revisions to rebase. Only the revisions
200
        topologically between stop_revid and start_revid (inclusive) are
201
        rebased; other revisions are ignored (and references to them are
202
        preserved).
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
203
    :param start_revid: Id of revision at which to start replaying
0.438.3 by Jelmer Vernooij
Add stop_revid argumen to generate_simple_plan.
204
    :param stop_revid: Id of revision until which to stop replaying
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
205
    :param onto_revid: Id of revision on top of which to replay
0.436.86 by Jelmer Vernooij
Fix some uses of get_revision_graph().
206
    :param graph: Graph object
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
207
    :param generate_revid: Function for generating new revision ids
0.436.177 by Jelmer Vernooij
Remove trailing whitespace.
208
    :param skip_full_merged: Skip revisions that merge already merged
0.436.55 by Jelmer Vernooij
Add functinality for skipping duplicate merges.
209
                             revisions.
0.436.3 by Jelmer Vernooij
Fill in commands.
210
0.436.4 by Jelmer Vernooij
Add some tests.
211
    :return: replace map
0.436.3 by Jelmer Vernooij
Fill in commands.
212
    """
0.441.4 by Robert Collins
* Fixed O(history) access during plan creation (Robert Collins, lp:#249823).
213
    assert start_revid is None or start_revid in todo_set, \
214
        "invalid start revid(%r), todo_set(%r)" % (start_revid, todo_set)
215
    assert stop_revid is None or stop_revid in todo_set, "invalid stop_revid"
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
216
    replace_map = {}
0.441.4 by Robert Collins
* Fixed O(history) access during plan creation (Robert Collins, lp:#249823).
217
    parent_map = graph.get_parent_map(todo_set)
218
    order = topo_sort(parent_map)
219
    if stop_revid is None:
220
        stop_revid = order[-1]
221
    if start_revid is None:
222
        # We need a common base.
223
        lca = graph.find_lca(stop_revid, onto_revid)
224
        if lca == set([NULL_REVISION]):
225
            raise UnrelatedBranches()
0.443.2 by Max Bowsher
generate_simple_plan: Do not rebase only a leftmost path. Rebase all revisions topologically between the endpoints.
226
        start_revid = order[0]
227
    todo = order[order.index(start_revid):order.index(stop_revid)+1]
0.441.4 by Robert Collins
* Fixed O(history) access during plan creation (Robert Collins, lp:#249823).
228
    heads_cache = FrozenHeadsCache(graph)
229
    # XXX: The output replacemap'd parents should get looked up in some manner
230
    # by the heads cache? RBC 20080719
231
    for oldrevid in todo:
0.436.86 by Jelmer Vernooij
Fix some uses of get_revision_graph().
232
        oldparents = parent_map[oldrevid]
233
        assert isinstance(oldparents, tuple), "not tuple: %r" % oldparents
0.443.3 by Max Bowsher
Overhaul the parent selection logic.
234
        parents = []
235
        # Left parent:
0.443.4 by Max Bowsher
Bugfix to parent computation.
236
        if heads_cache.heads((oldparents[0], onto_revid)) == set((onto_revid,)):
0.443.3 by Max Bowsher
Overhaul the parent selection logic.
237
            parents.append(onto_revid)
238
        elif oldparents[0] in replace_map:
239
            parents.append(replace_map[oldparents[0]][0])
240
        else:
241
            parents.append(onto_revid)
242
            parents.append(oldparents[0])
243
        # Other parents:
0.436.55 by Jelmer Vernooij
Add functinality for skipping duplicate merges.
244
        if len(oldparents) > 1:
0.441.4 by Robert Collins
* Fixed O(history) access during plan creation (Robert Collins, lp:#249823).
245
            additional_parents = heads_cache.heads(oldparents[1:])
0.443.3 by Max Bowsher
Overhaul the parent selection logic.
246
            for oldparent in oldparents[1:]:
247
                if oldparent in additional_parents:
0.443.4 by Max Bowsher
Bugfix to parent computation.
248
                    if heads_cache.heads((oldparent, onto_revid)) == set((onto_revid,)):
0.443.3 by Max Bowsher
Overhaul the parent selection logic.
249
                        pass
250
                    elif oldparent in replace_map:
251
                        newparent = replace_map[oldparent][0]
252
                        if parents[0] == onto_revid:
253
                            parents[0] = newparent
254
                        else:
255
                            parents.append(newparent)
256
                    else:
257
                        parents.append(oldparent)
0.436.55 by Jelmer Vernooij
Add functinality for skipping duplicate merges.
258
            if len(parents) == 1 and skip_full_merged:
259
                continue
0.443.3 by Max Bowsher
Overhaul the parent selection logic.
260
        parents = tuple(parents)
0.436.173 by Jelmer Vernooij
Add rebase-foreign command.
261
        newrevid = generate_revid(oldrevid, parents)
0.436.91 by Jelmer Vernooij
Fix blackbox tests, add comments to assertions.
262
        assert newrevid != oldrevid, "old and newrevid equal (%r)" % newrevid
263
        assert isinstance(parents, tuple), "parents not tuple: %r" % parents
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
264
        replace_map[oldrevid] = (newrevid, parents)
265
    return replace_map
266
267
0.436.88 by Jelmer Vernooij
Avoid use of get_revision_graph() calls.
268
def generate_transpose_plan(ancestry, renames, graph, generate_revid):
0.436.31 by Jelmer Vernooij
Refactor generate_transpose_plan() to not take a repository object but
269
    """Create a rebase plan that replaces a bunch of revisions
270
    in a revision graph.
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
271
0.436.88 by Jelmer Vernooij
Avoid use of get_revision_graph() calls.
272
    :param ancestry: Ancestry to consider
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
273
    :param renames: Renames of revision
0.436.88 by Jelmer Vernooij
Avoid use of get_revision_graph() calls.
274
    :param graph: Graph object
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
275
    :param generate_revid: Function for creating new revision ids
276
    """
277
    replace_map = {}
278
    todo = []
0.436.13 by Jelmer Vernooij
Add progress bar, some optimizations. Make merge type configurable.
279
    children = {}
0.436.88 by Jelmer Vernooij
Avoid use of get_revision_graph() calls.
280
    parent_map = {}
281
    for r, ps in ancestry:
0.436.118 by Jelmer Vernooij
Cope with ghosts in svn-upgrade.
282
        if not children.has_key(r):
283
            children[r] = []
284
        if ps is None: # Ghost
285
            continue
0.436.88 by Jelmer Vernooij
Avoid use of get_revision_graph() calls.
286
        parent_map[r] = ps
0.436.13 by Jelmer Vernooij
Add progress bar, some optimizations. Make merge type configurable.
287
        if not children.has_key(r):
288
            children[r] = []
0.436.88 by Jelmer Vernooij
Avoid use of get_revision_graph() calls.
289
        for p in ps:
0.436.13 by Jelmer Vernooij
Add progress bar, some optimizations. Make merge type configurable.
290
            if not children.has_key(p):
291
                children[p] = []
292
            children[p].append(r)
293
0.436.88 by Jelmer Vernooij
Avoid use of get_revision_graph() calls.
294
    parent_map.update(graph.get_parent_map(filter(lambda x: not x in parent_map, renames.values())))
295
0.436.177 by Jelmer Vernooij
Remove trailing whitespace.
296
    # todo contains a list of revisions that need to
0.436.31 by Jelmer Vernooij
Refactor generate_transpose_plan() to not take a repository object but
297
    # be rewritten
0.436.88 by Jelmer Vernooij
Avoid use of get_revision_graph() calls.
298
    for r, v in renames.items():
299
        replace_map[r] = (v, parent_map[v])
0.436.31 by Jelmer Vernooij
Refactor generate_transpose_plan() to not take a repository object but
300
        todo.append(r)
301
0.436.13 by Jelmer Vernooij
Add progress bar, some optimizations. Make merge type configurable.
302
    total = len(todo)
303
    processed = set()
304
    i = 0
305
    pb = ui.ui_factory.nested_progress_bar()
306
    try:
307
        while len(todo) > 0:
308
            r = todo.pop()
0.436.123 by Jelmer Vernooij
Cope with revision ids not changing.
309
            processed.add(r)
0.436.13 by Jelmer Vernooij
Add progress bar, some optimizations. Make merge type configurable.
310
            i += 1
311
            pb.update('determining dependencies', i, total)
312
            # Add entry for them in replace_map
313
            for c in children[r]:
314
                if c in renames:
315
                    continue
316
                if replace_map.has_key(c):
0.436.89 by Jelmer Vernooij
Use tuples for parents.
317
                    parents = replace_map[c][1]
0.436.13 by Jelmer Vernooij
Add progress bar, some optimizations. Make merge type configurable.
318
                else:
0.436.89 by Jelmer Vernooij
Use tuples for parents.
319
                    parents = parent_map[c]
320
                assert isinstance(parents, tuple), \
321
                        "Expected tuple of parents, got: %r" % parents
0.436.13 by Jelmer Vernooij
Add progress bar, some optimizations. Make merge type configurable.
322
                # replace r in parents with replace_map[r][0]
323
                if not replace_map[r][0] in parents:
0.436.90 by Jelmer Vernooij
Deal with parents being a tuple.
324
                    parents = list(parents)
325
                    parents[parents.index(r)] = replace_map[r][0]
326
                    parents = tuple(parents)
0.436.177 by Jelmer Vernooij
Remove trailing whitespace.
327
                replace_map[c] = (generate_revid(c, tuple(parents)),
0.436.173 by Jelmer Vernooij
Add rebase-foreign command.
328
                                  tuple(parents))
0.436.123 by Jelmer Vernooij
Cope with revision ids not changing.
329
                if replace_map[c][0] == c:
0.436.124 by Jelmer Vernooij
Remove erroneus [0].
330
                    del replace_map[c]
0.436.123 by Jelmer Vernooij
Cope with revision ids not changing.
331
                elif c not in processed:
332
                    todo.append(c)
0.436.13 by Jelmer Vernooij
Add progress bar, some optimizations. Make merge type configurable.
333
    finally:
334
        pb.finished()
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
335
0.436.31 by Jelmer Vernooij
Refactor generate_transpose_plan() to not take a repository object but
336
    # Remove items from the map that already exist
337
    for revid in renames:
338
        if replace_map.has_key(revid):
339
            del replace_map[revid]
340
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
341
    return replace_map
342
343
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
344
def rebase_todo(repository, replace_map):
345
    """Figure out what revisions still need to be rebased.
346
347
    :param repository: Repository that contains the revisions
348
    :param replace_map: Replace map
349
    """
0.436.91 by Jelmer Vernooij
Fix blackbox tests, add comments to assertions.
350
    for revid, parent_ids in replace_map.items():
351
        assert isinstance(parent_ids, tuple), "replace map parents not tuple"
352
        if not repository.has_revision(parent_ids[0]):
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
353
            yield revid
354
355
0.436.198 by Jelmer Vernooij
Refactor revision rewriters as objects.
356
def rebase(repository, replace_map, revision_rewriter):
0.436.5 by Jelmer Vernooij
Import change_revision_parent from bzr-svn.
357
    """Rebase a working tree according to the specified map.
358
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
359
    :param repository: Repository that contains the revisions
0.436.5 by Jelmer Vernooij
Import change_revision_parent from bzr-svn.
360
    :param replace_map: Dictionary with revisions to (optionally) rewrite
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
361
    :param merge_fn: Function for replaying a revision
0.436.5 by Jelmer Vernooij
Import change_revision_parent from bzr-svn.
362
    """
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
363
    # Figure out the dependencies
0.436.127 by Jelmer Vernooij
Use standard graph functions.
364
    graph = repository.get_graph()
365
    todo = list(graph.iter_topo_order(replace_map.keys()))
0.436.125 by Jelmer Vernooij
Use single call to find present revisions.
366
    pb = ui.ui_factory.nested_progress_bar()
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
367
    try:
0.436.127 by Jelmer Vernooij
Use standard graph functions.
368
        for i, revid in enumerate(todo):
369
            pb.update('rebase revisions', i, len(todo))
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
370
            (newrevid, newparents) = replace_map[revid]
0.436.91 by Jelmer Vernooij
Fix blackbox tests, add comments to assertions.
371
            assert isinstance(newparents, tuple), "Expected tuple for %r" % newparents
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
372
            if repository.has_revision(newrevid):
373
                # Was already converted, no need to worry about it again
374
                continue
0.436.198 by Jelmer Vernooij
Refactor revision rewriters as objects.
375
            revision_rewriter(revid, newrevid, newparents)
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
376
    finally:
377
        pb.finished()
0.436.177 by Jelmer Vernooij
Remove trailing whitespace.
378
0.436.32 by Jelmer Vernooij
Properly detect invalid snapshot replays.
379
0.436.204 by Jelmer Vernooij
Split up large method.
380
class ReplayParentsInconsistent(BzrError):
381
    """Raised when parents were inconsistent."""
382
    _fmt = """Parents were inconsistent while replaying commit for file id %(fileid)s, revision %(revid)s."""
383
384
    def __init__(self, fileid, revid):
385
        BzrError.__init__(self)
386
        self.fileid = fileid
387
        self.revid = revid
388
389
390
class CommitBuilderRevisionRewriter(object):
391
    """Revision rewriter that use commit builder.
392
393
    :ivar repository: Repository in which the revision is present.
394
    """
0.436.198 by Jelmer Vernooij
Refactor revision rewriters as objects.
395
0.436.209 by Jelmer Vernooij
Add map_ids option to CommitBuilderRevisionRewriter.
396
    def __init__(self, repository, map_ids=True):
0.436.198 by Jelmer Vernooij
Refactor revision rewriters as objects.
397
        self.repository = repository
0.436.209 by Jelmer Vernooij
Add map_ids option to CommitBuilderRevisionRewriter.
398
        self.map_ids = map_ids
0.436.198 by Jelmer Vernooij
Refactor revision rewriters as objects.
399
0.436.204 by Jelmer Vernooij
Split up large method.
400
    def _process_file(self, old_ie, oldtree, oldrevid, newrevid,
401
                      old_parent_invs, new_parent_invs, path):
402
        ie = old_ie.copy()
403
        # Either this file was modified last in this revision,
404
        # in which case it has to be rewritten
405
        if old_ie.revision == oldrevid:
406
            if self.repository.texts.has_key((ie.file_id, newrevid)):
407
                # Use the existing text
408
                ie.revision = newrevid
409
            else:
410
                # Create a new text
411
                ie.revision = None
412
        else:
413
            # or it was already there before the commit, in
414
            # which case the right revision should be used
415
            # one of the old parents had this revision, so find that
416
            # and then use the matching new parent
417
            old_file_id = oldtree.inventory.path2id(path)
418
            assert old_file_id is not None
419
            ie = None
420
            for (old_pinv, new_pinv) in zip(old_parent_invs, new_parent_invs):
421
                if (old_pinv.has_id(old_file_id) and
422
                    old_pinv[old_file_id].revision == old_ie.revision):
423
                    try:
424
                        ie = new_pinv[old_ie.file_id].copy()
425
                    except NoSuchId:
426
                        raise ReplayParentsInconsistent(old_ie.file_id, old_ie.revision)
427
                    break
428
            assert ie is not None
429
        return ie
430
431
    def _get_present_revisions(self, revids):
432
        return tuple([p for p in revids if self.repository.has_revision(p)])
433
0.436.198 by Jelmer Vernooij
Refactor revision rewriters as objects.
434
    def __call__(self, oldrevid, newrevid, new_parents):
435
        """Replay a commit by simply commiting the same snapshot with different
436
        parents.
437
438
        :param oldrevid: Revision id of the revision to copy.
439
        :param newrevid: Revision id of the revision to create.
440
        :param new_parents: Revision ids of the new parent revisions.
441
        """
0.436.204 by Jelmer Vernooij
Split up large method.
442
        assert isinstance(new_parents, tuple), "CommitBuilderRevisionRewriter: Expected tuple for %r" % new_parents
0.436.198 by Jelmer Vernooij
Refactor revision rewriters as objects.
443
        mutter('creating copy %r of %r with new parents %r' %
444
                                   (newrevid, oldrevid, new_parents))
445
        oldrev = self.repository.get_revision(oldrevid)
446
447
        revprops = dict(oldrev.properties)
448
        revprops[REVPROP_REBASE_OF] = oldrevid
449
450
        builder = self.repository.get_commit_builder(branch=None,
0.436.204 by Jelmer Vernooij
Split up large method.
451
            parents=new_parents, config=Config(), committer=oldrev.committer,
452
            timestamp=oldrev.timestamp, timezone=oldrev.timezone,
453
            revprops=revprops, revision_id=newrevid)
0.436.47 by Jelmer Vernooij
Add progress bar, fix compatibility with bzr.dev.
454
        try:
0.436.198 by Jelmer Vernooij
Refactor revision rewriters as objects.
455
            # Check what new_ie.file_id should be
456
            # use old and new parent inventories to generate new_id map
0.436.204 by Jelmer Vernooij
Split up large method.
457
            nonghost_oldparents = self._get_present_revisions(oldrev.parent_ids)
458
            nonghost_newparents = self._get_present_revisions(new_parents)
0.436.198 by Jelmer Vernooij
Refactor revision rewriters as objects.
459
            oldtree = self.repository.revision_tree(oldrevid)
0.436.209 by Jelmer Vernooij
Add map_ids option to CommitBuilderRevisionRewriter.
460
            if self.map_ids:
461
                fileid_map = map_file_ids(self.repository, nonghost_oldparents,
462
                    nonghost_newparents)
463
                mappedtree = MapTree(oldtree, fileid_map)
464
            else:
465
                mappedtree = oldtree
0.436.204 by Jelmer Vernooij
Split up large method.
466
            old_parent_invs = list(self.repository.iter_inventories(nonghost_oldparents))
467
            new_parent_invs = list(self.repository.iter_inventories(nonghost_newparents))
0.436.198 by Jelmer Vernooij
Refactor revision rewriters as objects.
468
            pb = ui.ui_factory.nested_progress_bar()
469
            try:
470
                for i, (path, old_ie) in enumerate(mappedtree.inventory.iter_entries()):
471
                    pb.update('upgrading file', i, len(mappedtree.inventory))
0.436.204 by Jelmer Vernooij
Split up large method.
472
                    ie = self._process_file(old_ie, oldtree, oldrevid, newrevid,
473
                        old_parent_invs, new_parent_invs, path)
474
                    builder.record_entry_contents(ie,
475
                            new_parent_invs, path, mappedtree,
0.436.198 by Jelmer Vernooij
Refactor revision rewriters as objects.
476
                            mappedtree.path_content_summary(path))
477
            finally:
478
                pb.finished()
479
            builder.finish_inventory()
480
            return builder.commit(oldrev.message)
481
        except:
482
            builder.abort()
483
            raise
0.436.5 by Jelmer Vernooij
Import change_revision_parent from bzr-svn.
484
485
0.436.203 by Jelmer Vernooij
Use camelcasing for class names.
486
class WorkingTreeRevisionRewriter(object):
0.436.228 by Jelmer Vernooij
use new style classes.
487
0.436.197 by Jelmer Vernooij
Use class.
488
    def __init__(self, wt, state, merge_type=None):
0.436.199 by Jelmer Vernooij
Eliminate unnecessary function call.
489
        """
490
        :param wt: Working tree in which to do the replays.
491
        """
0.436.197 by Jelmer Vernooij
Use class.
492
        self.wt = wt
0.436.201 by Jelmer Vernooij
Make determine_base a method.
493
        self.graph = self.wt.branch.repository.get_graph()
0.436.197 by Jelmer Vernooij
Use class.
494
        self.state = state
495
        self.merge_type = merge_type
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
496
0.436.198 by Jelmer Vernooij
Refactor revision rewriters as objects.
497
    def __call__(self, oldrevid, newrevid, newparents):
0.436.199 by Jelmer Vernooij
Eliminate unnecessary function call.
498
        """Replay a commit in a working tree, with a different base.
499
500
        :param oldrevid: Old revision id
501
        :param newrevid: New revision id
502
        :param newparents: New parent revision ids
0.436.197 by Jelmer Vernooij
Use class.
503
        """
0.436.199 by Jelmer Vernooij
Eliminate unnecessary function call.
504
        repository = self.wt.branch.repository
505
        if self.merge_type is None:
506
            from bzrlib.merge import Merge3Merger
507
            merge_type = Merge3Merger
0.444.1 by John Szakmeister
Don't traceback when the user specifies the --merge option to rebase.
508
        else:
509
            merge_type = self.merge_type
0.436.199 by Jelmer Vernooij
Eliminate unnecessary function call.
510
        oldrev = self.wt.branch.repository.get_revision(oldrevid)
511
        # Make sure there are no conflicts or pending merges/changes
512
        # in the working tree
513
        complete_revert(self.wt, [newparents[0]])
514
        assert not self.wt.changes_from(self.wt.basis_tree()).has_changed(), "Changes in rev"
515
516
        oldtree = repository.revision_tree(oldrevid)
517
        self.state.write_active_revid(oldrevid)
518
        merger = Merger(self.wt.branch, this_tree=self.wt)
519
        merger.set_other_revision(oldrevid, self.wt.branch)
0.436.201 by Jelmer Vernooij
Make determine_base a method.
520
        base_revid = self.determine_base(oldrevid, oldrev.parent_ids,
0.436.199 by Jelmer Vernooij
Eliminate unnecessary function call.
521
                                           newrevid, newparents)
522
        mutter('replaying %r as %r with base %r and new parents %r' %
523
               (oldrevid, newrevid, base_revid, newparents))
524
        merger.set_base_revision(base_revid, self.wt.branch)
525
        merger.merge_type = merge_type
526
        merger.do_merge()
527
        for newparent in newparents[1:]:
528
            self.wt.add_pending_merge(newparent)
0.436.200 by Jelmer Vernooij
Move in commit_rebase.
529
        self.commit_rebase(oldrev, newrevid)
0.436.199 by Jelmer Vernooij
Eliminate unnecessary function call.
530
        self.state.write_active_revid(None)
0.436.7 by Jelmer Vernooij
Add more test, make basic rebase work.
531
0.436.201 by Jelmer Vernooij
Make determine_base a method.
532
    def determine_base(self, oldrevid, oldparents, newrevid, newparents):
533
        """Determine the base for replaying a revision using merge.
534
535
        :param oldrevid: Revid of old revision.
536
        :param oldparents: List of old parents revids.
537
        :param newrevid: Revid of new revision.
538
        :param newparents: List of new parents revids.
539
        :return: Revision id of the new new revision.
540
        """
541
        # If this was the first commit, no base is needed
542
        if len(oldparents) == 0:
543
            return NULL_REVISION
544
545
        # In the case of a "simple" revision with just one parent,
546
        # that parent should be the base
547
        if len(oldparents) == 1:
548
            return oldparents[0]
549
0.436.204 by Jelmer Vernooij
Split up large method.
550
        # In case the rhs parent(s) of the origin revision has already been
551
        # merged in the new branch, use diff between rhs parent and diff from
0.436.201 by Jelmer Vernooij
Make determine_base a method.
552
        # original revision
553
        if len(newparents) == 1:
554
            # FIXME: Find oldparents entry that matches newparents[0]
555
            # and return it
556
            return oldparents[1]
557
558
        try:
559
            return self.graph.find_unique_lca(*[oldparents[0],newparents[1]])
560
        except NoCommonAncestor:
561
            return oldparents[0]
562
0.436.200 by Jelmer Vernooij
Move in commit_rebase.
563
    def commit_rebase(self, oldrev, newrevid):
564
        """Commit a rebase.
565
566
        :param oldrev: Revision info of new revision to commit.
567
        :param newrevid: New revision id."""
568
        assert oldrev.revision_id != newrevid, "Invalid revid %r" % newrevid
569
        revprops = dict(oldrev.properties)
570
        revprops[REVPROP_REBASE_OF] = oldrev.revision_id
571
        committer = self.wt.branch.get_config().username()
572
        authors = oldrev.get_apparent_authors()
573
        if oldrev.committer == committer:
574
            # No need to explicitly record the authors if the original
575
            # committer is rebasing.
576
            if [oldrev.committer] == authors:
577
                authors = None
578
        else:
579
            if not oldrev.committer in authors:
580
                authors.append(oldrev.committer)
581
        if 'author' in revprops:
582
            del revprops['author']
583
        if 'authors' in revprops:
584
            del revprops['authors']
585
        self.wt.commit(message=oldrev.message, timestamp=oldrev.timestamp,
586
                  timezone=oldrev.timezone, revprops=revprops, rev_id=newrevid,
587
                  committer=committer, authors=authors)
588
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
589
0.436.10 by Jelmer Vernooij
Add more agressive version of revert.
590
def complete_revert(wt, newparents):
0.436.177 by Jelmer Vernooij
Remove trailing whitespace.
591
    """Simple helper that reverts to specified new parents and makes sure none
0.436.16 by Jelmer Vernooij
Some more work on maptree.
592
    of the extra files are left around.
593
594
    :param wt: Working tree to use for rebase
595
    :param newparents: New parents of the working tree
0.436.10 by Jelmer Vernooij
Add more agressive version of revert.
596
    """
597
    newtree = wt.branch.repository.revision_tree(newparents[0])
598
    delta = wt.changes_from(newtree)
599
    wt.branch.generate_revision_history(newparents[0])
0.436.138 by Jelmer Vernooij
Support replaying in empty branch.
600
    wt.set_parent_ids([r for r in newparents[:1] if r != NULL_REVISION])
0.436.10 by Jelmer Vernooij
Add more agressive version of revert.
601
    for (f, _, _) in delta.added:
602
        abs_path = wt.abspath(f)
603
        if osutils.lexists(abs_path):
0.436.38 by Jelmer Vernooij
Handle directories in revert.
604
            if osutils.isdir(abs_path):
605
                osutils.rmtree(abs_path)
606
            else:
607
                os.unlink(abs_path)
0.436.48 by Jelmer Vernooij
Add replay command.
608
    wt.revert(None, old_tree=newtree, backups=False)
0.436.91 by Jelmer Vernooij
Fix blackbox tests, add comments to assertions.
609
    assert not wt.changes_from(wt.basis_tree()).has_changed(), "Rev changed"
0.436.138 by Jelmer Vernooij
Support replaying in empty branch.
610
    wt.set_parent_ids([r for r in newparents if r != NULL_REVISION])
0.436.32 by Jelmer Vernooij
Properly detect invalid snapshot replays.
611
612
0.436.34 by Jelmer Vernooij
Some more tests.
613
class ReplaySnapshotError(BzrError):
0.436.74 by Jelmer Vernooij
Add pydoctor file, more docstrings.
614
    """Raised when replaying a snapshot failed."""
0.436.151 by Jelmer Vernooij
Rename message to msg because the first happens to have the same name as an attribute that is deprecated in python 2.6.
615
    _fmt = """Replaying the snapshot failed: %(msg)s."""
0.436.32 by Jelmer Vernooij
Properly detect invalid snapshot replays.
616
0.436.151 by Jelmer Vernooij
Rename message to msg because the first happens to have the same name as an attribute that is deprecated in python 2.6.
617
    def __init__(self, msg):
0.436.32 by Jelmer Vernooij
Properly detect invalid snapshot replays.
618
        BzrError.__init__(self)
0.436.151 by Jelmer Vernooij
Rename message to msg because the first happens to have the same name as an attribute that is deprecated in python 2.6.
619
        self.msg = msg