/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
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
0.436.16 by Jelmer Vernooij
Some more work on maptree.
16
"""Rebase."""
0.436.3 by Jelmer Vernooij
Fill in commands.
17
0.436.5 by Jelmer Vernooij
Import change_revision_parent from bzr-svn.
18
from bzrlib.config import Config
0.436.52 by Jelmer Vernooij
Add docstrings, update NEWS.
19
from bzrlib.errors import (BzrError, NoSuchFile, UnknownFormatError, 
0.436.57 by Jelmer Vernooij
Allow overridding whether merges of already merged revisions should be replayed.
20
                           NoCommonAncestor, UnrelatedBranches)
0.436.4 by Jelmer Vernooij
Add some tests.
21
from bzrlib.generate_ids import gen_revision_id
0.438.2 by Jelmer Vernooij
More work using proper merge bases.
22
from bzrlib.merge import Merger
0.436.10 by Jelmer Vernooij
Add more agressive version of revert.
23
from bzrlib import osutils
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
24
from bzrlib.revision import NULL_REVISION
0.436.4 by Jelmer Vernooij
Add some tests.
25
from bzrlib.trace import mutter
0.436.5 by Jelmer Vernooij
Import change_revision_parent from bzr-svn.
26
import bzrlib.ui as ui
0.436.4 by Jelmer Vernooij
Add some tests.
27
0.436.17 by Jelmer Vernooij
Move maptree code to separate files.
28
from maptree import MapTree, map_file_ids
0.436.38 by Jelmer Vernooij
Handle directories in revert.
29
import os
0.436.17 by Jelmer Vernooij
Move maptree code to separate files.
30
0.436.3 by Jelmer Vernooij
Fill in commands.
31
REBASE_PLAN_FILENAME = 'rebase-plan'
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
32
REBASE_CURRENT_REVID_FILENAME = 'rebase-current'
0.436.4 by Jelmer Vernooij
Add some tests.
33
REBASE_PLAN_VERSION = 1
0.436.27 by Jelmer Vernooij
Note revision property 'rebase-of', add explanation of use of pregenerated revision ids.
34
REVPROP_REBASE_OF = 'rebase-of'
0.436.3 by Jelmer Vernooij
Fill in commands.
35
36
def rebase_plan_exists(wt):
37
    """Check whether there is a rebase plan present.
38
39
    :param wt: Working tree for which to check.
40
    :return: boolean
41
    """
0.436.7 by Jelmer Vernooij
Add more test, make basic rebase work.
42
    try:
43
        return wt._control_files.get(REBASE_PLAN_FILENAME).read() != ''
44
    except NoSuchFile:
45
        return False
0.436.3 by Jelmer Vernooij
Fill in commands.
46
47
48
def read_rebase_plan(wt):
49
    """Read a rebase plan file.
50
51
    :param wt: Working Tree for which to write the plan.
0.436.4 by Jelmer Vernooij
Add some tests.
52
    :return: Tuple with last revision info and replace map.
0.436.3 by Jelmer Vernooij
Fill in commands.
53
    """
54
    text = wt._control_files.get(REBASE_PLAN_FILENAME).read()
55
    if text == '':
0.436.7 by Jelmer Vernooij
Add more test, make basic rebase work.
56
        raise NoSuchFile(REBASE_PLAN_FILENAME)
0.436.4 by Jelmer Vernooij
Add some tests.
57
    return unmarshall_rebase_plan(text)
58
59
60
def write_rebase_plan(wt, replace_map):
0.436.3 by Jelmer Vernooij
Fill in commands.
61
    """Write a rebase plan file.
62
63
    :param wt: Working Tree for which to write the plan.
0.436.4 by Jelmer Vernooij
Add some tests.
64
    :param replace_map: Replace map (old revid -> (new revid, new parents))
0.436.3 by Jelmer Vernooij
Fill in commands.
65
    """
