/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to upgrade.py

  • Committer: Jelmer Vernooij
  • Date: 2008-11-12 14:36:10 UTC
  • mto: (0.219.2 trunk)
  • mto: This revision was merged to the branch mainline in revision 6960.
  • Revision ID: jelmer@samba.org-20081112143610-30109vbbkeuj94y0
import bzr-svn improvements.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006,2008 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 3 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
"""Upgrading revisions made with older versions of the mapping."""
 
17
 
 
18
from bzrlib import ui
 
19
from bzrlib.errors import BzrError, InvalidRevisionId, DependencyNotPresent
 
20
from bzrlib.trace import info
 
21
 
 
22
import itertools
 
23
 
 
24
class RebaseNotPresent(DependencyNotPresent):
 
25
    _fmt = "Unable to import bzr-rebase (required for upgrade support): %(error)s"
 
26
 
 
27
    def __init__(self, error):
 
28
        DependencyNotPresent.__init__(self, 'bzr-rebase', error)
 
29
 
 
30
 
 
31
def check_rebase_version(min_version):
 
32
    """Check what version of bzr-rebase is installed.
 
33
 
 
34
    Raises an exception when the version installed is older than 
 
35
    min_version.
 
36
 
 
37
    :raises RebaseNotPresent: Raised if bzr-rebase is not installed or too old.
 
38
    """
 
39
    try:
 
40
        from bzrlib.plugins.rebase import version_info as rebase_version_info
 
41
        if rebase_version_info[:2] < min_version:
 
42
            raise RebaseNotPresent("Version %r present, at least %r required" 
 
43
                                   % (rebase_version_info, min_version))
 
44
    except ImportError, e:
 
45
        raise RebaseNotPresent(e)
 
46
 
 
47
 
 
48
 
 
49
class UpgradeChangesContent(BzrError):
 
50
    """Inconsistency was found upgrading the mapping of a revision."""
 
51
    _fmt = """Upgrade will change contents in revision %(revid)s. Use --allow-changes to override."""
 
52
 
 
53
    def __init__(self, revid):
 
54
        self.revid = revid
 
55
 
 
56
 
 
57
 
 
58
def create_upgraded_revid(revid, mapping_suffix, upgrade_suffix="-upgrade"):
 
59
    """Create a new revision id for an upgraded version of a revision.
 
60
    
 
61
    Prevents suffix to be appended needlessly.
 
62
 
 
63
    :param revid: Original revision id.
 
64
    :return: New revision id
 
65
    """
 
66
    if revid.endswith(upgrade_suffix):
 
67
        return revid[0:revid.rfind("-svn")] + mapping_suffix + upgrade_suffix
 
68
    else:
 
69
        return revid + mapping_suffix + upgrade_suffix
 
70
 
 
71
 
 
72
def determine_fileid_renames(old_tree, new_tree):
 
73
    for old_file_id in old_tree:
 
74
        new_file_id = new_tree.path2id(old_tree.id2path(old_file_id))
 
75
        if old_file_id == new_file_id:
 
76
            continue
 
77
        if new_file_id is not None:
 
78
            yield new_tree.id2path(new_file_id), old_file_id, new_file_id
 
79
 
 
80
 
 
81
def upgrade_workingtree(wt, foreign_repository, new_mapping, mapping_registry, 
 
82
                        allow_changes=False, verbose=False):
 
83
    """Upgrade a working tree.
 
84
 
 
85
    :param foreign_repository: Foreign repository object
 
86
    """
 
87
    wt.lock_write()
 
88
    try:
 
89
        old_revid = wt.last_revision()
 
90
        revid_renames = upgrade_branch(wt.branch, foreign_repository, new_mapping=new_mapping,
 
91
                                 mapping_registry=mapping_registry,
 
92
                                 allow_changes=allow_changes, verbose=verbose)
 
93
        last_revid = wt.branch.last_revision()
 
94
        if old_revid == last_revid:
 
95
            return revid_renames
 
96
 
 
97
        fileid_renames = dict([(path, (old_fileid, new_fileid)) for (path, old_fileid, new_fileid) in determine_fileid_renames(wt.branch.repository.revision_tree(old_revid), wt.branch.repository.revision_tree(last_revid))])
 
98
        old_fileids = []
 
99
        new_fileids = []
 
100
        new_root_id = None
 
101
        # Adjust file ids in working tree
 
102
        for path in sorted(fileid_renames.keys(), reverse=True):
 
103
            if path != "":
 
104
                old_fileids.append(fileid_renames[path][0])
 
105
                new_fileids.append((path, fileid_renames[path][1]))
 
106
            else:
 
