~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bundle/serializer/v4.py

First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.

Show diffs side-by-side

added added

removed removed

Lines of Context:
278
278
            revision_ids = set(repository.get_ancestry(target,
279
279
                                                       topo_sorted=False))
280
280
            self.revision_ids = revision_ids.difference(self.base_ancestry)
 
281
        self.revision_keys = set([(revid,) for revid in self.revision_ids])
281
282
 
282
283
    def do_write(self):
283
284
        """Write all data to the bundle"""
284
 
        self.bundle.begin()
285
 
        self.write_info()
286
 
        self.write_files()
287
 
        self.write_revisions()
288
 
        self.bundle.end()
 
285
        self.repository.lock_read()
 
286
        try:
 
287
            self.bundle.begin()
 
288
            self.write_info()
 
289
            self.write_files()
 
290
            self.write_revisions()
 
291
            self.bundle.end()
 
292
        finally:
 
293
            self.repository.unlock()
289
294
        return self.revision_ids
290
295
 
291
296
    def write_info(self):
296
301
        self.bundle.add_info_record(serializer=serializer_format,
297
302
                                    supports_rich_root=supports_rich_root)
298
303
 
299
 
    def iter_file_revisions(self):
300
 
        """Iterate through all relevant revisions of all files.
301
 
 
302
 
        This is the correct implementation, but is not compatible with bzr.dev,
303
 
        because certain old revisions were not converted correctly, and have
304
 
        the wrong "revision" marker in inventories.
305
 
        """
306
 
        transaction = self.repository.get_transaction()
307
 
        altered = self.repository.fileids_altered_by_revision_ids(
308
 
            self.revision_ids)
309
 
        for file_id, file_revision_ids in altered.iteritems():
310
 
            vf = self.repository.weave_store.get_weave(file_id, transaction)
311
 
            yield vf, file_id, file_revision_ids
312
 
 
313
 
    def iter_file_revisions_aggressive(self):
314
 
        """Iterate through all relevant revisions of all files.
315
 
 
316
 
        This uses the standard iter_file_revisions to determine what revisions
317
 
        are referred to by inventories, but then uses the versionedfile to
318
 
        determine what the build-dependencies of each required revision.
319
 
 
320
 
        All build dependencies which are not ancestors of the base revision
321
 
        are emitted.
322
 
        """
323
 
        for vf, file_id, file_revision_ids in self.iter_file_revisions():
324
 
            new_revision_ids = set()
325
 
            pending = list(file_revision_ids)
326
 
            while len(pending) > 0:
327
 
                revision_id = pending.pop()
328
 
                if revision_id in new_revision_ids:
329
 
                    continue
330
 
                if revision_id in self.base_ancestry:
331
 
                    continue
332
 
                new_revision_ids.add(revision_id)
333
 
                pending.extend(vf.get_parent_map([revision_id])[revision_id])
334
 
            yield vf, file_id, new_revision_ids
335
 
 
336
304
    def write_files(self):
337
305
        """Write bundle records for all revisions of all files"""
338
 
        for vf, file_id, revision_ids in self.iter_file_revisions():
339
 
            self.add_mp_records('file', file_id, vf, revision_ids)
 
306
        texts = self.repository.texts
 
307
        text_keys = []
 
308
        for file_id, revision_ids in \
 
309
            self.repository.fileids_altered_by_revision_ids(
 
310
                self.revision_ids).iteritems():
 
311
            revision_ids = list(revision_ids)
 
312
            for revision_id in revision_ids:
 
313
                text_keys.append((file_id, revision_id))
 
314
        self.add_mp_records_keys('file', texts, text_keys)
340
315
 
341
316
    def write_revisions(self):
342
317
        """Write bundle records for all revisions and signatures"""
343
 
        inv_vf = self.repository.get_inventory_weave()
344
 
        revision_order = list(multiparent.topo_iter(inv_vf, self.revision_ids))
 
318
        inv_vf = self.repository.inventories
 
319
        revision_order = [key[-1] for key in multiparent.topo_iter_keys(inv_vf,
 
320
            self.revision_keys)]