0.436.7 by Jelmer Vernooij
Add more test, make basic rebase work.
66
    wt._control_files.put_utf8(REBASE_PLAN_FILENAME, 
67
            marshall_rebase_plan(wt.branch.last_revision_info(), replace_map))
0.436.3 by Jelmer Vernooij
Fill in commands.
68
69
70
def remove_rebase_plan(wt):
71
    """Remove a rebase plan file.
72
73
    :param wt: Working Tree for which to remove the plan.
74
    """
0.436.7 by Jelmer Vernooij
Add more test, make basic rebase work.
75
    wt._control_files.put_utf8(REBASE_PLAN_FILENAME, '')
0.436.3 by Jelmer Vernooij
Fill in commands.
76
77
0.436.4 by Jelmer Vernooij
Add some tests.
78
def marshall_rebase_plan(last_rev_info, replace_map):
0.436.3 by Jelmer Vernooij
Fill in commands.
79
    """Marshall a rebase plan.
80
81
    :param last_rev_info: Last revision info tuple.
0.436.4 by Jelmer Vernooij
Add some tests.
82
    :param replace_map: Replace map (old revid -> (new revid, new parents))
0.436.3 by Jelmer Vernooij
Fill in commands.
83
    :return: string
84
    """
0.436.4 by Jelmer Vernooij
Add some tests.
85
    ret = "# Bazaar rebase plan %d\n" % REBASE_PLAN_VERSION
86
    ret += "%d %s\n" % last_rev_info
87
    for oldrev in replace_map:
88
        (newrev, newparents) = replace_map[oldrev]
89
        ret += "%s %s" % (oldrev, newrev) + \
90
            "".join([" %s" % p for p in newparents]) + "\n"
91
    return ret
92
93
94
def unmarshall_rebase_plan(text):
0.436.3 by Jelmer Vernooij
Fill in commands.
95
    """Unmarshall a rebase plan.
96
97
    :param text: Text to parse
0.436.4 by Jelmer Vernooij
Add some tests.
98
    :return: Tuple with last revision info, replace map.
0.436.3 by Jelmer Vernooij
Fill in commands.
99
    """
0.436.4 by Jelmer Vernooij
Add some tests.
100
    lines = text.split('\n')
101
    # Make sure header is there
102
    if lines[0] != "# Bazaar rebase plan %d" % REBASE_PLAN_VERSION:
103
        raise UnknownFormatError(lines[0])
104
105
    pts = lines[1].split(" ", 1)
106
    last_revision_info = (int(pts[0]), pts[1])
107
    replace_map = {}
108
    for l in lines[2:]:
109
        if l == "":
110
            # Skip empty lines
111
            continue
112
        pts = l.split(" ")
113
        replace_map[pts[0]] = (pts[1], pts[2:])
114
    return (last_revision_info, replace_map)
115
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
116
0.436.39 by Jelmer Vernooij
Some more refactoring, add test that demonstrates #126743.
117
def regenerate_default_revid(repository, revid):
0.436.52 by Jelmer Vernooij
Add docstrings, update NEWS.
118
    """Generate a revision id for the rebase of an existing revision.
119
    
120
    :param repository: Repository in which the revision is present.
121
    :param revid: Revision id of the revision that is being rebased.
122
    :return: new revision id."""
0.436.39 by Jelmer Vernooij
Some more refactoring, add test that demonstrates #126743.
123
    rev = repository.get_revision(revid)
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
124
    return gen_revision_id(rev.committer, rev.timestamp)
125
126
0.438.3 by Jelmer Vernooij
Add stop_revid argumen to generate_simple_plan.
127
def generate_simple_plan(history, start_revid, stop_revid, onto_revid, 
0.436.55 by Jelmer Vernooij
Add functinality for skipping duplicate merges.
128
                         onto_ancestry, get_parents, generate_revid,
129
                         skip_full_merged=False):
