13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
18
"""Upgrading revisions made with older versions of the mapping."""
19
from bzrlib.errors import (
25
from ...errors import (
25
from bzrlib.trace import info
29
class RebaseNotPresent(DependencyNotPresent):
30
_fmt = "Unable to import bzr-rebase (required for upgrade support): %(error)s"
32
def __init__(self, error):
33
DependencyNotPresent.__init__(self, 'bzr-rebase', error)
36
def check_rebase_version(min_version):
37
"""Check what version of bzr-rebase is installed.
39
Raises an exception when the version installed is older than
42
:raises RebaseNotPresent: Raised if bzr-rebase is not installed or too old.
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)
29
generate_transpose_plan,
30
CommitBuilderRevisionRewriter,
54
36
class UpgradeChangesContent(BzrError):
44
def create_deterministic_revid(revid, new_parents):
45
"""Create a new deterministic revision id with specified new parents.
63
def create_upgraded_revid(revid, mapping_suffix, upgrade_suffix="-upgrade"):
64
"""Create a new revision id for an upgraded version of a revision.
66
47
Prevents suffix to be appended needlessly.
68
49
:param revid: Original revision id.
69
50
:return: New revision id
71
if revid.endswith(upgrade_suffix):
72
return revid[0:revid.rfind("-svn")] + mapping_suffix + upgrade_suffix
74
return revid + mapping_suffix + upgrade_suffix
77
def determine_fileid_renames(old_inv, new_inv):
78
"""Determine the file ids based on a old and a new inventory that
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
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:
93
"Unable to find %s in new inventory" % old_file_id)
94
ret[new_inv.id2path(new_file_id)] = (old_file_id, new_file_id)
98
def update_workinginv_fileids(wt, old_inv, new_inv):
99
"""Update all file ids in wt according to old_tree/new_tree.
101
old_tree and new_tree should be two RevisionTree's that differ only
104
fileid_renames = determine_fileid_renames(old_inv, new_inv)
108
# Adjust file ids in working tree
109
# Sorted, so we process parents before children
110
for path in sorted(fileid_renames.keys()):
112
old_fileids.append(fileid_renames[path][0])
113
new_fileids.append((path, fileid_renames[path][1]))
115
new_root_id = fileid_renames[path][1]
116
new_fileids.reverse()
117
wt.unversion(old_fileids)
118
if new_root_id is not None:
119
wt.set_root_id(new_root_id)
120
wt.add([x[0] for x in new_fileids], [x[1] for x in new_fileids])
121
wt.set_last_revision(new_inv.revision_id)
124
def upgrade_workingtree(wt, foreign_repository, new_mapping,
125
allow_changes=False, verbose=False):
126
"""Upgrade a working tree.
128
:param foreign_repository: Foreign repository object
132
old_revid = wt.last_revision()
133
revid_renames = upgrade_branch(wt.branch, foreign_repository, new_mapping=new_mapping,
134
allow_changes=allow_changes, verbose=verbose)
135
last_revid = wt.branch.last_revision()
136
if old_revid == last_revid:
138
old_inv = wt.branch.repository.get_inventory(old_revid)
139
new_inv = wt.branch.repository.get_inventory(last_revid)
140
update_workinginv_fileids(wt, old_inv, new_inv)
147
def upgrade_tags(tags, repository, foreign_repository, new_mapping,
148
allow_changes=False, verbose=False, branch_renames=None):
52
if "-rebase-" in revid:
53
revid = revid[0:revid.rfind("-rebase-")]
54
return revid + "-rebase-" + osutils.sha_string(":".join(new_parents))[:8]
57
def upgrade_tags(tags, repository, generate_rebase_map, determine_new_revid,
58
allow_changes=False, verbose=False, branch_renames=None,
59
branch_ancestry=None):
149
60
"""Upgrade a tags dictionary."""
151
62
if branch_renames is not None:
153
64
pb = ui.ui_factory.nested_progress_bar()
155
66
tags_dict = tags.get_tag_dict()
156
for i, (name, revid) in enumerate(tags_dict.items()):
67
for i, (name, revid) in enumerate(tags_dict.iteritems()):
157
68
pb.update("upgrading tags", i, len(tags_dict))
158
if not revid in renames:
159
renames.update(upgrade_repository(repository, foreign_repository,
160
revision_id=revid, new_mapping=new_mapping,
161
allow_changes=allow_changes, verbose=verbose))
69
if revid not in renames:
71
repository.lock_read()
72
revid_exists = repository.has_revision(revid)
76
renames.update(upgrade_repository(
78
generate_rebase_map, determine_new_revid,
79
revision_id=revid, allow_changes=allow_changes,
81
if (revid in renames and
82
(branch_ancestry is None or revid not in branch_ancestry)):
163
83
tags.set_tag(name, renames[revid])
168
def upgrade_branch(branch, foreign_repository, new_mapping,
88
def upgrade_branch(branch, generate_rebase_map, determine_new_revid,
169
89
allow_changes=False, verbose=False):
170
90
"""Upgrade a branch to the current mapping version.
172
92
:param branch: Branch to upgrade.
173
93
:param foreign_repository: Repository to fetch new revisions from
174
94
:param allow_changes: Allow changes in mappings.
175
95
:param verbose: Whether to print verbose list of rewrites
177
97
revid = branch.last_revision()
178
renames = upgrade_repository(branch.repository, foreign_repository,
179
revision_id=revid, new_mapping=new_mapping,
180
allow_changes=allow_changes, verbose=verbose)
181
upgrade_tags(branch.tags, branch.repository, foreign_repository,
182
new_mapping=new_mapping,
183
allow_changes=allow_changes, verbose=verbose, branch_renames=renames)
98
renames = upgrade_repository(
99
branch.repository, generate_rebase_map,
100
determine_new_revid, revision_id=revid,
101
allow_changes=allow_changes, verbose=verbose)
184
102
if revid in renames:
185
103
branch.generate_revision_history(renames[revid])
104
ancestry = branch.repository.get_ancestry(
105
branch.last_revision(), topo_sorted=False)
107
branch.tags, branch.repository, generate_rebase_map,
108
determine_new_revid, allow_changes=allow_changes, verbose=verbose,
109
branch_renames=renames, branch_ancestry=ancestry)
189
113
def check_revision_changed(oldrev, newrev):
190
"""Check if two revisions are different. This is exactly the same
114
"""Check if two revisions are different. This is exactly the same
191
115
as Revision.equals() except that it does not check the revision_id."""
192
116
if (newrev.inventory_sha1 != oldrev.inventory_sha1 or
193
newrev.timestamp != oldrev.timestamp or
194
newrev.message != oldrev.message or
195
newrev.timezone != oldrev.timezone or
196
newrev.committer != oldrev.committer or
197
newrev.properties != oldrev.properties):
117
newrev.timestamp != oldrev.timestamp or
118
newrev.message != oldrev.message or
119
newrev.timezone != oldrev.timezone or
120
newrev.committer != oldrev.committer or
121
newrev.properties != oldrev.properties):
198
122
raise UpgradeChangesContent(oldrev.revision_id)
201
def generate_upgrade_map(revs, vcs, determine_upgraded_revid):
202
"""Generate an upgrade map for use by bzr-rebase.
204
:param new_mapping: Mapping to upgrade revisions to.
205
:param vcs: The foreign vcs
206
:param revs: Iterator over revisions to upgrade.
207
:return: Map from old revids as keys, new revids as values stored in a
211
# Create a list of revisions that can be renamed during the upgrade
213
assert isinstance(revid, str)
215
(foreign_revid, old_mapping) = vcs.mapping_registry.parse_revision_id(revid)
216
except InvalidRevisionId:
217
# Not a foreign revision, nothing to do
219
newrevid = determine_upgraded_revid(foreign_revid)
220
if newrevid in (revid, None):
222
rename_map[revid] = newrevid
225
MIN_REBASE_VERSION = (0, 4, 3)
227
def create_upgrade_plan(repository, foreign_repository, new_mapping,
125
def create_upgrade_plan(repository, generate_rebase_map, determine_new_revid,
228
126
revision_id=None, allow_changes=False):
229
127
"""Generate a rebase plan for upgrading revisions.
231
129
:param repository: Repository to do upgrade in
232
:param foreign_repository: Subversion repository to fetch new revisions from.
130
:param foreign_repository: Subversion repository to fetch new revisions
233
132
:param new_mapping: New mapping to use.
234
:param revision_id: Revision to upgrade (None for all revisions in
133
:param revision_id: Revision to upgrade (None for all revisions in
236
135
:param allow_changes: Whether an upgrade is allowed to change the contents
238
137
:return: Tuple with a rebase plan and map of renamed revisions.
240
from bzrlib.plugins.rebase.rebase import generate_transpose_plan
241
check_rebase_version(MIN_REBASE_VERSION)
243
140
graph = repository.get_graph()
244
if revision_id is None:
245
potential = repository.all_revision_ids()
247
potential = itertools.imap(lambda (rev, parents): rev,
248
graph.iter_ancestry([revision_id]))
250
def determine_upgraded_revid(foreign_revid):
251
# FIXME: Try all mappings until new_mapping rather than just new_mapping
252
new_revid = foreign_repository.upgrade_foreign_revision_id(foreign_revid, new_mapping)
253
if new_revid is None:
255
# Make sure the revision is there
256
if not repository.has_revision(new_revid):
258
repository.fetch(foreign_repository, new_revid)
259
except NoSuchRevision:
261
if not repository.has_revision(new_revid):
265
upgrade_map = generate_upgrade_map(potential, foreign_repository.vcs,
266
determine_upgraded_revid)
141
upgrade_map = generate_rebase_map(revision_id)
268
143
if not allow_changes:
269
144
for oldrevid, newrevid in upgrade_map.iteritems():
270
145
oldrev = repository.get_revision(oldrevid)
272
147
check_revision_changed(oldrev, newrev)
274
149
if revision_id is None:
275
heads = repository.all_revision_ids()
150
heads = repository.all_revision_ids()
277
152
heads = [revision_id]
279
def determine_new_revid(old_revid):
280
# If this revision id already exists round-tripped upstream,
282
if foreign_repository.has_revision(old_revid):
284
# if not, return old_revid'
285
return create_upgraded_revid(old_revid, new_mapping.upgrade_suffix)
287
plan = generate_transpose_plan(graph.iter_ancestry(heads), upgrade_map,
288
graph, determine_new_revid)
289
def remove_parents((oldrevid, (newrevid, parents))):
154
plan = generate_transpose_plan(
155
graph.iter_ancestry(heads), upgrade_map, graph, determine_new_revid)
156
def remove_parents(entry):
157
(oldrevid, (newrevid, parents)) = entry
290
158
return (oldrevid, newrevid)
291
159
upgrade_map.update(dict(map(remove_parents, plan.iteritems())))
293
161
return (plan, upgrade_map)
296
def upgrade_repository(repository, foreign_repository, new_mapping,
297
revision_id=None, allow_changes=False,
164
def upgrade_repository(repository, generate_rebase_map,
165
determine_new_revid, revision_id=None,
166
allow_changes=False, verbose=False):
299
167
"""Upgrade the revisions in repository until the specified stop revision.
301
169
:param repository: Repository in which to upgrade.
302
170
:param foreign_repository: Repository to fetch new revisions from.
303
171
:param new_mapping: New mapping.
304
:param revision_id: Revision id up until which to upgrade, or None for
172
:param revision_id: Revision id up until which to upgrade, or None for
306
174
:param allow_changes: Allow changes to mappings.
307
175
:param verbose: Whether to print list of rewrites
308
176
:return: Dictionary of mapped revisions
310
check_rebase_version(MIN_REBASE_VERSION)
311
from bzrlib.plugins.rebase.rebase import (
312
replay_snapshot, rebase, rebase_todo)
314
178
# Find revisions that need to be upgraded, create
315
179
# dictionary with revision ids in key, new parents in value
317
repository.lock_write()
318
foreign_repository.lock_read()
319
(plan, revid_renames) = create_upgrade_plan(repository, foreign_repository,
321
revision_id=revision_id,
322
allow_changes=allow_changes)
180
with repository.lock_write():
181
(plan, revid_renames) = create_upgrade_plan(
182
repository, generate_rebase_map, determine_new_revid,
183
revision_id=revision_id, allow_changes=allow_changes)
324
185
for revid in rebase_todo(repository, plan):
325
info("%s -> %s" % (revid, plan[revid][0]))
326
rebase(repository, plan, replay_snapshot)
186
trace.note("%s -> %s" % (revid, plan[revid][0]))
187
rebase(repository, plan, CommitBuilderRevisionRewriter(repository))
327
188
return revid_renames
330
foreign_repository.unlock()