108
108
def encode_name(content_kind, revision_id, file_id=None):
109
109
"""Encode semantic ids as a container name"""
110
if content_kind not in ('revision', 'file', 'inventory', 'signature',
112
raise ValueError(content_kind)
110
assert content_kind in ('revision', 'file', 'inventory', 'signature',
113
113
if content_kind == 'file':
115
raise AssertionError()
114
assert file_id is not None
117
if file_id is not None:
118
raise AssertionError()
116
assert file_id is None
119
117
if content_kind == 'info':
120
if revision_id is not None:
121
raise AssertionError()
122
elif revision_id is None:
123
raise AssertionError()
118
assert revision_id is None
120
assert revision_id is not None
124
121
names = [n.replace('/', '//') for n in
125
122
(content_kind, revision_id, file_id) if n is not None]
126
123
return '/'.join(names)
301
293
self.bundle.add_info_record(serializer=serializer_format,
302
294
supports_rich_root=supports_rich_root)
296
def iter_file_revisions(self):
297
"""Iterate through all relevant revisions of all files.
299
This is the correct implementation, but is not compatible with bzr.dev,
300
because certain old revisions were not converted correctly, and have
301
the wrong "revision" marker in inventories.
303
transaction = self.repository.get_transaction()
304
altered = self.repository.fileids_altered_by_revision_ids(
306
for file_id, file_revision_ids in altered.iteritems():
307
vf = self.repository.weave_store.get_weave(file_id, transaction)
308
yield vf, file_id, file_revision_ids
310
def iter_file_revisions_aggressive(self):
311
"""Iterate through all relevant revisions of all files.
313
This uses the standard iter_file_revisions to determine what revisions
314
are referred to by inventories, but then uses the versionedfile to
315
determine what the build-dependencies of each required revision.
317
All build dependencies which are not ancestors of the base revision
320
for vf, file_id, file_revision_ids in self.iter_file_revisions():
321
new_revision_ids = set()
322
pending = list(file_revision_ids)
323
while len(pending) > 0:
324
revision_id = pending.pop()
325
if revision_id in new_revision_ids:
327
if revision_id in self.base_ancestry:
329
new_revision_ids.add(revision_id)
330
pending.extend(vf.get_parents(revision_id))
331
yield vf, file_id, new_revision_ids
304
333
def write_files(self):
305
334
"""Write bundle records for all revisions of all files"""
307
altered_fileids = self.repository.fileids_altered_by_revision_ids(
309
for file_id, revision_ids in altered_fileids.iteritems():
310
for revision_id in revision_ids:
311
text_keys.append((file_id, revision_id))
312
self._add_mp_records_keys('file', self.repository.texts, text_keys)
335
for vf, file_id, revision_ids in self.iter_file_revisions():
336
self.add_mp_records('file', file_id, vf, revision_ids)
314
338
def write_revisions(self):
315
339
"""Write bundle records for all revisions and signatures"""
316
inv_vf = self.repository.inventories
317
revision_order = [key[-1] for key in multiparent.topo_iter_keys(inv_vf,
340
inv_vf = self.repository.get_inventory_weave()
341
revision_order = list(multiparent.topo_iter(inv_vf, self.revision_ids))
319
342
if self.target is not None and self.target in self.revision_ids:
320
343
revision_order.remove(self.target)
321
344
revision_order.append(self.target)
322
self._add_mp_records_keys('inventory', inv_vf, [(revid,) for revid in revision_order])
345
self.add_mp_records('inventory', None, inv_vf, revision_order)
323
346
parent_map = self.repository.get_parent_map(revision_order)
324
347
for revision_id in revision_order:
325
348
parents = parent_map.get(revision_id, None)
348
371
base = parents[0]
349
372
return base, target
351
def _add_mp_records_keys(self, repo_kind, vf, keys):
374
def add_mp_records(self, repo_kind, file_id, vf, revision_ids):
352
375
"""Add multi-parent diff records to a bundle"""
353
ordered_keys = list(multiparent.topo_iter_keys(vf, keys))
354
mpdiffs = vf.make_mpdiffs(ordered_keys)
355
sha1s = vf.get_sha1s(ordered_keys)
356
parent_map = vf.get_parent_map(ordered_keys)
357
for mpdiff, item_key, in zip(mpdiffs, ordered_keys):
358
sha1 = sha1s[item_key]
359
parents = [key[-1] for key in parent_map[item_key]]
376
revision_ids = list(multiparent.topo_iter(vf, revision_ids))
377
mpdiffs = vf.make_mpdiffs(revision_ids)
378
sha1s = vf.get_sha1s(revision_ids)
379
for mpdiff, revision_id, sha1, in zip(mpdiffs, revision_ids, sha1s):
380
parents = vf.get_parents(revision_id)
360
381
text = ''.join(mpdiff.to_patch())
361
# Infer file id records as appropriate.
362
if len(item_key) == 2:
363
file_id = item_key[0]
366
382
self.bundle.add_multiparent_record(text, sha1, parents, repo_kind,
367
item_key[-1], file_id)
383
revision_id, file_id)
370
386
class BundleInfoV4(object):
477
493
for bytes, metadata, repo_kind, revision_id, file_id in\
478
494
self._container.iter_records():
479
495
if repo_kind == 'info':
480
if self._info is not None:
481
raise AssertionError()
496
assert self._info is None
482
497
self._handle_info(metadata)
483
if (pending_file_records and
484
(repo_kind, file_id) != ('file', current_file)):
485
# Flush the data for a single file - prevents memory
486
# spiking due to buffering all files in memory.
487
self._install_mp_records_keys(self._repository.texts,
488
pending_file_records)
498
if (repo_kind, file_id) != ('file', current_file):
499
if len(pending_file_records) > 0:
500
self._install_mp_records(current_versionedfile,
501
pending_file_records)
489
502
current_file = None
490
del pending_file_records[:]
503
current_versionedfile = None
504
pending_file_records = []
491
505
if len(pending_inventory_records) > 0 and repo_kind != 'inventory':
492
self._install_inventory_records(pending_inventory_records)
506
self._install_inventory_records(inventory_vf,
507
pending_inventory_records)
493
508
pending_inventory_records = []
494
509
if repo_kind == 'inventory':
495
pending_inventory_records.append(((revision_id,), metadata, bytes))
510
if inventory_vf is None:
511
inventory_vf = self._repository.get_inventory_weave()
512
if revision_id not in inventory_vf:
513
pending_inventory_records.append((revision_id, metadata,
496
515
if repo_kind == 'revision':
497
516
target_revision = revision_id
498
517
self._install_revision(revision_id, metadata, bytes)
500
519
self._install_signature(revision_id, metadata, bytes)
501
520
if repo_kind == 'file':
502
521
current_file = file_id
503
pending_file_records.append(((file_id, revision_id), metadata, bytes))
504
self._install_mp_records_keys(self._repository.texts, pending_file_records)
522
if current_versionedfile is None:
523
current_versionedfile = \
524
self._repository.weave_store.get_weave_or_empty(
525
file_id, self._repository.get_transaction())
526
pending_file_records = []
527
if revision_id in current_versionedfile:
529
pending_file_records.append((revision_id, metadata, bytes))
530
self._install_mp_records(current_versionedfile, pending_file_records)
505
531
return target_revision
507
533
def _handle_info(self, info):
522
548
records if r not in versionedfile]
523
549
versionedfile.add_mpdiffs(vf_records)
525
def _install_mp_records_keys(self, versionedfile, records):
526
d_func = multiparent.MultiParent.from_patch
528
for key, meta, text in records:
529
# Adapt to tuple interface: A length two key is a file_id,
530
# revision_id pair, a length 1 key is a
531
# revision/signature/inventory. We need to do this because
532
# the metadata extraction from the bundle has not yet been updated
533
# to use the consistent tuple interface itself.
538
parents = [prefix + (parent,) for parent in meta['parents']]
539
vf_records.append((key, parents, meta['sha1'], d_func(text)))
540
versionedfile.add_mpdiffs(vf_records)
542
def _install_inventory_records(self, records):
551
def _install_inventory_records(self, vf, records):
543
552
if self._info['serializer'] == self._repository._serializer.format_num:
544
return self._install_mp_records_keys(self._repository.inventories,
546
for key, metadata, bytes in records:
547
revision_id = key[-1]
553
return self._install_mp_records(vf, records)
554
for revision_id, metadata, bytes in records:
548
555
parent_ids = metadata['parents']
549
556
parents = [self._repository.get_inventory(p)
550
557
for p in parent_ids]
567
574
def _handle_root(self, target_inv, parent_ids):
568
575
revision_id = target_inv.revision_id
569
576
if self.update_root:
570
text_key = (target_inv.root.file_id, revision_id)
571
parent_keys = [(target_inv.root.file_id, parent) for
572
parent in parent_ids]
573
self._repository.texts.add_lines(text_key, parent_keys, [])
577
target_inv.root.revision = revision_id
578
store = self._repository.weave_store
579
transaction = self._repository.get_transaction()
580
vf = store.get_weave_or_empty(target_inv.root.file_id, transaction)
581
vf.add_lines(revision_id, parent_ids, [])
574
582
elif not self._repository.supports_rich_root():
575
583
if target_inv.root.revision != revision_id:
576
584
raise errors.IncompatibleRevision(repr(self._repository))
578
587
def _install_revision(self, revision_id, metadata, text):
579
588
if self._repository.has_revision(revision_id):
581
revision = self._source_serializer.read_revision_from_string(text)
582
self._repository.add_revision(revision.revision_id, revision)
590
self._repository._add_revision_text(revision_id, text)
584
592
def _install_signature(self, revision_id, metadata, text):
585
593
transaction = self._repository.get_transaction()
586
if self._repository.has_signature_for_revision_id(revision_id):
594
if self._repository._revision_store.has_signature(revision_id,
588
self._repository.add_signature_text(revision_id, text)
597
self._repository._revision_store.add_revision_signature_text(
598
revision_id, text, transaction)