0.436.3 by Jelmer Vernooij
Fill in commands.
130
    """Create a simple rebase plan that replays history based 
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
131
    on one revision being replayed on top of another.
0.436.3 by Jelmer Vernooij
Fill in commands.
132
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
133
    :param history: Revision history
134
    :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.
135
    :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.
136
    :param onto_revid: Id of revision on top of which to replay
0.438.1 by Jelmer Vernooij
Split out function for determining rebase base.
137
    :param onto_ancestry: Ancestry of onto_revid
0.436.39 by Jelmer Vernooij
Some more refactoring, add test that demonstrates #126743.
138
    :param get_parents: Function for obtaining the parents of a revision
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
139
    :param generate_revid: Function for generating new revision ids
0.436.55 by Jelmer Vernooij
Add functinality for skipping duplicate merges.
140
    :param skip_full_merged: Skip revisions that merge already merged 
141
                             revisions.
0.436.3 by Jelmer Vernooij
Fill in commands.
142
0.436.4 by Jelmer Vernooij
Add some tests.
143
    :return: replace map
0.436.3 by Jelmer Vernooij
Fill in commands.
144
    """
0.436.43 by Jelmer Vernooij
Factor out common revid code - maybe some of this can use aaron's common ancestor code instead?
145
    assert start_revid is None or start_revid in history
146
    assert stop_revid is None or stop_revid in history
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
147
    replace_map = {}
0.438.3 by Jelmer Vernooij
Add stop_revid argumen to generate_simple_plan.
148
    if start_revid is not None:
149
        start_revno = history.index(start_revid)
150
    else:
151
        start_revno = None
152
    if stop_revid is not None:
153
        stop_revno = history.index(stop_revid)+1
154
    else:
155
        stop_revno = None
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
156
    new_parent = onto_revid
0.438.3 by Jelmer Vernooij
Add stop_revid argumen to generate_simple_plan.
157
    for oldrevid in history[start_revno:stop_revno]: 
0.436.55 by Jelmer Vernooij
Add functinality for skipping duplicate merges.
158
        oldparents = get_parents(oldrevid)
159
        assert oldparents == [] or \
160
                oldparents[0] == history[history.index(oldrevid)-1]
161
        if len(oldparents) > 1:
162
            parents = [new_parent] + filter(lambda p: p not in onto_ancestry or p == onto_revid, oldparents[1:]) 
163
            if len(parents) == 1 and skip_full_merged:
164
                continue
165
        else:
166
            parents = [new_parent]
0.436.39 by Jelmer Vernooij
Some more refactoring, add test that demonstrates #126743.
167
        newrevid = generate_revid(oldrevid)
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
168
        assert newrevid != oldrevid
169
        replace_map[oldrevid] = (newrevid, parents)
170
        new_parent = newrevid
171
    return replace_map
172
173
0.436.31 by Jelmer Vernooij
Refactor generate_transpose_plan() to not take a repository object but
174
def generate_transpose_plan(graph, renames, get_parents, generate_revid):
175
    """Create a rebase plan that replaces a bunch of revisions
176
    in a revision graph.
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
177
178
    :param graph: Revision graph in which to operate
179
    :param renames: Renames of revision
0.436.31 by Jelmer Vernooij
Refactor generate_transpose_plan() to not take a repository object but
180
    :param get_parents: Function for determining parents
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
181
    :param generate_revid: Function for creating new revision ids
182
    """
183
    replace_map = {}
184
    todo = []
0.436.13 by Jelmer Vernooij
Add progress bar, some optimizations. Make merge type configurable.
185
    children = {}
186
    for r in graph:
187
        if not children.has_key(r):
188
            children[r] = []
189
        for p in graph[r]:
190
            if not children.has_key(p):
191
                children[p] = []
192
            children[p].append(r)
193
0.436.31 by Jelmer Vernooij
Refactor generate_transpose_plan() to not take a repository object but
194
    # todo contains a list of revisions that need to 
195
    # be rewritten
196
    for r in renames:
197
        replace_map[r] = (renames[r], get_parents(renames[r]))