345
321
        if self.target is not None and self.target in self.revision_ids:
346
322
            revision_order.remove(self.target)
347
323
            revision_order.append(self.target)
348
 
        self.add_mp_records('inventory', None, inv_vf, revision_order)
 
324
        self.add_mp_records_keys('inventory', inv_vf, [(revid,) for revid in revision_order])
349
325
        parent_map = self.repository.get_parent_map(revision_order)
350
326
        for revision_id in revision_order:
351
327
            parents = parent_map.get(revision_id, None)
374
350
                base = parents[0]
375
351
        return base, target
376
352
 
377
 
    def add_mp_records(self, repo_kind, file_id, vf, revision_ids):
 
353
    def add_mp_records_keys(self, repo_kind, vf, keys):
378
354
        """Add multi-parent diff records to a bundle"""
379
 
        revision_ids = list(multiparent.topo_iter(vf, revision_ids))
380
 
        mpdiffs = vf.make_mpdiffs(revision_ids)
381
 
        sha1s = vf.get_sha1s(revision_ids)
382
 
        parent_map = vf.get_parent_map(revision_ids)
383
 
        for mpdiff, revision_id, sha1, in zip(mpdiffs, revision_ids, sha1s):
384
 
            parents = parent_map[revision_id]
 
355
        ordered_keys = list(multiparent.topo_iter_keys(vf, keys))
 
356
        mpdiffs = vf.make_mpdiffs(ordered_keys)
 
357
        sha1s = vf.get_sha1s(ordered_keys)
 
358
        parent_map = vf.get_parent_map(ordered_keys)
 
359
        for mpdiff, item_key, sha1, in zip(mpdiffs, ordered_keys, sha1s):
 
360
            parents = [key[-1] for key in parent_map[item_key]]
385
361
            text = ''.join(mpdiff.to_patch())
 
362
            # Infer file id records as appropriate.
 
363
            if len(item_key) == 2:
 
364
                file_id = item_key[0]
 
365
            else:
 
366
                file_id = None
386
367
            self.bundle.add_multiparent_record(text, sha1, parents, repo_kind,
387
 
                                               revision_id, file_id)
 
368
                                               item_key[-1], file_id)
388
369
 
389
370
 
390
371
class BundleInfoV4(object):
500
481
                if self._info is not None:
501
482
                    raise AssertionError()
502
483
                self._handle_info(metadata)
503
 
            if (repo_kind, file_id) != ('file', current_file):
504
 
                if len(pending_file_records) > 0:
505
 
                    self._install_mp_records(current_versionedfile,
506
 
                                             pending_file_records)
 
484
            if (pending_file_records and
 
485
                (repo_kind, file_id) != ('file', current_file)):
 
486
                # Flush the data for a single file - prevents memory
 
487
                # spiking due to buffering all files in memory.
 
488
                self._install_mp_records_keys(self._repository.texts,
 
489
                    pending_file_records)
507
490
                current_file = None
508
 
                current_versionedfile = None
509
 
                pending_file_records = []
 
491
                del pending_file_records[:]
510
492
            if len(pending_inventory_records) > 0 and repo_kind != 'inventory':
511
 
                self._install_inventory_records(inventory_vf,
512
 
                                                pending_inventory_records)
 
493
                self._install_inventory_records(pending_inventory_records)
513
494
                pending_inventory_records = []
514
495
            if repo_kind == 'inventory':
515
 
                if inventory_vf is None:
516
 
                    inventory_vf = self._repository.get_inventory_weave()
517
 
                if revision_id not in inventory_vf:
518
 
                    pending_inventory_records.append((revision_id, metadata,
519
 
                                                      bytes))
 
496
                pending_inventory_records.append(((revision_id,), metadata, bytes))
520
497
            if repo_kind == 'revision':
521
498
                target_revision = revision_id
522
499
                self._install_revision(revision_id, metadata, bytes)
524
501
                self._install_signature(revision_id, metadata, bytes)
525
502
            if repo_kind == 'file':
526
503
                current_file = file_id
