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)
205
199
:return: a generator of (bytes, metadata, content_kind, revision_id,
208
iterator = pack.iter_records_from_file(self._container_file)
209
for names, bytes in iterator:
202
iterator = self._container.iter_records()
203
for names, meta_bytes in iterator:
210
204
if len(names) != 1:
211
205
raise errors.BadBundle('Record has %d names instead of 1'
213
metadata = bencode.bdecode(bytes)
207
metadata = bencode.bdecode(meta_bytes(None))
214
208
if metadata['storage_kind'] == 'header':
217
211
_unused, bytes = iterator.next()
218
213
yield (bytes, metadata) + self.decode_name(names[0][0])
301
291
self.bundle.add_info_record(serializer=serializer_format,
302
292
supports_rich_root=supports_rich_root)
294
def iter_file_revisions(self):
295
"""Iterate through all relevant revisions of all files.
297
This is the correct implementation, but is not compatible with bzr.dev,
298
because certain old revisions were not converted correctly, and have
299
the wrong "revision" marker in inventories.
301
transaction = self.repository.get_transaction()
302
altered = self.repository.fileids_altered_by_revision_ids(
304
for file_id, file_revision_ids in altered.iteritems():
305
vf = self.repository.weave_store.get_weave(file_id, transaction)
306
yield vf, file_id, file_revision_ids
308
def iter_file_revisions_aggressive(self):
309
"""Iterate through all relevant revisions of all files.
311
This uses the standard iter_file_revisions to determine what revisions
312
are referred to by inventories, but then uses the versionedfile to
313
determine what the build-dependencies of each required revision.
315
All build dependencies which are not ancestors of the base revision
318
for vf, file_id, file_revision_ids in self.iter_file_revisions():
319
new_revision_ids = set()
320
pending = list(file_revision_ids)
321
while len(pending) > 0:
322
revision_id = pending.pop()
323
if revision_id in new_revision_ids:
325
if revision_id in self.base_ancestry:
327
new_revision_ids.add(revision_id)
328
pending.extend(vf.get_parents(revision_id))
329
yield vf, file_id, new_revision_ids
304
331
def write_files(self):
305
332
"""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)
333
for vf, file_id, revision_ids in self.iter_file_revisions():
334
self.add_mp_records('file', file_id, vf, revision_ids)
314
336
def write_revisions(self):
315
337
"""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,
338
inv_vf = self.repository.get_inventory_weave()
339
revision_order = list(multiparent.topo_iter(inv_vf, self.revision_ids))
319
340
if self.target is not None and self.target in self.revision_ids:
320
341
revision_order.remove(self.target)
321
342
revision_order.append(self.target)
322
self._add_mp_records_keys('inventory', inv_vf, [(revid,) for revid in revision_order])
323
parent_map = self.repository.get_parent_map(revision_order)
324
for revision_id in revision_order:
325
parents = parent_map.get(revision_id, None)
343
self.add_mp_records('inventory', None, inv_vf, revision_order)
344
parents_list = self.repository.get_parents(revision_order)
345
for parents, revision_id in zip(parents_list, revision_order):
326
346
revision_text = self.repository.get_revision_xml(revision_id)
327
347
self.bundle.add_fulltext_record(revision_text, parents,
328
348
'revision', revision_id)
348
368
base = parents[0]
349
369
return base, target
351
def _add_mp_records_keys(self, repo_kind, vf, keys):
371
def add_mp_records(self, repo_kind, file_id, vf, revision_ids):
352
372
"""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]]
373
revision_ids = list(multiparent.topo_iter(vf, revision_ids))
374
mpdiffs = vf.make_mpdiffs(revision_ids)
375
sha1s = vf.get_sha1s(revision_ids)
376
for mpdiff, revision_id, sha1, in zip(mpdiffs, revision_ids, sha1s):
377
parents = vf.get_parents(revision_id)
360
378
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
379
self.bundle.add_multiparent_record(text, sha1, parents, repo_kind,
367
item_key[-1], file_id)
380
revision_id, file_id)
370
383
class BundleInfoV4(object):
477
490
for bytes, metadata, repo_kind, revision_id, file_id in\
478
491
self._container.iter_records():
479
492
if repo_kind == 'info':
480
if self._info is not None:
481
raise AssertionError()
493
assert self._info is None
482
494
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)
495
if (repo_kind, file_id) != ('file', current_file):
496
if len(pending_file_records) > 0:
497
self._install_mp_records(current_versionedfile,
498
pending_file_records)
489
499
current_file = None
490
del pending_file_records[:]
500
current_versionedfile = None
501
pending_file_records = []
491
502
if len(pending_inventory_records) > 0 and repo_kind != 'inventory':
492
self._install_inventory_records(pending_inventory_records)
503
self._install_inventory_records(inventory_vf,
504
pending_inventory_records)
493
505
pending_inventory_records = []
494
506
if repo_kind == 'inventory':
495
pending_inventory_records.append(((revision_id,), metadata, bytes))
507
if inventory_vf is None:
508
inventory_vf = self._repository.get_inventory_weave()
509
if revision_id not in inventory_vf:
510
pending_inventory_records.append((revision_id, metadata,
496
512
if repo_kind == 'revision':
497
513
target_revision = revision_id
498
514
self._install_revision(revision_id, metadata, bytes)
500
516
self._install_signature(revision_id, metadata, bytes)
501
517
if repo_kind == 'file':
502
518
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)
519
if current_versionedfile is None:
520
current_versionedfile = \
521
self._repository.weave_store.get_weave_or_empty(
522
file_id, self._repository.get_transaction())
523
pending_file_records = []
524
if revision_id in current_versionedfile:
526
pending_file_records.append((revision_id, metadata, bytes))
527
self._install_mp_records(current_versionedfile, pending_file_records)
505
528
return target_revision
507
530
def _handle_info(self, info):
522
545
records if r not in versionedfile]
523
546
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):
548
def _install_inventory_records(self, vf, records):
543
549
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]
550
return self._install_mp_records(vf, records)
551
for revision_id, metadata, bytes in records:
548
552
parent_ids = metadata['parents']
549
553
parents = [self._repository.get_inventory(p)
550
554
for p in parent_ids]
567
571
def _handle_root(self, target_inv, parent_ids):
568
572
revision_id = target_inv.revision_id
569
573
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, [])
574
target_inv.root.revision = revision_id
575
store = self._repository.weave_store
576
transaction = self._repository.get_transaction()
577
vf = store.get_weave_or_empty(target_inv.root.file_id, transaction)
578
vf.add_lines(revision_id, parent_ids, [])
574
579
elif not self._repository.supports_rich_root():
575
580
if target_inv.root.revision != revision_id:
576
581
raise errors.IncompatibleRevision(repr(self._repository))
578
584
def _install_revision(self, revision_id, metadata, text):
579
585
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)
587
self._repository._add_revision_text(revision_id, text)
584
589
def _install_signature(self, revision_id, metadata, text):
585
590
transaction = self._repository.get_transaction()
586
if self._repository.has_signature_for_revision_id(revision_id):
591
if self._repository._revision_store.has_signature(revision_id,
588
self._repository.add_signature_text(revision_id, text)
594
self._repository._revision_store.add_revision_signature_text(
595
revision_id, text, transaction)