65
68
# the moment saves us having to worry about when files need new
71
from cStringIO import StringIO
74
from bzrlib.branch import Branch, find_branch
75
from bzrlib.branch import BZR_BRANCH_FORMAT_5, BZR_BRANCH_FORMAT_6
78
from bzrlib.branch import Branch
79
import bzrlib.bzrdir as bzrdir
80
from bzrlib.bzrdir import BzrDirFormat, BzrDirFormat4, BzrDirFormat5, BzrDirFormat6
81
import bzrlib.errors as errors
82
from bzrlib.errors import NoSuchFile, UpgradeReadonly
76
83
import bzrlib.hashcache as hashcache
84
from bzrlib.lockable_files import LockableFiles
85
from bzrlib.osutils import sha_strings, sha_string, pathjoin, abspath
86
from bzrlib.ui import ui_factory
87
from bzrlib.store.text import TextStore
88
from bzrlib.store.weave import WeaveStore
89
from bzrlib.trace import mutter, note, warning
90
from bzrlib.transactions import PassThroughTransaction
91
from bzrlib.transport import get_transport
92
from bzrlib.transport.local import LocalTransport
77
93
from bzrlib.weave import Weave
78
94
from bzrlib.weavefile import read_weave, write_weave
79
from bzrlib.ui import ui_factory
80
from bzrlib.atomicfile import AtomicFile
81
95
from bzrlib.xml4 import serializer_v4
82
96
from bzrlib.xml5 import serializer_v5
83
from bzrlib.trace import mutter, note, warning
84
from bzrlib.osutils import sha_strings, sha_string, pathjoin, abspath
87
99
class Convert(object):
88
def __init__(self, base_dir):
101
def __init__(self, transport):
102
self.base = transport.base
90
103
self.converted_revs = set()
91
104
self.absent_revisions = set()
92
105
self.text_count = 0
93
106
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
print "FIXME: control files reuse"
117
self.control_files.lock_write()
121
self.control_files.unlock()
97
123
def convert(self):
98
124
if not self._open_branch():
100
note('starting upgrade of %s', os.path.abspath(self.base))
126
note('starting upgrade of %s', self.base)
101
127
self._backup_control_dir()
102
128
self.pb = ui_factory.progress_bar()
103
if self.old_format == 4:
129
if isinstance(self.old_format, BzrDirFormat4):
104
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')
105
133
self._convert_to_weaves()
107
if self.old_format == 5:
134
if isinstance(self.old_format, BzrDirFormat5):
108
135
note('starting upgrade from format 5 to 6')
109
136
self._convert_to_prefixed()
111
cache = hashcache.HashCache(abspath(self.base))
117
139
def _convert_to_prefixed(self):
118
140
from bzrlib.store import hash_prefix
141
bzr_transport = self.transport.clone('.bzr')
142
bzr_transport.delete('branch-format')
119
143
for store_name in ["weaves", "revision-store"]:
120
144
note("adding prefixes to %s" % store_name)
121
store_dir = pathjoin(self.base, ".bzr", store_name)
122
for filename in os.listdir(store_dir):
123
if filename.endswith(".weave") or filename.endswith(".gz"):
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")):
124
150
file_id = os.path.splitext(filename)[0]
126
152
file_id = filename
127
prefix_dir = pathjoin(store_dir, hash_prefix(file_id))
128
if not os.path.isdir(prefix_dir):
130
os.rename(pathjoin(store_dir, filename),
131
pathjoin(prefix_dir, filename))
132
self._set_new_format(BZR_BRANCH_FORMAT_6)
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()
135
165
def _convert_to_weaves(self):
136
166
note('note: upgrade may be faster if all store files are ungzipped first')
137
if not os.path.isdir(self.base + '/.bzr/weaves'):
138
os.mkdir(self.base + '/.bzr/weaves')
167
bzr_transport = self.transport.clone('.bzr')
170
stat = bzr_transport.stat('weaves')
171
if not S_ISDIR(stat.st_mode):
172
bzr_transport.delete('weaves')
173
bzr_transport.mkdir('weaves')
175
bzr_transport.mkdir('weaves')
139
176
self.inv_weave = Weave('inventory')
140
177
# holds in-memory weaves for all files
141
178
self.text_weaves = {}
142
os.remove(self.branch.controlfilename('branch-format'))
179
bzr_transport.delete('branch-format')
143
180
self._convert_working_inv()
144
181
rev_history = self.branch.revision_history()
145
182
# to_read is a stack holding the revisions we still need to process;
157
194
self.pb.update('converting revision', i, len(to_import))
158
195
self._convert_one_rev(rev_id)
197
self._write_all_weaves()
198
self._write_all_revs()
160
199
note('upgraded to weaves:')
161
200
note(' %6d revisions and inventories' % len(self.revisions))
162
201
note(' %6d revisions not present' % len(self.absent_revisions))
163
202
note(' %6d texts' % self.text_count)
164
self._write_all_weaves()
165
self._write_all_revs()
166
self._cleanup_spare_files()
167
self._set_new_format(BZR_BRANCH_FORMAT_5)
203
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()
170
209
def _open_branch(self):
171
self.branch = Branch.open_downlevel(self.base)
172
self.old_format = self.branch._branch_format
173
if self.old_format == 6:
174
note('this branch is in the most current format')
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)
176
if self.old_format not in (4, 5):
177
raise BzrError("cannot upgrade from branch format %r" %
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" %
178
220
self.branch._branch_format)
182
223
def _set_new_format(self, format):
183
self.branch.put_controlfile('branch-format', format)
186
def _cleanup_spare_files(self):
224
self.branch.control_files.put_utf8('branch-format', format)
226
def _cleanup_spare_files_after_format4(self):
227
transport = self.transport.clone('.bzr')
228
print "FIXME working tree upgrade foo."
187
229
for n in 'merged-patches', 'pending-merged-patches':
188
p = self.branch.controlfilename(n)
189
if not os.path.exists(p):
191
## assert os.path.getsize(p) == 0
193
shutil.rmtree(self.base + '/.bzr/inventory-store')
194
shutil.rmtree(self.base + '/.bzr/text-store')
231
## assert os.path.getsize(p) == 0
235
transport.delete_tree('inventory-store')
236
transport.delete_tree('text-store')
197
238
def _backup_control_dir(self):
198
orig = self.base + '/.bzr'
199
backup = orig + '.backup'
200
239
note('making backup of tree history')
201
shutil.copytree(orig, backup)
202
note('%s has been backed up to %s', orig, backup)
240
self.transport.copy_tree('.bzr', '.bzr.backup')
241
note('%s.bzr has been backed up to %s.bzr.backup',
203
244
note('if conversion fails, you can move this directory back to .bzr')
204
245
note('if it succeeds, you can remove this directory if you wish')
207
247
def _convert_working_inv(self):
208
248
branch = self.branch
209
inv = serializer_v4.read_inventory(branch.controlfile('inventory', 'rb'))
249
inv = serializer_v4.read_inventory(branch.control_files.get('inventory'))
210
250
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
211
branch.put_controlfile('inventory', new_inv_xml)
251
print "fixme inventory is a working tree change."
252
branch.control_files.put('inventory', new_inv_xml)
215
254
def _write_all_weaves(self):
216
write_a_weave(self.inv_weave, self.base + '/.bzr/inventory.weave')
255
bzr_transport = self.transport.clone('.bzr')
256
controlweaves = WeaveStore(bzr_transport, prefixed=False)
257
weave_transport = bzr_transport.clone('weaves')
258
weaves = WeaveStore(weave_transport, prefixed=False)
259
transaction = PassThroughTransaction()
261
controlweaves.put_weave('inventory', self.inv_weave, transaction)
219
264
for file_id, file_weave in self.text_weaves.items():
220
265
self.pb.update('writing weave', i, len(self.text_weaves))
221
write_a_weave(file_weave, self.base + '/.bzr/weaves/%s.weave' % file_id)
266
weaves.put_weave(file_id, file_weave, transaction)
227
271
def _write_all_revs(self):
228
272
"""Write all revisions out in new form."""
229
shutil.rmtree(self.base + '/.bzr/revision-store')
230
os.mkdir(self.base + '/.bzr/revision-store')
273
transport = self.transport.clone('.bzr')
274
transport.delete_tree('revision-store')
275
transport.mkdir('revision-store')
276
revision_transport = transport.clone('revision-store')
278
revision_store = TextStore(revision_transport,
232
282
for i, rev_id in enumerate(self.converted_revs):
233
283
self.pb.update('write revision', i, len(self.converted_revs))
234
f = file(self.base + '/.bzr/revision-store/%s' % rev_id, 'wb')
236
serializer_v5.write_revision(self.revisions[rev_id], f)
285
serializer_v5.write_revision(self.revisions[rev_id], rev_tmp)
287
revision_store.add(rev_tmp, rev_id)