19
19
# change upgrade from .bzr to create a '.bzr-new', then do a bait and switch.
22
# To make this properly useful
24
# 1. assign text version ids, and put those text versions into
25
# the inventory as they're converted.
27
# 2. keep track of the previous version of each file, rather than
28
# just using the last one imported
30
# 3. assign entry versions when files are added, renamed or moved.
32
# 4. when merged-in versions are observed, walk down through them
33
# to discover everything, then commit bottom-up
35
# 5. track ancestry as things are merged in, and commit that in each
38
# Perhaps it's best to first walk the whole graph and make a plan for
39
# what should be imported in what order? Need a kind of topological
40
# sort of all revisions. (Or do we, can we just before doing a revision
41
# see that all its parents have either been converted or abandoned?)
44
# Cannot import a revision until all its parents have been
45
# imported. in other words, we can only import revisions whose
46
# parents have all been imported. the first step must be to
47
# import a revision with no parents, of which there must be at
48
# least one. (So perhaps it's useful to store forward pointers
49
# from a list of parents to their children?)
51
# Another (equivalent?) approach is to build up the ordered
52
# ancestry list for the last revision, and walk through that. We
53
# are going to need that.
55
# We don't want to have to recurse all the way back down the list.
57
# Suppose we keep a queue of the revisions able to be processed at
58
# any point. This starts out with all the revisions having no
61
# This seems like a generally useful algorithm...
63
# The current algorithm is dumb (O(n**2)?) but will do the job, and
64
# takes less than a second on the bzr.dev branch.
66
# This currently does a kind of lazy conversion of file texts, where a
67
# new text is written in every version. That's unnecessary but for
68
# the moment saves us having to worry about when files need new
71
22
from cStringIO import StringIO
96
46
from bzrlib.xml5 import serializer_v5
99
class Convert(object):
101
def __init__(self, transport):
102
self.base = transport.base
49
class Converter(object):
50
"""Converts a disk format object from one format to another."""
52
def __init__(self, pb):
53
"""Create a converter.
55
:param pb: a progress bar to use for progress information.
60
class ConvertBzrDir4To5(Converter):
61
"""Converts format 4 bzr dirs to format 5."""
63
def __init__(self, to_convert, pb):
64
"""Create a converter.
66
:param to_convert: The disk object to convert.
67
:param pb: a progress bar to use for progress information.
69
super(ConvertBzrDir4To5, self).__init__(pb)
70
self.bzrdir = to_convert
103
71
self.converted_revs = set()
104
72
self.absent_revisions = set()
105
73
self.text_count = 0
106
74
self.revisions = {}
107
self.transport = transport
108
if self.transport.is_readonly():
109
raise UpgradeReadonly
110
self.control_files = LockableFiles(transport.clone(bzrlib.BZRDIR), 'branch-lock')
111
# Lock the branch (soon to be meta dir) to prevent anyone racing with us
112
# This is currently windows incompatible, it will deadlock. When the upgrade
113
# logic becomes format specific, then we can have the format know how to pass this
114
# on. Also note that we probably have an 'upgrade meta' which upgrades the constituent
116
# FIXME: control files reuse
117
self.control_files.lock_write()
121
self.control_files.unlock()
123
76
def convert(self):
124
if not self._open_branch():
126
note('starting upgrade of %s', self.base)
127
self._backup_control_dir()
128
self.pb = ui_factory.progress_bar()
129
if isinstance(self.old_format, BzrDirFormat4):
130
note('starting upgrade from format 4 to 5')
131
if isinstance(self.transport, LocalTransport):
132
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
133
self._convert_to_weaves()
134
if isinstance(self.old_format, BzrDirFormat5):
135
note('starting upgrade from format 5 to 6')
136
self._convert_to_prefixed()
139
def _convert_to_prefixed(self):
140
from bzrlib.store import hash_prefix
141
bzr_transport = self.transport.clone('.bzr')
142
bzr_transport.delete('branch-format')
143
for store_name in ["weaves", "revision-store"]:
144
note("adding prefixes to %s" % store_name)
145
store_transport = bzr_transport.clone(store_name)
146
for filename in store_transport.list_dir('.'):
147
if (filename.endswith(".weave") or
148
filename.endswith(".gz") or
149
filename.endswith(".sig")):
150
file_id = os.path.splitext(filename)[0]
153
prefix_dir = hash_prefix(file_id)
154
# FIXME keep track of the dirs made RBC 20060121
156
store_transport.move(filename, prefix_dir + '/' + filename)
157
except NoSuchFile: # catches missing dirs strangely enough
158
store_transport.mkdir(prefix_dir)
159
store_transport.move(filename, prefix_dir + '/' + filename)
160
self.old_format = BzrDirFormat6()
161
self._set_new_format(self.old_format.get_format_string())
162
self.bzrdir = self.old_format.open(self.transport)
163
self.branch = self.bzrdir.open_branch()
77
"""See Converter.convert()."""
78
self.pb.note('starting upgrade from format 4 to 5')
79
if isinstance(self.bzrdir.transport, LocalTransport):
80
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
81
self._convert_to_weaves()
82
return bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
165
84
def _convert_to_weaves(self):
166
note('note: upgrade may be faster if all store files are ungzipped first')
167
bzr_transport = self.transport.clone('.bzr')
85
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
169
87
# TODO permissions
170
stat = bzr_transport.stat('weaves')
88
stat = self.bzrdir.transport.stat('weaves')
171
89
if not S_ISDIR(stat.st_mode):
172
bzr_transport.delete('weaves')
173
bzr_transport.mkdir('weaves')
90
self.bzrdir.transport.delete('weaves')
91
self.bzrdir.transport.mkdir('weaves')
174
92
except NoSuchFile:
175
bzr_transport.mkdir('weaves')
93
self.bzrdir.transport.mkdir('weaves')
176
94
self.inv_weave = Weave('inventory')
177
95
# holds in-memory weaves for all files
178
96
self.text_weaves = {}
179
bzr_transport.delete('branch-format')
97
self.bzrdir.transport.delete('branch-format')
98
self.branch = self.bzrdir.open_branch()
180
99
self._convert_working_inv()
181
100
rev_history = self.branch.revision_history()
182
101
# to_read is a stack holding the revisions we still need to process;
197
116
self._write_all_weaves()
198
117
self._write_all_revs()
199
note('upgraded to weaves:')
200
note(' %6d revisions and inventories' % len(self.revisions))
201
note(' %6d revisions not present' % len(self.absent_revisions))
202
note(' %6d texts' % self.text_count)
118
self.pb.note('upgraded to weaves:')
119
self.pb.note(' %6d revisions and inventories', len(self.revisions))
120
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
121
self.pb.note(' %6d texts', self.text_count)
203
122
self._cleanup_spare_files_after_format4()
204
self.old_format = BzrDirFormat5()
205
self._set_new_format(self.old_format.get_format_string())
206
self.bzrdir = self.old_format.open(self.transport)
207
self.branch = self.bzrdir.open_branch()
209
def _open_branch(self):
210
self.old_format = BzrDirFormat.find_format(self.transport)
211
self.bzrdir = self.old_format.open(self.transport)
212
self.branch = self.bzrdir.open_branch()
213
if isinstance(self.old_format, BzrDirFormat6):
214
note('this branch is in the most current format (%s)', self.old_format)
216
if (not isinstance(self.old_format, BzrDirFormat4) and
217
not isinstance(self.old_format, BzrDirFormat5) and
218
not isinstance(self.old_format, bzrdir.BzrDirMetaFormat1)):
219
raise errors.BzrError("cannot upgrade from branch format %s" %
220
self.branch._branch_format)
223
def _set_new_format(self, format):
224
self.branch.control_files.put_utf8('branch-format', format)
123
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
226
125
def _cleanup_spare_files_after_format4(self):
227
transport = self.transport.clone('.bzr')
228
126
# FIXME working tree upgrade foo.
229
127
for n in 'merged-patches', 'pending-merged-patches':
231
129
## assert os.path.getsize(p) == 0
130
self.bzrdir.transport.delete(n)
233
131
except NoSuchFile:
235
transport.delete_tree('inventory-store')
236
transport.delete_tree('text-store')
238
def _backup_control_dir(self):
239
note('making backup of tree history')
240
self.transport.copy_tree('.bzr', '.bzr.backup')
241
note('%s.bzr has been backed up to %s.bzr.backup',
244
note('if conversion fails, you can move this directory back to .bzr')
245
note('if it succeeds, you can remove this directory if you wish')
133
self.bzrdir.transport.delete_tree('inventory-store')
134
self.bzrdir.transport.delete_tree('text-store')
247
136
def _convert_working_inv(self):
249
inv = serializer_v4.read_inventory(branch.control_files.get('inventory'))
137
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
250
138
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
251
139
# FIXME inventory is a working tree change.
252
branch.control_files.put('inventory', new_inv_xml)
140
self.branch.control_files.put('inventory', new_inv_xml)
254
142
def _write_all_weaves(self):
255
bzr_transport = self.transport.clone('.bzr')
256
controlweaves = WeaveStore(bzr_transport, prefixed=False)
257
weave_transport = bzr_transport.clone('weaves')
143
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
144
weave_transport = self.bzrdir.transport.clone('weaves')
258
145
weaves = WeaveStore(weave_transport, prefixed=False)
259
146
transaction = PassThroughTransaction()
333
class ConvertBzrDir5To6(Converter):
334
"""Converts format 5 bzr dirs to format 6."""
336
def __init__(self, to_convert, pb):
337
"""Create a converter.
339
:param to_convert: The disk object to convert.
340
:param pb: a progress bar to use for progress information.
342
super(ConvertBzrDir5To6, self).__init__(pb)
343
self.bzrdir = to_convert
346
"""See Converter.convert()."""
347
self.pb.note('starting upgrade from format 5 to 6')
348
self._convert_to_prefixed()
349
return bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
351
def _convert_to_prefixed(self):
352
from bzrlib.store import hash_prefix
353
self.bzrdir.transport.delete('branch-format')
354
for store_name in ["weaves", "revision-store"]:
355
note("adding prefixes to %s" % store_name)
356
store_transport = self.bzrdir.transport.clone(store_name)
357
for filename in store_transport.list_dir('.'):
358
if (filename.endswith(".weave") or
359
filename.endswith(".gz") or
360
filename.endswith(".sig")):
361
file_id = os.path.splitext(filename)[0]
364
prefix_dir = hash_prefix(file_id)
365
# FIXME keep track of the dirs made RBC 20060121
367
store_transport.move(filename, prefix_dir + '/' + filename)
368
except NoSuchFile: # catches missing dirs strangely enough
369
store_transport.mkdir(prefix_dir)
370
store_transport.move(filename, prefix_dir + '/' + filename)
371
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
374
class Convert(object):
376
def __init__(self, transport):
377
self.base = transport.base
378
self.transport = transport
379
if self.transport.is_readonly():
380
raise UpgradeReadonly
381
# self.control_files = LockableFiles(transport.clone(bzrlib.BZRDIR), 'branch-lock')
382
# Lock the branch (soon to be meta dir) to prevent anyone racing with us
383
# This is currently windows incompatible, it will deadlock. When the upgrade
384
# logic becomes format specific, then we can have the format know how to pass this
385
# on. Also note that we probably have an 'upgrade meta' which upgrades the constituent
387
# FIXME: control files reuse
388
# self.control_files.lock_write()
392
# self.control_files.unlock()
395
self.old_format = BzrDirFormat.find_format(self.transport)
396
self.bzrdir = self.old_format.open(self.transport)
397
self.branch = self.bzrdir.open_branch()
398
self.pb = ui_factory.progress_bar()
399
if isinstance(self.old_format, BzrDirFormat6):
400
self.pb.note('this branch is in the most current format (%s)', self.old_format)
402
if (not isinstance(self.old_format, BzrDirFormat4) and
403
not isinstance(self.old_format, BzrDirFormat5) and
404
not isinstance(self.old_format, bzrdir.BzrDirMetaFormat1)):
405
raise errors.BzrError("cannot upgrade from branch format %s" %
407
# return self.bzrdir.upgrade(pb)
408
self.pb.note('starting upgrade of %s', self.base)
409
self._backup_control_dir()
410
if isinstance(self.bzrdir._format, BzrDirFormat4):
411
converter = ConvertBzrDir4To5(self.bzrdir, self.pb)
412
self.bzrdir = converter.convert()
413
if isinstance(self.bzrdir._format, BzrDirFormat5):
414
converter = ConvertBzrDir5To6(self.bzrdir, self.pb)
415
self.bzrdir = converter.convert()
416
self.pb.note("finished")
418
def _backup_control_dir(self):
419
note('making backup of tree history')
420
self.transport.copy_tree('.bzr', '.bzr.backup')
421
note('%s.bzr has been backed up to %s.bzr.backup',
424
note('if conversion fails, you can move this directory back to .bzr')
425
note('if it succeeds, you can remove this directory if you wish')
447
427
def upgrade(url):
448
428
t = get_transport(url)