/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-10 16:14:20 UTC
  • mto: (0.219.2 trunk)
  • mto: This revision was merged to the branch mainline in revision 6960.
  • Revision ID: jelmer@samba.org-20081110161420-bax1raw5jkm1p67h
Import fixes from bzr-svn.

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