198
        todo.append(r)
199
0.436.13 by Jelmer Vernooij
Add progress bar, some optimizations. Make merge type configurable.
200
    total = len(todo)
201
    processed = set()
202
    i = 0
203
    pb = ui.ui_factory.nested_progress_bar()
204
    try:
205
        while len(todo) > 0:
206
            r = todo.pop()
207
            i += 1
208
            pb.update('determining dependencies', i, total)
209
            # Add entry for them in replace_map
210
            for c in children[r]:
211
                if c in renames:
212
                    continue
213
                if replace_map.has_key(c):
214
                    parents = replace_map[c][1]
215
                else:
0.436.36 by Jelmer Vernooij
Fix compatibility with bzr 0.19.
216
                    parents = list(graph[c])
217
                assert isinstance(parents, list), \
218
                        "Expected list of parents, got: %r" % parents
0.436.13 by Jelmer Vernooij
Add progress bar, some optimizations. Make merge type configurable.
219
                # replace r in parents with replace_map[r][0]
220
                if not replace_map[r][0] in parents:
221
                    parents[parents.index(r)] = replace_map[r][0]
0.436.31 by Jelmer Vernooij
Refactor generate_transpose_plan() to not take a repository object but
222
                replace_map[c] = (generate_revid(c), parents)
223
                assert replace_map[c][0] != c
0.436.13 by Jelmer Vernooij
Add progress bar, some optimizations. Make merge type configurable.
224
            processed.add(r)
225
            # Add them to todo[]
0.436.15 by Jelmer Vernooij
Fix inverse bug - needs tests.
226
            todo.extend(filter(lambda x: not x in processed, children[r]))
0.436.13 by Jelmer Vernooij
Add progress bar, some optimizations. Make merge type configurable.
227
    finally:
228
        pb.finished()
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
229
0.436.31 by Jelmer Vernooij
Refactor generate_transpose_plan() to not take a repository object but
230
    # Remove items from the map that already exist
231
    for revid in renames:
232
        if replace_map.has_key(revid):
233
            del replace_map[revid]
234
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
235
    return replace_map
236
237
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
238
def rebase_todo(repository, replace_map):
239
    """Figure out what revisions still need to be rebased.
240
241
    :param repository: Repository that contains the revisions
242
    :param replace_map: Replace map
243
    """
244
    for revid in replace_map:
245
        if not repository.has_revision(replace_map[revid][0]):
246
            yield revid
247
248
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
249
def rebase(repository, replace_map, replay_fn):
0.436.5 by Jelmer Vernooij
Import change_revision_parent from bzr-svn.
250
    """Rebase a working tree according to the specified map.
251
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
252
    :param repository: Repository that contains the revisions
0.436.5 by Jelmer Vernooij
Import change_revision_parent from bzr-svn.
253
    :param replace_map: Dictionary with revisions to (optionally) rewrite
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
254
    :param merge_fn: Function for replaying a revision
0.436.5 by Jelmer Vernooij
Import change_revision_parent from bzr-svn.
255
    """
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
256
    todo = list(rebase_todo(repository, replace_map))
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
257
    dependencies = {}
258
259
    # Figure out the dependencies
260
    for revid in todo:
261
        for p in replace_map[revid][1]:
262
            if repository.has_revision(p):
263
                continue
264
            if not dependencies.has_key(p):
265
                dependencies[p] = []
266
            dependencies[p].append(revid)
267
268
    pb = ui.ui_factory.nested_progress_bar()
0.436.22 by Jelmer Vernooij
Try to improve the progress bar a bit.
269
    total = len(todo)
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
270
    i = 0
271
    try:
272
        while len(todo) > 0:
0.436.22 by Jelmer Vernooij
Try to improve the progress bar a bit.
273
            pb.update('rebase revisions', i, total)
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
274
            i += 1
275
            revid = todo.pop()
276
            (newrevid, newparents) = replace_map[revid]