527
 
                if current_versionedfile is None:
528
 
                    current_versionedfile = \
529
 
                        self._repository.weave_store.get_weave_or_empty(
530
 
                        file_id, self._repository.get_transaction())
531
 
                    pending_file_records = []
532
 
                if revision_id in current_versionedfile:
533
 
                    continue
534
 
                pending_file_records.append((revision_id, metadata, bytes))
535
 
        self._install_mp_records(current_versionedfile, pending_file_records)
 
504
                pending_file_records.append(((file_id, revision_id), metadata, bytes))
 
505
        self._install_mp_records_keys(self._repository.texts, pending_file_records)
536
506
        return target_revision
537
507
 
538
508
    def _handle_info(self, info):
553
523
                      records if r not in versionedfile]
554
524
        versionedfile.add_mpdiffs(vf_records)
555
525
 
556
 
    def _install_inventory_records(self, vf, records):
 
526
    def _install_mp_records_keys(self, versionedfile, records):
 
527
        d_func = multiparent.MultiParent.from_patch
 
528
        vf_records = []
 
529
        for key, meta, text in records:
 
530
            # Adapt to tuple interface:
 
531
            if len(key) == 2:
 
532
                prefix = key[:1]
 
533
            else:
 
534
                prefix = ()
 
535
            parents = [prefix + (parent,) for parent in meta['parents']]
 
536
            vf_records.append((key, parents, meta['sha1'], d_func(text)))
 
537
        versionedfile.add_mpdiffs(vf_records)
 
538
 
 
539
    def _install_inventory_records(self, records):
557
540
        if self._info['serializer'] == self._repository._serializer.format_num:
558
 
            return self._install_mp_records(vf, records)
559
 
        for revision_id, metadata, bytes in records:
 
541
            return self._install_mp_records_keys(self._repository.inventories,
 
542
                records)
 
543
        for key, metadata, bytes in records:
 
544
            revision_id = key[-1]
560
545
            parent_ids = metadata['parents']
561
546
            parents = [self._repository.get_inventory(p)
562
547
                       for p in parent_ids]
579
564
    def _handle_root(self, target_inv, parent_ids):
580
565
        revision_id = target_inv.revision_id
581
566
        if self.update_root:
582
 
            target_inv.root.revision = revision_id
583
 
            store = self._repository.weave_store
584
 
            transaction = self._repository.get_transaction()
585
 
            vf = store.get_weave_or_empty(target_inv.root.file_id, transaction)
586
 
            vf.add_lines(revision_id, parent_ids, [])
 
567
            text_key = (target_inv.root.file_id, revision_id)
 
568
            parent_keys = [(target_inv.root.file_id, parent) for
 
569
                parent in parent_ids]
 
570
            self._repository.texts.add_lines(text_key, parent_keys, [])
587
571
        elif not self._repository.supports_rich_root():
588
572
            if target_inv.root.revision != revision_id:
589
573
                raise errors.IncompatibleRevision(repr(self._repository))
590
574
 
591
 
 
592
575
    def _install_revision(self, revision_id, metadata, text):
593
576
        if self._repository.has_revision(revision_id):
594
577
            return
595
 
        if self._info['serializer'] == self._repository._serializer.format_num:
596
 
            self._repository._add_revision_text(revision_id, text)
597
 
        else:
598
 
            revision = self._source_serializer.read_revision_from_string(text)
599
 
            self._repository.add_revision(revision.revision_id, revision)
 
578
        revision = self._source_serializer.read_revision_from_string(text)
 
579
        self._repository.add_revision(revision.revision_id, revision)
600
580
 
601
581
    def _install_signature(self, revision_id, metadata, text):
602
582
        transaction = self._repository.get_transaction()
603
 
        if self._repository._revision_store.has_signature(revision_id,
604
 
                                                          transaction):
 
583
        if self._repository.has_signature_for_revision_id(revision_id):
605
584
            return
606
 
        self._repository._revision_store.add_revision_signature_text(
607
 
            revision_id, text, transaction)
 
585
        self._repository.add_signature_text(revision_id, text)