/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: 2009-02-01 04:08:46 UTC
  • mto: (0.205.39 trunk)
  • mto: This revision was merged to the branch mainline in revision 6960.
  • Revision ID: jelmer@samba.org-20090201040846-191viq9riqwholfh
s/get_revision/get_inventory.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006-2009 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 (
 
20
    DependencyNotPresent,
 
21
    BzrError,
 
22
    InvalidRevisionId,
 
23
    NoSuchRevision,
 
24
    )
 
25
from bzrlib.trace import info
 
26
 
 
27
import itertools
 
28
 
 
29
class RebaseNotPresent(DependencyNotPresent):
 
30
    _fmt = "Unable to import bzr-rebase (required for upgrade support): %(error)s"
 
31
 
 
32
    def __init__(self, error):
 
33
        DependencyNotPresent.__init__(self, 'bzr-rebase', error)
 
34
 
 
35
 
 
36
def check_rebase_version(min_version):
 
37
    """Check what version of bzr-rebase is installed.
 
38
 
 
39
    Raises an exception when the version installed is older than 
 
40
    min_version.
 
41
 
 
42
    :raises RebaseNotPresent: Raised if bzr-rebase is not installed or too old.
 
43
    """
 
44
    try:
 
45
        from bzrlib.plugins.rebase import version_info as rebase_version_info
 
46
        if rebase_version_info[:len(min_version)] < min_version:
 
47
            raise RebaseNotPresent("Version %r present, at least %r required" 
 
48
                                   % (rebase_version_info, min_version))
 
49
    except ImportError, e:
 
50
        raise RebaseNotPresent(e)
 
51
 
 
52
 
 
53
 
 
54
class UpgradeChangesContent(BzrError):
 
55
    """Inconsistency was found upgrading the mapping of a revision."""
 
56
    _fmt = """Upgrade will change contents in revision %(revid)s. Use --allow-changes to override."""
 
57
 
 
58
    def __init__(self, revid):
 
59
        self.revid = revid
 
60
 
 
61
 
 
62
 
 
63
def create_upgraded_revid(revid, mapping_suffix, upgrade_suffix="-upgrade"):
 
64
    """Create a new revision id for an upgraded version of a revision.
 
65
    
 
66
    Prevents suffix to be appended needlessly.
 
67
 
 
68
    :param revid: Original revision id.
 
69
    :return: New revision id
 
70
    """
 
71
    if revid.endswith(upgrade_suffix):
 
72
        return revid[0:revid.rfind("-svn")] + mapping_suffix + upgrade_suffix
 
73
    else:
 
74
        return revid + mapping_suffix + upgrade_suffix
 
75
 
 
76
 
 
77
def determine_fileid_renames(old_inv, new_inv):
 
78
    """Determine the file ids based on a old and a new inventory that 
 
79
    are equal in content.
 
80
 
 
81
    :param old_inv: Old inventory
 
82
    :param new_inv: New inventory
 
83
    :return: Dictionary a (old_id, new_id) tuple for each path in the 
 
84
        inventories.
 
85
    """
 
86
    ret = {}
 
87
    if len(old_inv) != len(new_inv):
 
88
        raise AssertionError("Inventories are not of the same size")
 
89
    for old_file_id in old_inv:
 
90
        new_file_id = new_inv.path2id(old_inv.id2path(old_file_id))
 
91
        if new_file_id is None:
 
92
            raise AssertionError(
 
93
                "Unable to find %s in new inventory" % old_file_id)
 
94
        if new_file_id != old_file_id:
 
95
            ret[new_inv.id2path(new_file_id)] = (old_file_id, new_file_id)
 
96
    return ret
 
97
 
 
98
 
 
99
def update_workinginv_fileids(wt, old_inv, new_inv):
 
100
    """Update all file ids in wt according to old_tree/new_tree. 
 
101
 
 
102
    old_tree and new_tree should be two RevisionTree's that differ only
 
103
    in file ids.
 
104
    """
 
105
    fileid_renames = determine_fileid_renames(old_inv, new_inv)
 
106
    old_fileids = []
 
107
    new_fileids = []
 
108
    new_root_id = None
 
109
    # Adjust file ids in working tree
 
110
    # Sorted, so we process parents before children
 
111
    for path in sorted(fileid_renames.keys(), reverse=True):
 
112
        if path != "":
 
113
            old_fileids.append(fileid_renames[path][0])
 
114
            new_fileids.append((path, fileid_renames[path][1]))
 
115
        else:
 
116
            new_root_id = fileid_renames[path][1]
 
117
    new_fileids.reverse()
 
118
    wt.unversion(old_fileids)
 
119
    if new_root_id is not None:
 
120
        wt.set_root_id(new_root_id)
 
121
    wt.add([x[0] for x in new_fileids], [x[1] for x in new_fileids])
 
122
    wt.set_last_revision(new_inv.revision_id)
 