107
                new_root_id = fileid_renames[path][1]
 
108
        new_fileids.reverse()
 
109
        wt.unversion(old_fileids)
 
110
        if new_root_id is not None:
 
111
            wt.set_root_id(new_root_id)
 
112
        wt.add([x[0] for x in new_fileids], [x[1] for x in new_fileids])
 
113
        wt.set_last_revision(last_revid)
 
114
    finally:
 
115
        wt.unlock()
 
116
 
 
117
    return revid_renames
 
118
 
 
119
 
 
120
def upgrade_tags(tags, repository, foreign_repository, new_mapping, mapping_registry, 
 
121
                 allow_changes=False, verbose=False, branch_renames=None):
 
122
    """Upgrade a tags dictionary."""
 
123
    pb = ui.ui_factory.nested_progress_bar()
 
124
    try:
 
125
        tags_dict = tags.get_tag_dict()
 
126
        for i, (name, revid) in enumerate(tags_dict.items()):
 
127
            pb.update("upgrading tags", i, len(tags_dict))
 
128
            if branch_renames is not None and revid in branch_renames:
 
129
                renames = branch_renames
 
130
            else:
 
131
                renames = upgrade_repository(repository, foreign_repository, 
 
132
                      revision_id=revid, new_mapping=new_mapping,
 
133
                      mapping_registry=mapping_registry,
 
134
                      allow_changes=allow_changes, verbose=verbose)
 
135
            if revid in renames:
 
136
                tags.set_tag(name, renames[revid])
 
137
    finally:
 
138
        pb.finished()
 
139
 
 
140
 
 
141
def upgrade_branch(branch, foreign_repository, new_mapping, 
 
142
                   mapping_registry, allow_changes=False, verbose=False):
 
143
    """Upgrade a branch to the current mapping version.
 
144
    
 
145
    :param branch: Branch to upgrade.
 
146
    :param foreign_repository: Repository to fetch new revisions from
 
147
    :param allow_changes: Allow changes in mappings.
 
148
    :param verbose: Whether to print verbose list of rewrites
 
149
    """
 
150
    revid = branch.last_revision()
 
151
    renames = upgrade_repository(branch.repository, foreign_repository, 
 
152
              revision_id=revid, new_mapping=new_mapping,
 
153
              mapping_registry=mapping_registry,
 
154
              allow_changes=allow_changes, verbose=verbose)
 
155
    upgrade_tags(branch.tags, branch.repository, foreign_repository, 
 
156
           new_mapping=new_mapping, mapping_registry=mapping_registry, 
 
157
           allow_changes=allow_changes, verbose=verbose)
 
158
    if len(renames) > 0:
 
159
        branch.generate_revision_history(renames[revid])
 
160
    return renames
 
161
 
 
162
 
 
163
def check_revision_changed(oldrev, newrev):
 
164
    """Check if two revisions are different. This is exactly the same 
 
165
    as Revision.equals() except that it does not check the revision_id."""
 
166
    if (newrev.inventory_sha1 != oldrev.inventory_sha1 or
 
167
        newrev.timestamp != oldrev.timestamp or
 
168
        newrev.message != oldrev.message or
 
169
        newrev.timezone != oldrev.timezone or
 
170
        newrev.committer != oldrev.committer or
 
171
        newrev.properties != oldrev.properties):
 
172
        raise UpgradeChangesContent(oldrev.revision_id)
 
173
 
 
174
 
 
175
def generate_upgrade_map(new_mapping, revs, mapping_registry):
 
176
    """Generate an upgrade map for use by bzr-rebase.
 
177
 
 
178
    :param new_mapping: Mapping to upgrade revisions to.
 
179
    :param revs: Iterator over revisions to upgrade.
 
180
    :return: Map from old revids as keys, new revids as values stored in a 
 
181
             dictionary.
 
182
    """
 
183
    rename_map = {}
 
184
    # Create a list of revisions that can be renamed during the upgrade
 
185
    for revid in revs:
 
186
        assert isinstance(revid, str)
 
187
        try:
 
188
            (foreign_revid, _) = mapping_registry.parse_revision_id(revid)
 
189
        except InvalidRevisionId:
 
190
            # Not a foreign revision, nothing to do
 
191
            continue
 
192
        newrevid = new_mapping.revision_id_foreign_to_bzr(foreign_revid)
 
193
        if revid == newrevid:
 
194
            continue
 
195
        rename_map[revid] = newrevid
 
196
 
 
197
    return rename_map
 
198
 
 
199
MIN_REBASE_VERSION = (0, 4)
 
200
 
 
201
def create_upgrade_plan(repository, foreign_repository, new_mapping,
 
202
                        mapping_registry, revision_id=None, allow_changes=False):
 
