265
269
"""Convert revision and all referenced objects to new format."""
266
270
rev = self.revisions[rev_id]
267
271
inv = self._load_old_inventory(rev_id)
268
present_parents = [p for p in rev.parent_ids
269
if p not in self.absent_revisions]
270
self._convert_revision_contents(rev, inv, present_parents)
271
self._store_new_weave(rev, inv, present_parents)
272
for parent_id in rev.parent_ids[:]:
273
if parent_id in self.absent_revisions:
274
rev.parent_ids.remove(parent_id)
276
note('remove {%s} as parent of {%s}', parent_id, rev_id)
277
self._convert_revision_contents(rev, inv)
278
self._store_new_weave(rev, inv)
279
self._make_rev_ancestry(rev)
272
280
self.converted_revs.add(rev_id)
275
def _store_new_weave(self, rev, inv, present_parents):
283
def _store_new_weave(self, rev, inv):
276
284
# the XML is now updated with text versions
278
286
for file_id in inv:
279
287
ie = inv[file_id]
280
288
if ie.kind == 'root_directory':
282
assert hasattr(ie, 'revision'), \
283
'no revision on {%s} in {%s}' % \
290
assert hasattr(ie, 'name_version'), \
291
'no name_version on {%s} in {%s}' % \
284
292
(file_id, rev.revision_id)
293
if ie.kind == 'file':
294
assert hasattr(ie, 'text_version')
285
296
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
286
297
new_inv_sha1 = sha_string(new_inv_xml)
287
self.inv_weave.add(rev.revision_id,
298
self.inv_weave.add(rev.revision_id, rev.parent_ids,
289
299
new_inv_xml.splitlines(True),
291
301
rev.inventory_sha1 = new_inv_sha1
293
def _convert_revision_contents(self, rev, inv, present_parents):
304
def _make_rev_ancestry(self, rev):
305
rev_id = rev.revision_id
306
for parent_id in rev.parent_ids:
307
assert parent_id in self.converted_revs
309
lines = list(self.anc_weave.mash_iter(rev.parent_ids))
312
lines.append(rev_id + '\n')
314
parent_ancestries = [self.ancestries[p] for p in rev.parent_ids]
315
new_lines = merge_ancestry_lines(rev_id, parent_ancestries)
316
assert set(lines) == set(new_lines)
317
self.ancestries[rev_id] = new_lines
318
self.anc_weave.add(rev_id, rev.parent_ids, lines)
321
def _convert_revision_contents(self, rev, inv):
294
322
"""Convert all the files within a revision.
296
324
Also upgrade the inventory to refer to the text revision ids."""
297
325
rev_id = rev.revision_id
298
326
mutter('converting texts of revision {%s}',
300
parent_invs = map(self._load_updated_inventory, present_parents)
328
parent_invs = map(self._load_updated_inventory, rev.parent_ids)
301
329
for file_id in inv:
302
330
ie = inv[file_id]
331
self._set_name_version(rev, ie, parent_invs)
332
if ie.kind != 'file':
303
334
self._convert_file_version(rev, ie, parent_invs)
337
def _set_name_version(self, rev, ie, parent_invs):
338
"""Set name version for a file.
340
Done in a slightly lazy way: if the file is renamed or in a merge revision
341
it gets a new version, otherwise the same as before.
344
if ie.kind == 'root_directory':
346
if len(parent_invs) != 1:
347
ie.name_version = rev.revision_id
349
old_inv = parent_invs[0]
350
if not old_inv.has_id(file_id):
351
ie.name_version = rev.revision_id
353
old_ie = old_inv[file_id]
354
if (old_ie.parent_id != ie.parent_id
355
or old_ie.name != ie.name):
356
ie.name_version = rev.revision_id
358
ie.name_version = old_ie.name_version
305
362
def _convert_file_version(self, rev, ie, parent_invs):
306
363
"""Convert one version of one file.
308
365
The file needs to be added into the weave if it is a merge
309
366
of >=2 parents or if it's changed from its parent.
311
if ie.kind == 'root_directory':
313
368
file_id = ie.file_id
314
369
rev_id = rev.revision_id
315
370
w = self.text_weaves.get(file_id)
317
372
w = Weave(file_id)
318
373
self.text_weaves[file_id] = w
319
375
text_changed = False
320
previous_entries = ie.find_previous_heads(parent_invs, w)
321
for old_revision in previous_entries:
322
# if this fails, its a ghost ?
323
assert old_revision in self.converted_revs
324
self.snapshot_ie(previous_entries, ie, w, rev_id)
326
assert getattr(ie, 'revision', None) is not None
328
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
329
# TODO: convert this logic, which is ~= snapshot to
330
# a call to:. This needs the path figured out. rather than a work_tree
331
# a v4 revision_tree can be given, or something that looks enough like
332
# one to give the file content to the entry if it needs it.
333
# and we need something that looks like a weave store for snapshot to
335
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
336
if len(previous_revisions) == 1:
337
previous_ie = previous_revisions.values()[0]
338
if ie._unchanged(previous_ie):
339
ie.revision = previous_ie.revision
341
parent_indexes = map(w.lookup, previous_revisions)
376
for parent_inv in parent_invs:
377
if parent_inv.has_id(file_id):
378
parent_ie = parent_inv[file_id]
379
old_text_version = parent_ie.text_version
380
assert old_text_version in self.converted_revs
381
if old_text_version not in file_parents:
382
file_parents.append(old_text_version)
383
if parent_ie.text_sha1 != ie.text_sha1:
385
if len(file_parents) != 1 or text_changed:
343
386
file_lines = self.branch.text_store[ie.text_id].readlines()
344
387
assert sha_strings(file_lines) == ie.text_sha1
345
388
assert sum(map(len, file_lines)) == ie.text_size
346
w.add(rev_id, parent_indexes, file_lines, ie.text_sha1)
389
w.add(rev_id, file_parents, file_lines, ie.text_sha1)
390
ie.text_version = rev_id
347
391
self.text_count += 1
392
##mutter('import text {%s} of {%s}',
393
## ie.text_id, file_id)
349
w.add(rev_id, parent_indexes, [], None)
351
##mutter('import text {%s} of {%s}',
352
## ie.text_id, file_id)
395
##mutter('text of {%s} unchanged from parent', file_id)
396
ie.text_version = file_parents[0]
354
401
def _make_order(self):
355
402
"""Return a suitable order for importing revisions.