123
 
 
124
 
 
125
def upgrade_workingtree(wt, foreign_repository, new_mapping, mapping_registry, 
 
126
                        allow_changes=False, verbose=False):
 
127
    """Upgrade a working tree.
 
128
 
 
129
    :param foreign_repository: Foreign repository object
 
130
    """
 
131
    wt.lock_write()
 
132
    try:
 
133
        old_revid = wt.last_revision()
 
134
        revid_renames = upgrade_branch(wt.branch, foreign_repository, new_mapping=new_mapping,
 
135
                                 mapping_registry=mapping_registry,
 
136
                                 allow_changes=allow_changes, verbose=verbose)
 
137
        last_revid = wt.branch.last_revision()
 
138
        if old_revid == last_revid:
 
139
            return revid_renames
 
140
        old_inv = wt.branch.repository.get_inventory(old_revid)
 
141
        new_inv = wt.branch.repository.get_inventory(last_revid)
 
142
        update_workinginv_fileids(wt, old_inv, new_inv)
 
143
    finally:
 
144
        wt.unlock()
 
145
 
 
146
    return revid_renames
 
147
 
 
148
 
 
149
def upgrade_tags(tags, repository, foreign_repository, new_mapping, mapping_registry, 
 
150
                 allow_changes=False, verbose=False, branch_renames=None):
 
151
    """Upgrade a tags dictionary."""
 
152
    renames = {}
 
153
    if branch_renames is not None:
 
154
        renames.update(branch_renames)
 
155
    pb = ui.ui_factory.nested_progress_bar()
 
156
    try:
 
157
        tags_dict = tags.get_tag_dict()
 
158
        for i, (name, revid) in enumerate(tags_dict.items()):
 
159
            pb.update("upgrading tags", i, len(tags_dict))
 
160
            if not revid in renames:
 
161
                renames.update(upgrade_repository(repository, foreign_repository, 
 
162
                      revision_id=revid, new_mapping=new_mapping,
 
163
                      mapping_registry=mapping_registry,
 
164
                      allow_changes=allow_changes, verbose=verbose))
 
165
            if revid in renames:
 
166
                tags.set_tag(name, renames[revid])
 
167
    finally:
 
168
        pb.finished()
 
169
 
 
170
 
 
171
def upgrade_branch(branch, foreign_repository, new_mapping, 
 
172
                   mapping_registry, allow_changes=False, verbose=False):
 
173
    """Upgrade a branch to the current mapping version.
 
174
    
 
175
    :param branch: Branch to upgrade.
 
176
    :param foreign_repository: Repository to fetch new revisions from
 
177
    :param allow_changes: Allow changes in mappings.
 
178
    :param verbose: Whether to print verbose list of rewrites
 
179
    """
 
180
    revid = branch.last_revision()
 
181
    renames = upgrade_repository(branch.repository, foreign_repository, 
 
182
              revision_id=revid, new_mapping=new_mapping,
 
183
              mapping_registry=mapping_registry,
 
184
              allow_changes=allow_changes, verbose=verbose)
 
185
    upgrade_tags(branch.tags, branch.repository, foreign_repository, 
 
186
           new_mapping=new_mapping, mapping_registry=mapping_registry, 
 
187
           allow_changes=allow_changes, verbose=verbose, branch_renames=renames)
 
188
    if len(renames) > 0:
 
189
        branch.generate_revision_history(renames[revid])
 
190
    return renames
 
191
 
 
192
 
 
193
def check_revision_changed(oldrev, newrev):
 
194
    """Check if two revisions are different. This is exactly the same 
 
195
    as Revision.equals() except that it does not check the revision_id."""
 
196
    if (newrev.inventory_sha1 != oldrev.inventory_sha1 or
 
197
        newrev.timestamp != oldrev.timestamp or
 
198
        newrev.message != oldrev.message or
 
199
        newrev.timezone != oldrev.timezone or
 
200
        newrev.committer != oldrev.committer or
 
201
        newrev.properties != oldrev.properties):
 
202
        raise UpgradeChangesContent(oldrev.revision_id)
 
203
 
 
204
 
 
205
def generate_upgrade_map(revs, mapping_registry, determine_upgraded_revid):
 
206
    """Generate an upgrade map for use by bzr-rebase.
 
207
 
 
208
    :param new_mapping: Mapping to upgrade revisions to.
 
209
    :param revs: Iterator over revisions to upgrade.
 
210
    :return: Map from old revids as keys, new revids as values stored in a 
 
211
             dictionary.
 
212
    """
 
213
    rename_map = {}
 
214
    # Create a list of revisions that can be renamed during the upgrade
 
215
    for revid in revs:
 
216
        assert isinstance(revid, str)
 
217
        try:
 
218
            (foreign_revid, old_mapping) = mapping_registry.parse_revision_id(revid)
 
219
        except InvalidRevisionId:
 
220
            # Not a foreign revision, nothing to do
 