0.436.19 by Jelmer Vernooij
- Add blackbox tests
277
            if filter(repository.has_revision, newparents) != newparents:
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
278
                # Not all parents present yet, avoid for now
279
                continue
280
            if repository.has_revision(newrevid):
281
                # Was already converted, no need to worry about it again
282
                continue
283
            replay_fn(repository, revid, newrevid, newparents)
284
            assert repository.has_revision(newrevid)
0.436.32 by Jelmer Vernooij
Properly detect invalid snapshot replays.
285
            assert repository.revision_parents(newrevid) == newparents, \
286
                   "expected parents %r, got %r" % (newparents, 
287
                           repository.revision_parents(newrevid))
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
288
            if dependencies.has_key(newrevid):
289
                todo.extend(dependencies[newrevid])
290
                del dependencies[newrevid]
291
    finally:
292
        pb.finished()
293
        
0.436.19 by Jelmer Vernooij
- Add blackbox tests
294
    #assert all(map(repository.has_revision, 
295
    #           [replace_map[r][0] for r in replace_map]))
0.436.16 by Jelmer Vernooij
Some more work on maptree.
296
0.436.32 by Jelmer Vernooij
Properly detect invalid snapshot replays.
297
298
0.436.34 by Jelmer Vernooij
Some more tests.
299
def replay_snapshot(repository, oldrevid, newrevid, new_parents, 
0.436.47 by Jelmer Vernooij
Add progress bar, fix compatibility with bzr.dev.
300
                    revid_renames, fix_revid=None):
0.436.32 by Jelmer Vernooij
Properly detect invalid snapshot replays.
301
    """Replay a commit by simply commiting the same snapshot with different 
302
    parents.
0.436.5 by Jelmer Vernooij
Import change_revision_parent from bzr-svn.
303
304
    :param repository: Repository in which the revision is present.
305
    :param oldrevid: Revision id of the revision to copy.
306
    :param newrevid: Revision id of the revision to create.
307
    :param new_parents: Revision ids of the new parent revisions.
0.436.34 by Jelmer Vernooij
Some more tests.
308
    :param revid_renames: Revision id renames for texts.
0.436.5 by Jelmer Vernooij
Import change_revision_parent from bzr-svn.
309
    """
310
    assert isinstance(new_parents, list)
0.436.16 by Jelmer Vernooij
Some more work on maptree.
311
    mutter('creating copy %r of %r with new parents %r' % 
312
                               (newrevid, oldrevid, new_parents))
0.436.5 by Jelmer Vernooij
Import change_revision_parent from bzr-svn.
313
    oldrev = repository.get_revision(oldrevid)
314
0.436.7 by Jelmer Vernooij
Add more test, make basic rebase work.
315
    revprops = dict(oldrev.properties)
0.436.27 by Jelmer Vernooij
Note revision property 'rebase-of', add explanation of use of pregenerated revision ids.
316
    revprops[REVPROP_REBASE_OF] = oldrevid
0.436.7 by Jelmer Vernooij
Add more test, make basic rebase work.
317
0.436.49 by Jelmer Vernooij
Fix locking.
318
    builder = repository.get_commit_builder(branch=None, 
319
                                            parents=new_parents, 
320
                                            config=Config(),
321
                                            committer=oldrev.committer,
322
                                            timestamp=oldrev.timestamp,
323
                                            timezone=oldrev.timezone,
324
                                            revprops=revprops,
325
                                            revision_id=newrevid)
0.436.5 by Jelmer Vernooij
Import change_revision_parent from bzr-svn.
326
    try:
0.436.47 by Jelmer Vernooij
Add progress bar, fix compatibility with bzr.dev.
327
328
        # Check what new_ie.file_id should be
329
        # use old and new parent inventories to generate new_id map
330
        fileid_map = map_file_ids(repository, oldrev.parent_ids, new_parents)
331
        oldtree = MapTree(repository.revision_tree(oldrevid), fileid_map)
332
        total = len(oldtree.inventory)