203
    """Generate a rebase plan for upgrading revisions.
 
204
 
 
205
    :param repository: Repository to do upgrade in
 
206
    :param foreign_repository: Subversion repository to fetch new revisions from.
 
207
    :param new_mapping: New mapping to use.
 
208
    :param revision_id: Revision to upgrade (None for all revisions in 
 
209
        repository.)
 
210
    :param allow_changes: Whether an upgrade is allowed to change the contents
 
211
        of revisions.
 
212
    :return: Tuple with a rebase plan and map of renamed revisions.
 
213
    """
 
214
    from bzrlib.plugins.rebase.rebase import generate_transpose_plan
 
215
    check_rebase_version(MIN_REBASE_VERSION)
 
216
 
 
217
    graph = repository.get_graph()
 
218
    if revision_id is None:
 
219
        potential = repository.all_revision_ids()
 
220
    else:
 
221
        potential = itertools.imap(lambda (rev, parents): rev, 
 
222
                graph.iter_ancestry([revision_id]))
 
223
    upgrade_map = generate_upgrade_map(new_mapping, potential, mapping_registry)
 
224
   
 
225
    # Make sure all the required current version revisions are present
 
226
    for revid in upgrade_map.values():
 
227
        if not repository.has_revision(revid):
 
228
            repository.fetch(foreign_repository, revid)
 
229
 
 
230
    if not allow_changes:
 
231
        for oldrevid, newrevid in upgrade_map.items():
 
232
            oldrev = repository.get_revision(oldrevid)
 
233
            newrev = repository.get_revision(newrevid)
 
234
            check_revision_changed(oldrev, newrev)
 
235
 
 
236
    if revision_id is None:
 
237
        heads = repository.all_revision_ids() 
 
238
    else:
 
239
        heads = [revision_id]
 
240
 
 
241
    plan = generate_transpose_plan(graph.iter_ancestry(heads), upgrade_map, 
 
242
      graph, lambda revid: create_upgraded_revid(revid, new_mapping.upgrade_suffix))
 
243
    def remove_parents((oldrevid, (newrevid, parents))):
 
244
        return (oldrevid, newrevid)
 
245
    upgrade_map.update(dict(map(remove_parents, plan.items())))
 
246
 
 
247
    return (plan, upgrade_map)
 
248
 
 
249
 
 
250
def upgrade_repository(repository, foreign_repository, new_mapping, 
 
251
                       mapping_registry, revision_id=None, allow_changes=False, 
 
252
                       verbose=False):
 
253
    """Upgrade the revisions in repository until the specified stop revision.
 
254
 
 
255
    :param repository: Repository in which to upgrade.
 
256
    :param foreign_repository: Repository to fetch new revisions from.
 
257
    :param new_mapping: New mapping.
 
258
    :param revision_id: Revision id up until which to upgrade, or None for 
 
259
                        all revisions.
 
260
    :param allow_changes: Allow changes to mappings.
 
261
    :param verbose: Whether to print list of rewrites
 
262
    :return: Dictionary of mapped revisions
 
263
    """
 
264
    check_rebase_version(MIN_REBASE_VERSION)
 
265
    from bzrlib.plugins.rebase.rebase import (
 
266
        replay_snapshot, rebase, rebase_todo)
 
267
 
 
268
    # Find revisions that need to be upgraded, create
 
269
    # dictionary with revision ids in key, new parents in value
 
270
    try:
 
271
        repository.lock_write()
 
272
        foreign_repository.lock_read()
 
273
        (plan, revid_renames) = create_upgrade_plan(repository, foreign_repository, 
 
274
                                                    new_mapping, mapping_registry,
 
275
                                                    revision_id=revision_id,
 
276
                                                    allow_changes=allow_changes)
 
277
        if verbose:
 
278
            for revid in rebase_todo(repository, plan):
 
279
                info("%s -> %s" % (revid, plan[revid][0]))
 
280
        def fix_revid(revid):
 
281
            try:
 
282
                (foreign_revid, mapping) = mapping_registry.parse_revision_id(revid)
 
283
            except InvalidRevisionId:
 
284
                return revid
 
285
            return new_mapping.revision_id_foreign_to_bzr(foreign_revid)
 
286
        def replay(repository, oldrevid, newrevid, new_parents):
 
287
            return replay_snapshot(repository, oldrevid, newrevid, new_parents,
 
288
                                   revid_renames, fix_revid)
 
289
        rebase(repository, plan, replay)
 
290
        return revid_renames
 
291
    finally:
 
292
        repository.unlock()
 
293
        foreign_repository.unlock()
 
294