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