333
        pb = ui.ui_factory.nested_progress_bar()
334
        i = 0
335
        try:
336
            parent_invs = map(repository.get_revision_inventory, new_parents)
337
            transact = repository.get_transaction()
338
            for path, ie in oldtree.inventory.iter_entries():
339
                pb.update('upgrading file', i, total)
340
                ie = ie.copy()
341
                # Either this file was modified last in this revision, 
342
                # in which case it has to be rewritten
343
                if fix_revid is not None:
344
                    ie.revision = fix_revid(ie.revision)
345
                if ie.revision == oldrevid:
0.436.54 by Jelmer Vernooij
Move determination of merge base to separate function.
346
                    if repository.weave_store.get_weave_or_empty(ie.file_id, 
347
                            repository.get_transaction()).has_version(newrevid):
0.436.47 by Jelmer Vernooij
Add progress bar, fix compatibility with bzr.dev.
348
                        ie.revision = newrevid
349
                    else:
350
                        ie.revision = None
351
                else:
352
                    # or it was already there before the commit, in 
353
                    # which case the right revision should be used
354
                    if revid_renames.has_key(ie.revision):
355
                        ie.revision = revid_renames[ie.revision]
356
                    # make sure at least one of the new parents contains 
357
                    # the ie.file_id, ie.revision combination
0.436.50 by Jelmer Vernooij
Fix inconsistent parent errors.
358
                    if len(filter(lambda inv: ie.file_id in inv and inv[ie.file_id].revision == ie.revision, parent_invs)) == 0:
359
                        raise ReplayParentsInconsistent(ie.file_id, ie.revision)
0.436.47 by Jelmer Vernooij
Add progress bar, fix compatibility with bzr.dev.
360
                i += 1
361
                builder.record_entry_contents(ie, parent_invs, path, oldtree,
362
                        oldtree.path_content_summary(path))
363
        finally:
364
            pb.finished()
365
        builder.finish_inventory()
366
    except:
367
        builder.repository.abort_write_group()
368
        raise
0.436.5 by Jelmer Vernooij
Import change_revision_parent from bzr-svn.
369
    return builder.commit(oldrev.message)
370
371
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
372
def commit_rebase(wt, oldrev, newrevid):
373
    """Commit a rebase.
374
    
375
    :param wt: Mutable tree with the changes.
376
    :param oldrev: Revision info of new revision to commit.
377
    :param newrevid: New revision id."""
378
    assert oldrev.revision_id != newrevid
379
    revprops = dict(oldrev.properties)
0.436.27 by Jelmer Vernooij
Note revision property 'rebase-of', add explanation of use of pregenerated revision ids.
380
    revprops[REVPROP_REBASE_OF] = oldrev.revision_id
0.436.37 by Jelmer Vernooij
Store parents correctly.
381
    wt.commit(message=oldrev.message, timestamp=oldrev.timestamp, 
382
              timezone=oldrev.timezone, revprops=revprops, rev_id=newrevid)
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
383
    write_active_rebase_revid(wt, None)
384
385
0.436.54 by Jelmer Vernooij
Move determination of merge base to separate function.
386
def replay_determine_base(graph, oldrevid, oldparents, newrevid, newparents):
387
    # If this was the first commit, no base is needed
388
    if len(oldparents) == 0:
389
        return NULL_REVISION
390
391
    # In the case of a "simple" revision with just one parent, 
392
    # that parent should be the base
393
    if len(oldparents) == 1:
394
        return oldparents[0]
395
0.436.55 by Jelmer Vernooij
Add functinality for skipping duplicate merges.
396
    # In case the rhs parent(s) of the origin revision has already been merged
397
    # in the new branch, use diff between rhs parent and diff from 
398
    # original revision
399
    if len(newparents) == 1:
400
        # FIXME: Find oldparents entry that matches newparents[0] 
401
        # and return it
402
        return oldparents[1]