221
            continue
 
222
        newrevid = determine_upgraded_revid(foreign_revid)
 
223
        if newrevid in (revid, None):
 
224
            continue
 
225
        rename_map[revid] = newrevid
 
226
    return rename_map
 
227
 
 
228
MIN_REBASE_VERSION = (0, 4, 3)
 
229
 
 
230
def create_upgrade_plan(repository, foreign_repository, new_mapping,
 
231
                        mapping_registry, revision_id=None, allow_changes=False):
 
232
    """Generate a rebase plan for upgrading revisions.
 
233
 
 
234
    :param repository: Repository to do upgrade in
 
235
    :param foreign_repository: Subversion repository to fetch new revisions from.
 
236
    :param new_mapping: New mapping to use.
 
237
    :param revision_id: Revision to upgrade (None for all revisions in 
 
238
        repository.)
 
239
    :param allow_changes: Whether an upgrade is allowed to change the contents
 
240
        of revisions.
 
241
    :return: Tuple with a rebase plan and map of renamed revisions.
 
242
    """
 
243
    from bzrlib.plugins.rebase.rebase import generate_transpose_plan
 
244
    check_rebase_version(MIN_REBASE_VERSION)
 
245
 
 
246
    graph = repository.get_graph()
 
247
    if revision_id is None:
 
248
        potential = repository.all_revision_ids()
 
249
    else:
 
250
        potential = itertools.imap(lambda (rev, parents): rev, 
 
251
                graph.iter_ancestry([revision_id]))
 
252
 
 
253
    def determine_upgraded_revid(foreign_revid):
 
254
        # FIXME: Try all mappings until new_mapping rather than just new_mapping
 
255
        new_revid = new_mapping.revision_id_foreign_to_bzr(foreign_revid)
 
256
        # Make sure the revision is there
 
257
        if not repository.has_revision(new_revid):
 
258
            try:
 
259
                repository.fetch(foreign_repository, new_revid)
 
260
            except NoSuchRevision:
 
261
                return None
 
262
        return new_revid
 
263
 
 
264
    upgrade_map = generate_upgrade_map(potential, mapping_registry, determine_upgraded_revid)
 
265
   
 
266
    if not allow_changes:
 
267
        for oldrevid, newrevid in upgrade_map.iteritems():
 
268
            oldrev = repository.get_revision(oldrevid)
 
269
            newrev = repository.get_revision(newrevid)
 
270
            check_revision_changed(oldrev, newrev)
 
271
 
 
272
    if revision_id is None:
 
273
        heads = repository.all_revision_ids() 
 
274
    else:
 
275
        heads = [revision_id]
 
276
 
 
277
    plan = generate_transpose_plan(graph.iter_ancestry(heads), upgrade_map, 
 
278
      graph, lambda revid: create_upgraded_revid(revid, new_mapping.upgrade_suffix))
 
279
    def remove_parents((oldrevid, (newrevid, parents))):
 
280
        return (oldrevid, newrevid)
 
281
    upgrade_map.update(dict(map(remove_parents, plan.iteritems())))
 
282
 
 
283
    return (plan, upgrade_map)
 
284
 
 
285
 
 
286
def upgrade_repository(repository, foreign_repository, new_mapping, 
 
287
                       mapping_registry, revision_id=None, allow_changes=False, 
 
288
                       verbose=False):
 
289
    """Upgrade the revisions in repository until the specified stop revision.
 
290
 
 
291
    :param repository: Repository in which to upgrade.
 
292
    :param foreign_repository: Repository to fetch new revisions from.
 
293
    :param new_mapping: New mapping.
 
294
    :param revision_id: Revision id up until which to upgrade, or None for 
 
295
                        all revisions.
 
296
    :param allow_changes: Allow changes to mappings.
 
297
    :param verbose: Whether to print list of rewrites
 
298
    :return: Dictionary of mapped revisions
 
299
    """
 
300
    check_rebase_version(MIN_REBASE_VERSION)
 
301
    from bzrlib.plugins.rebase.rebase import (
 
302
        replay_snapshot, rebase, rebase_todo)
 
303
 
 
304
    # Find revisions that need to be upgraded, create
 
305
    # dictionary with revision ids in key, new parents in value
 
306
    try:
 
307
        repository.lock_write()
 
308
        foreign_repository.lock_read()
 
309
        (plan, revid_renames) = create_upgrade_plan(repository, foreign_repository, 
 
310
                                                    new_mapping, mapping_registry,
 
311
                                                    revision_id=revision_id,
 
312
                                                    allow_changes=allow_changes)
 
313
        if verbose:
 
314
            for revid in rebase_todo(repository, plan):
 
315
                info("%s -> %s" % (revid, plan[revid][0]))
 
316
        rebase(repository, plan, replay_snapshot)
 
317
        return revid_renames
 
318
    finally:
 
319
        repository.unlock()
 
320
        foreign_repository.unlock()
 
321