265
266
"""Convert revision and all referenced objects to new format."""
266
267
rev = self.revisions[rev_id]
267
268
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)
269
for parent_id in rev.parent_ids[:]:
270
if parent_id in self.absent_revisions:
271
rev.parent_ids.remove(parent_id)
273
note('remove {%s} as parent of {%s}', parent_id, rev_id)
274
self._convert_revision_contents(rev, inv)
275
self._store_new_weave(rev, inv)
276
self._make_rev_ancestry(rev)
272
277
self.converted_revs.add(rev_id)
275
def _store_new_weave(self, rev, inv, present_parents):
280
def _store_new_weave(self, rev, inv):
276
281
# the XML is now updated with text versions
278
283
for file_id in inv:
282
287
assert hasattr(ie, 'revision'), \
283
288
'no revision on {%s} in {%s}' % \
284
289
(file_id, rev.revision_id)
285
291
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
286
292
new_inv_sha1 = sha_string(new_inv_xml)
287
self.inv_weave.add(rev.revision_id,
293
self.inv_weave.add(rev.revision_id, rev.parent_ids,
289
294
new_inv_xml.splitlines(True),
291
296
rev.inventory_sha1 = new_inv_sha1
293
def _convert_revision_contents(self, rev, inv, present_parents):
299
def _make_rev_ancestry(self, rev):
300
rev_id = rev.revision_id
301
for parent_id in rev.parent_ids:
302
assert parent_id in self.converted_revs
304
lines = list(self.anc_weave.mash_iter(rev.parent_ids))
307
lines.append(rev_id + '\n')
309
parent_ancestries = [self.ancestries[p] for p in rev.parent_ids]
310
new_lines = merge_ancestry_lines(rev_id, parent_ancestries)
311
assert set(lines) == set(new_lines)
312
self.ancestries[rev_id] = new_lines
313
self.anc_weave.add(rev_id, rev.parent_ids, lines)
316
def _convert_revision_contents(self, rev, inv):
294
317
"""Convert all the files within a revision.
296
319
Also upgrade the inventory to refer to the text revision ids."""
297
320
rev_id = rev.revision_id
298
321
mutter('converting texts of revision {%s}',
300
parent_invs = map(self._load_updated_inventory, present_parents)
323
parent_invs = map(self._load_updated_inventory, rev.parent_ids)
301
324
for file_id in inv:
302
325
ie = inv[file_id]
326
self._set_revision(rev, ie, parent_invs)
327
if not ie.has_text():
303
329
self._convert_file_version(rev, ie, parent_invs)
332
def _set_revision(self, rev, ie, parent_invs):
333
"""Set name version for a file.
335
Done in a slightly lazy way: if the file is renamed or in a merge revision
336
it gets a new version, otherwise the same as before.
339
if ie.kind == 'root_directory':
341
if len(parent_invs) != 1:
342
ie.revision = rev.revision_id
344
old_inv = parent_invs[0]
345
if not old_inv.has_id(file_id):
346
ie.revision = rev.revision_id
348
old_ie = old_inv[file_id]
349
if (old_ie.parent_id != ie.parent_id
350
or old_ie.name != ie.name):
351
ie.revision = rev.revision_id
353
ie.revision = old_ie.revision
305
357
def _convert_file_version(self, rev, ie, parent_invs):
306
358
"""Convert one version of one file.
308
360
The file needs to be added into the weave if it is a merge
309
361
of >=2 parents or if it's changed from its parent.
311
if ie.kind == 'root_directory':
313
363
file_id = ie.file_id
314
364
rev_id = rev.revision_id
315
365
w = self.text_weaves.get(file_id)
317
367
w = Weave(file_id)
318
368
self.text_weaves[file_id] = w
319
370
text_changed = False
320
previous_entries = ie.find_previous_heads(parent_invs, w)
321
for old_revision in previous_entries:
371
for parent_inv in parent_invs:
372
if parent_inv.has_id(file_id):
373
parent_ie = parent_inv[file_id]
374
old_revision = parent_ie.revision
322
375
# if this fails, its a ghost ?
323
376
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)
377
if old_revision not in file_parents:
378
file_parents.append(old_revision)
379
if parent_ie.text_sha1 != ie.text_sha1:
381
if len(file_parents) != 1 or text_changed:
343
382
file_lines = self.branch.text_store[ie.text_id].readlines()
344
383
assert sha_strings(file_lines) == ie.text_sha1
345
384
assert sum(map(len, file_lines)) == ie.text_size
346
w.add(rev_id, parent_indexes, file_lines, ie.text_sha1)
385
w.add(rev_id, file_parents, file_lines, ie.text_sha1)
347
387
self.text_count += 1
388
##mutter('import text {%s} of {%s}',
389
## 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)
391
##mutter('text of {%s} unchanged from parent', file_id)
392
ie.revision = file_parents[0]
354
397
def _make_order(self):
355
398
"""Return a suitable order for importing revisions.