403
0.436.57 by Jelmer Vernooij
Allow overridding whether merges of already merged revisions should be replayed.
404
    try:
405
        return graph.find_unique_lca(*[oldparents[0],newparents[1]])
406
    except NoCommonAncestor:
407
        return oldparents[0]
0.436.54 by Jelmer Vernooij
Move determination of merge base to separate function.
408
409
0.438.1 by Jelmer Vernooij
Split out function for determining rebase base.
410
def replay_delta_workingtree(wt, oldrevid, newrevid, newparents, 
0.438.2 by Jelmer Vernooij
More work using proper merge bases.
411
                             merge_type=None):
0.436.7 by Jelmer Vernooij
Add more test, make basic rebase work.
412
    """Replay a commit in a working tree, with a different base.
413
414
    :param wt: Working tree in which to do the replays.
415
    :param oldrevid: Old revision id
416
    :param newrevid: New revision id
417
    :param newparents: New parent revision ids
418
    """
419
    repository = wt.branch.repository
420
    if merge_type is None:
421
        from bzrlib.merge import Merge3Merger
422
        merge_type = Merge3Merger
423
    oldrev = wt.branch.repository.get_revision(oldrevid)
424
    # Make sure there are no conflicts or pending merges/changes 
425
    # in the working tree
426
    if wt.changes_from(wt.basis_tree()).has_changed():
427
        raise BzrError("Working tree has uncommitted changes.")
0.438.2 by Jelmer Vernooij
More work using proper merge bases.
428
    complete_revert(wt, [newparents[0]])
0.436.8 by Jelmer Vernooij
Couple more minor fixes.
429
    assert not wt.changes_from(wt.basis_tree()).has_changed()
0.436.7 by Jelmer Vernooij
Add more test, make basic rebase work.
430
431
    oldtree = repository.revision_tree(oldrevid)
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
432
    write_active_rebase_revid(wt, oldrevid)
0.438.2 by Jelmer Vernooij
More work using proper merge bases.
433
    merger = Merger(wt.branch, this_tree=wt)
434
    merger.set_other_revision(oldrevid, wt.branch)
0.436.54 by Jelmer Vernooij
Move determination of merge base to separate function.
435
    base_revid = replay_determine_base(repository.get_graph(), 
436
                                       oldrevid, oldrev.parent_ids,
437
                                       newrevid, newparents)
0.436.55 by Jelmer Vernooij
Add functinality for skipping duplicate merges.
438
    mutter('replaying %r as %r with base %r and new parents %r' % 
439
           (oldrevid, newrevid, base_revid, newparents))
0.436.54 by Jelmer Vernooij
Move determination of merge base to separate function.
440
    merger.set_base_revision(base_revid, wt.branch)
0.438.2 by Jelmer Vernooij
More work using proper merge bases.
441
    merger.merge_type = merge_type
442
    merger.do_merge()
443
    for newparent in newparents[1:]:
444
        wt.add_pending_merge(newparent)
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
445
    commit_rebase(wt, oldrev, newrevid)
0.436.8 by Jelmer Vernooij
Couple more minor fixes.
446
0.436.10 by Jelmer Vernooij
Add more agressive version of revert.
447
0.436.13 by Jelmer Vernooij
Add progress bar, some optimizations. Make merge type configurable.
448
def workingtree_replay(wt, map_ids=False, merge_type=None):
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
449
    """Returns a function that can replay revisions in wt.
450
451
    :param wt: Working tree in which to do the replays.
0.436.7 by Jelmer Vernooij
Add more test, make basic rebase work.
452
    :param map_ids: Whether to try to map between file ids (False for path-based merge)
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
453
    """
454
    def replay(repository, oldrevid, newrevid, newparents):
0.436.7 by Jelmer Vernooij
Add more test, make basic rebase work.
455
        assert wt.branch.repository == repository
0.436.16 by Jelmer Vernooij
Some more work on maptree.
456
        return replay_delta_workingtree(wt, oldrevid, newrevid, newparents, 
457
                                        merge_type=merge_type)
