~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

Merge thread.

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
        text_keys = []
 
307
        altered_fileids = self.repository.fileids_altered_by_revision_ids(
 
308
                self.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)
340
313
 
341
314
    def write_revisions(self):
342
315
        """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))
 
316
        inv_vf = self.repository.inventories
 
317
        revision_order = [key[-1] for key in multiparent.topo_iter_keys(inv_vf,
 
318
            self.revision_keys)]
345
319
        if self.target is not None and self.target in self.revision_ids:
346
320
            revision_order.remove(self.target)
347
321
            revision_order.append(self.target)
348
 
        self.add_mp_records('inventory', None, inv_vf, revision_order)
 
322
        self._add_mp_records_keys('inventory', inv_vf, [(revid,) for revid in revision_order])
349
323
        parent_map = self.repository.get_parent_map(revision_order)
350
324
        for revision_id in revision_order:
351
325
            parents = parent_map.get(revision_id, None)
374
348
                base = parents[0]
375
349
        return base, target
376
350
 
377
 
    def add_mp_records(self, repo_kind, file_id, vf, revision_ids):
 
351
    def _add_mp_records_keys(self, repo_kind, vf, keys):
378
352
        """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]
 
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, sha1, in zip(mpdiffs, ordered_keys, sha1s):
 
358
            parents = [key[-1] for key in parent_map[item_key]]
385
359
            text = ''.join(mpdiff.to_patch())
 
360
            # Infer file id records as appropriate.
 
361
            if len(item_key) == 2:
 
362
                file_id = item_key[0]
 
363
            else:
 
364
                file_id = None
386
365
            self.bundle.add_multiparent_record(text, sha1, parents, repo_kind,
387
 
                                               revision_id, file_id)
 
366
                                               item_key[-1], file_id)
388
367
 
389
368
 
390
369
class BundleInfoV4(object):
500
479
                if self._info is not None:
501
480
                    raise AssertionError()
502
481
                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)
 
482
            if (pending_file_records and
 
483
                (repo_kind, file_id) != ('file', current_file)):
 
484
                # Flush the data for a single file - prevents memory
 
485
                # spiking due to buffering all files in memory.
 
486
                self._install_mp_records_keys(self._repository.texts,
 
487
                    pending_file_records)
507
488
                current_file = None
508
 
                current_versionedfile = None
509
 
                pending_file_records = []
 
489
                del pending_file_records[:]
510
490
            if len(pending_inventory_records) > 0 and repo_kind != 'inventory':
511
 
                self._install_inventory_records(inventory_vf,
512
 
                                                pending_inventory_records)
 
491
                self._install_inventory_records(pending_inventory_records)
513
492
                pending_inventory_records = []
514
493
            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))
 
494
                pending_inventory_records.append(((revision_id,), metadata, bytes))
520
495
            if repo_kind == 'revision':
521
496
                target_revision = revision_id
522
497
                self._install_revision(revision_id, metadata, bytes)
524
499
                self._install_signature(revision_id, metadata, bytes)
525
500
            if repo_kind == 'file':
526
501
                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)
 
502
                pending_file_records.append(((file_id, revision_id), metadata, bytes))
 
503
        self._install_mp_records_keys(self._repository.texts, pending_file_records)
536
504
        return target_revision
537
505
 
538
506
    def _handle_info(self, info):
553
521
                      records if r not in versionedfile]
554
522
        versionedfile.add_mpdiffs(vf_records)
555
523
 
556
 
    def _install_inventory_records(self, vf, records):
 
524
    def _install_mp_records_keys(self, versionedfile, records):
 
525
        d_func = multiparent.MultiParent.from_patch
 
526
        vf_records = []
 
527
        for key, meta, text in records:
 
528
            # Adapt to tuple interface: A length two key is a file_id,
 
529
            # revision_id pair, a length 1 key is a
 
530
            # revision/signature/inventory. We need to do this because
 
531
            # the metadata extraction from the bundle has not yet been updated
 
532
            # to use the consistent tuple interface itself.
 
533
            if len(key) == 2:
 
534
                prefix = key[:1]
 
535
            else:
 
536
                prefix = ()
 
537
            parents = [prefix + (parent,) for parent in meta['parents']]
 
538
            vf_records.append((key, parents, meta['sha1'], d_func(text)))
 
539
        versionedfile.add_mpdiffs(vf_records)
 
540
 
 
541
    def _install_inventory_records(self, records):
557
542
        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:
 
543
            return self._install_mp_records_keys(self._repository.inventories,
 
544
                records)
 
545
        for key, metadata, bytes in records:
 
546
            revision_id = key[-1]
560
547
            parent_ids = metadata['parents']
561
548
            parents = [self._repository.get_inventory(p)
562
549
                       for p in parent_ids]
579
566
    def _handle_root(self, target_inv, parent_ids):
580
567
        revision_id = target_inv.revision_id
581
568
        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, [])
 
569
            text_key = (target_inv.root.file_id, revision_id)
 
570
            parent_keys = [(target_inv.root.file_id, parent) for
 
571
                parent in parent_ids]
 
572
            self._repository.texts.add_lines(text_key, parent_keys, [])
587
573
        elif not self._repository.supports_rich_root():
588
574
            if target_inv.root.revision != revision_id:
589
575
                raise errors.IncompatibleRevision(repr(self._repository))
590
576
 
591
 
 
592
577
    def _install_revision(self, revision_id, metadata, text):
593
578
        if self._repository.has_revision(revision_id):
594
579
            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)
 
580
        revision = self._source_serializer.read_revision_from_string(text)
 
581
        self._repository.add_revision(revision.revision_id, revision)
600
582
 
601
583
    def _install_signature(self, revision_id, metadata, text):
602
584
        transaction = self._repository.get_transaction()
603
 
        if self._repository._revision_store.has_signature(revision_id,
604
 
                                                          transaction):
 
585
        if self._repository.has_signature_for_revision_id(revision_id):
605
586
            return
606
 
        self._repository._revision_store.add_revision_signature_text(
607
 
            revision_id, text, transaction)
 
587
        self._repository.add_signature_text(revision_id, text)