0.436.6 by Jelmer Vernooij
Add somewhat more complex plan generation function, rebase implementation.
458
    return replay
0.436.7 by Jelmer Vernooij
Add more test, make basic rebase work.
459
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
460
461
def write_active_rebase_revid(wt, revid):
0.436.16 by Jelmer Vernooij
Some more work on maptree.
462
    """Write the id of the revision that is currently being rebased. 
463
464
    :param wt: Working Tree that is being used for the rebase.
465
    :param revid: Revision id to write
466
    """
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
467
    if revid is None:
468
        revid = NULL_REVISION
469
    wt._control_files.put_utf8(REBASE_CURRENT_REVID_FILENAME, revid)
470
0.436.10 by Jelmer Vernooij
Add more agressive version of revert.
471
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
472
def read_active_rebase_revid(wt):
0.436.16 by Jelmer Vernooij
Some more work on maptree.
473
    """Read the id of the revision that is currently being rebased.
474
475
    :param wt: Working Tree that is being used for the rebase.
476
    :return: Id of the revision that is being rebased.
477
    """
0.436.9 by Jelmer Vernooij
Add rebase-todo command, fix rebase-continue.
478
    try:
479
        text = wt._control_files.get(REBASE_CURRENT_REVID_FILENAME).read().rstrip("\n")
480
        if text == NULL_REVISION:
481
            return None
482
        return text
483
    except NoSuchFile:
484
        return None
0.436.10 by Jelmer Vernooij
Add more agressive version of revert.
485
486
487
def complete_revert(wt, newparents):
0.436.16 by Jelmer Vernooij
Some more work on maptree.
488
    """Simple helper that reverts to specified new parents and makes sure none 
489
    of the extra files are left around.
490
491
    :param wt: Working tree to use for rebase
492
    :param newparents: New parents of the working tree
0.436.10 by Jelmer Vernooij
Add more agressive version of revert.
493
    """
494
    newtree = wt.branch.repository.revision_tree(newparents[0])
495
    delta = wt.changes_from(newtree)
496
    wt.branch.generate_revision_history(newparents[0])
0.436.38 by Jelmer Vernooij
Handle directories in revert.
497
    wt.set_parent_ids(newparents[:1])
0.436.10 by Jelmer Vernooij
Add more agressive version of revert.
498
    for (f, _, _) in delta.added:
499
        abs_path = wt.abspath(f)
500
        if osutils.lexists(abs_path):
0.436.38 by Jelmer Vernooij
Handle directories in revert.
501
            if osutils.isdir(abs_path):
502
                osutils.rmtree(abs_path)
503
            else:
504
                os.unlink(abs_path)
0.436.48 by Jelmer Vernooij
Add replay command.
505
    wt.revert(None, old_tree=newtree, backups=False)
0.436.38 by Jelmer Vernooij
Handle directories in revert.
506
    assert not wt.changes_from(wt.basis_tree()).has_changed()
0.436.37 by Jelmer Vernooij
Store parents correctly.
507
    wt.set_parent_ids(newparents)
0.436.32 by Jelmer Vernooij
Properly detect invalid snapshot replays.
508
509
0.436.34 by Jelmer Vernooij
Some more tests.
510
class ReplaySnapshotError(BzrError):
0.436.32 by Jelmer Vernooij
Properly detect invalid snapshot replays.
511
    _fmt = """Replaying the snapshot failed: %(message)s."""
512
513
    def __init__(self, message):
514
        BzrError.__init__(self)
515
        self.message = message
0.436.34 by Jelmer Vernooij
Some more tests.
516
517
518
class ReplayParentsInconsistent(BzrError):
519
    _fmt = """Parents were inconsistent while replaying commit for file id %(fileid)s, revision %(revid)s."""
520
521
    def __init__(self, fileid, revid):
522
        BzrError.__init__(self)
523
        self.fileid = fileid
524
        self.revid = revid