/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

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

  • Committer: Alexander Belchenko
  • Date: 2007-11-02 08:45:10 UTC
  • mto: This revision was merged to the branch mainline in revision 2968.
  • Revision ID: bialix@ukr.net-20071102084510-ngqdd24hjhfdkgw3
start 0.93 development cycle; deprecate osutils.backup_file

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
from cStringIO import StringIO
18
18
import bz2
22
22
    diff,
23
23
    errors,
24
24
    iterablefile,
25
 
    lru_cache,
26
25
    multiparent,
27
26
    osutils,
28
27
    pack,
29
28
    revision as _mod_revision,
30
 
    serializer,
31
29
    trace,
32
 
    ui,
 
30
    xml_serializer,
33
31
    )
34
 
from bzrlib.bundle import bundle_data, serializer as bundle_serializer
35
 
from bzrlib import bencode
 
32
from bzrlib.bundle import bundle_data, serializer
 
33
from bzrlib.util import bencode
36
34
 
37
35
 
38
36
class BundleWriter(object):
56
54
 
57
55
    def begin(self):
58
56
        """Start writing the bundle"""
59
 
        self._fileobj.write(bundle_serializer._get_bundle_header(
60
 
            bundle_serializer.v4_string))
 
57
        self._fileobj.write(serializer._get_bundle_header(
 
58
            serializer.v4_string))
61
59
        self._fileobj.write('#\n')
62
60
        self._container.begin()
63
61
 
109
107
    @staticmethod
110
108
    def encode_name(content_kind, revision_id, file_id=None):
111
109
        """Encode semantic ids as a container name"""
112
 
        if content_kind not in ('revision', 'file', 'inventory', 'signature',
113
 
                'info'):
114
 
            raise ValueError(content_kind)
 
110
        assert content_kind in ('revision', 'file', 'inventory', 'signature',
 
111
                                'info')
 
112
 
115
113
        if content_kind == 'file':
116
 
            if file_id is None:
117
 
                raise AssertionError()
 
114
            assert file_id is not None
118
115
        else:
119
 
            if file_id is not None:
120
 
                raise AssertionError()
 
116
            assert file_id is None
121
117
        if content_kind == 'info':
122
 
            if revision_id is not None:
123
 
                raise AssertionError()
124
 
        elif revision_id is None:
125
 
            raise AssertionError()
 
118
            assert revision_id is None
 
119
        else:
 
120
            assert revision_id is not None
126
121
        names = [n.replace('/', '//') for n in
127
122
                 (content_kind, revision_id, file_id) if n is not None]
128
123
        return '/'.join(names)
165
160
            source_file = iterablefile.IterableFile(self.iter_decode(fileobj))
166
161
        else:
167
162
            source_file = StringIO(bz2.decompress(fileobj.read()))
168
 
        self._container_file = source_file
 
163
        self._container = pack.ContainerReader(source_file)
169
164
 
170
165
    @staticmethod
171
166
    def iter_decode(fileobj):
172
167
        """Iterate through decoded fragments of the file"""
173
168
        decompressor = bz2.BZ2Decompressor()
174
169
        for line in fileobj:
175
 
            try:
176
 
                yield decompressor.decompress(line)
177
 
            except EOFError:
178
 
                return
 
170
            yield decompressor.decompress(line)
179
171
 
180
172
    @staticmethod
181
173
    def decode_name(name):
207
199
        :return: a generator of (bytes, metadata, content_kind, revision_id,
208
200
            file_id)
209
201
        """
210
 
        iterator = pack.iter_records_from_file(self._container_file)
211
 
        for names, bytes in iterator:
 
202
        iterator = self._container.iter_records()
 
203
        for names, meta_bytes in iterator:
212
204
            if len(names) != 1:
213
205
                raise errors.BadBundle('Record has %d names instead of 1'
214
206
                                       % len(names))
215
 
            metadata = bencode.bdecode(bytes)
 
207
            metadata = bencode.bdecode(meta_bytes(None))
216
208
            if metadata['storage_kind'] == 'header':
217
209
                bytes = None
218
210
            else:
219
211
                _unused, bytes = iterator.next()
 
212
                bytes = bytes(None)
220
213
            yield (bytes, metadata) + self.decode_name(names[0][0])
221
214
 
222
215
 
223
 
class BundleSerializerV4(bundle_serializer.BundleSerializer):
 
216
class BundleSerializerV4(serializer.BundleSerializer):
224
217
    """Implement the high-level bundle interface"""
225
218
 
226
219
    def write(self, repository, revision_ids, forced_bases, fileobj):
252
245
    @staticmethod
253
246
    def get_source_serializer(info):
254
247
        """Retrieve the serializer for a given info object"""
255
 
        return serializer.format_registry.get(info['serializer'])
 
248
        return xml_serializer.format_registry.get(info['serializer'])
256
249
 
257
250
 
258
251
class BundleWriteOperation(object):
272
265
        self.repository = repository
273
266
        bundle = BundleWriter(fileobj)
274
267
        self.bundle = bundle
 
268
        self.base_ancestry = set(repository.get_ancestry(base,
 
269
                                                         topo_sorted=False))
275
270
        if revision_ids is not None:
276
271
            self.revision_ids = revision_ids
277
272
        else:
278
 
            graph = repository.get_graph()
279
 
            revision_ids = graph.find_unique_ancestors(target, [base])
280
 
            # Strip ghosts
281
 
            parents = graph.get_parent_map(revision_ids)
282
 
            self.revision_ids = [r for r in revision_ids if r in parents]
283
 
        self.revision_keys = set([(revid,) for revid in self.revision_ids])
 
273
            revision_ids = set(repository.get_ancestry(target,
 
274
                                                       topo_sorted=False))
 
275
            self.revision_ids = revision_ids.difference(self.base_ancestry)
284
276
 
285
277
    def do_write(self):
286
278
        """Write all data to the bundle"""
287
 
        trace.note('Bundling %d revision(s).', len(self.revision_ids))
288
 
        self.repository.lock_read()
289
 
        try:
290
 
            self.bundle.begin()
291
 
            self.write_info()
292
 
            self.write_files()
293
 
            self.write_revisions()
294
 
            self.bundle.end()
295
 
        finally:
296
 
            self.repository.unlock()
 
279
        self.bundle.begin()
 
280
        self.write_info()
 
281
        self.write_files()
 
282
        self.write_revisions()
 
283
        self.bundle.end()
297
284
        return self.revision_ids
298
285
 
299
286
    def write_info(self):
304
291
        self.bundle.add_info_record(serializer=serializer_format,
305
292
                                    supports_rich_root=supports_rich_root)
306
293
 
 
294
    def iter_file_revisions(self):
 
295
        """Iterate through all relevant revisions of all files.
 
296
 
 
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.
 
300
        """
 
301
        transaction = self.repository.get_transaction()
 
302
        altered = self.repository.fileids_altered_by_revision_ids(
 
303
            self.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
 
307
 
 
308
    def iter_file_revisions_aggressive(self):
 
309
        """Iterate through all relevant revisions of all files.
 
310
 
 
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.
 
314
 
 
315
        All build dependencies which are not ancestors of the base revision
 
316
        are emitted.
 
317
        """
 
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:
 
324
                    continue
 
325
                if revision_id in self.base_ancestry:
 
326
                    continue
 
327
                new_revision_ids.add(revision_id)
 
328
                pending.extend(vf.get_parents(revision_id))
 
329
            yield vf, file_id, new_revision_ids
 
330
 
307
331
    def write_files(self):
308
332
        """Write bundle records for all revisions of all files"""
309
 
        text_keys = []
310
 
        altered_fileids = self.repository.fileids_altered_by_revision_ids(
311
 
                self.revision_ids)
312
 
        for file_id, revision_ids in altered_fileids.iteritems():
313
 
            for revision_id in revision_ids:
314
 
                text_keys.append((file_id, revision_id))
315
 
        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)
316
335
 
317
336
    def write_revisions(self):
318
337
        """Write bundle records for all revisions and signatures"""
319
 
        inv_vf = self.repository.inventories
320
 
        topological_order = [key[-1] for key in multiparent.topo_iter_keys(
321
 
                                inv_vf, self.revision_keys)]
322
 
        revision_order = topological_order
 
338
        inv_vf = self.repository.get_inventory_weave()
 
339
        revision_order = list(multiparent.topo_iter(inv_vf, self.revision_ids))
323
340
        if self.target is not None and self.target in self.revision_ids:
324
 
            # Make sure the target revision is always the last entry
325
 
            revision_order = list(topological_order)
326
341
            revision_order.remove(self.target)
327
342
            revision_order.append(self.target)
328
 
        if self.repository._serializer.support_altered_by_hack:
329
 
            # Repositories that support_altered_by_hack means that
330
 
            # inventories.make_mpdiffs() contains all the data about the tree
331
 
            # shape. Formats without support_altered_by_hack require
332
 
            # chk_bytes/etc, so we use a different code path.
333
 
            self._add_mp_records_keys('inventory', inv_vf,
334
 
                                      [(revid,) for revid in topological_order])
335
 
        else:
336
 
            # Inventories should always be added in pure-topological order, so
337
 
            # that we can apply the mpdiff for the child to the parent texts.
338
 
            self._add_inventory_mpdiffs_from_serializer(topological_order)
339
 
        self._add_revision_texts(revision_order)
340
 
 
341
 
    def _add_inventory_mpdiffs_from_serializer(self, revision_order):
342
 
        """Generate mpdiffs by serializing inventories.
343
 
 
344
 
        The current repository only has part of the tree shape information in
345
 
        the 'inventories' vf. So we use serializer.write_inventory_to_string to
346
 
        get a 'full' representation of the tree shape, and then generate
347
 
        mpdiffs on that data stream. This stream can then be reconstructed on
348
 
        the other side.
349
 
        """
350
 
        inventory_key_order = [(r,) for r in revision_order]
351
 
        parent_map = self.repository.inventories.get_parent_map(
352
 
                            inventory_key_order)
353
 
        missing_keys = set(inventory_key_order).difference(parent_map)
354
 
        if missing_keys:
355
 
            raise errors.RevisionNotPresent(list(missing_keys)[0],
356
 
                                            self.repository.inventories)
357
 
        inv_to_str = self.repository._serializer.write_inventory_to_string
358
 
        # Make sure that we grab the parent texts first
359
 
        just_parents = set()
360
 
        map(just_parents.update, parent_map.itervalues())
361
 
        just_parents.difference_update(parent_map)
362
 
        # Ignore ghost parents
363
 
        present_parents = self.repository.inventories.get_parent_map(
364
 
                            just_parents)
365
 
        ghost_keys = just_parents.difference(present_parents)
366
 
        needed_inventories = list(present_parents) + inventory_key_order
367
 
        needed_inventories = [k[-1] for k in needed_inventories]
368
 
        all_lines = {}
369
 
        for inv in self.repository.iter_inventories(needed_inventories):
370
 
            revision_id = inv.revision_id
371
 
            key = (revision_id,)
372
 
            as_bytes = inv_to_str(inv)
373
 
            # The sha1 is validated as the xml/textual form, not as the
374
 
            # form-in-the-repository
375
 
            sha1 = osutils.sha_string(as_bytes)
376
 
            as_lines = osutils.split_lines(as_bytes)
377
 
            del as_bytes
378
 
            all_lines[key] = as_lines
379
 
            if key in just_parents:
380
 
                # We don't transmit those entries
381
 
                continue
382
 
            # Create an mpdiff for this text, and add it to the output
383
 
            parent_keys = parent_map[key]
384
 
            # See the comment in VF.make_mpdiffs about how this effects
385
 
            # ordering when there are ghosts present. I think we have a latent
386
 
            # bug
387
 
            parent_lines = [all_lines[p_key] for p_key in parent_keys
388
 
                            if p_key not in ghost_keys]
389
 
            diff = multiparent.MultiParent.from_lines(
390
 
                as_lines, parent_lines)
391
 
            text = ''.join(diff.to_patch())
392
 
            parent_ids = [k[-1] for k in parent_keys]
393
 
            self.bundle.add_multiparent_record(text, sha1, parent_ids,
394
 
                                               'inventory', revision_id, None)
395
 
 
396
 
    def _add_revision_texts(self, revision_order):
397
 
        parent_map = self.repository.get_parent_map(revision_order)
398
 
        revision_to_str = self.repository._serializer.write_revision_to_string
399
 
        revisions = self.repository.get_revisions(revision_order)
400
 
        for revision in revisions:
401
 
            revision_id = revision.revision_id
402
 
            parents = parent_map.get(revision_id, None)
403
 
            revision_text = revision_to_str(revision)
 
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):
 
346
            revision_text = self.repository.get_revision_xml(revision_id)
404
347
            self.bundle.add_fulltext_record(revision_text, parents,
405
348
                                       'revision', revision_id)
406
349
            try:
425
368
                base = parents[0]
426
369
        return base, target
427
370
 
428
 
    def _add_mp_records_keys(self, repo_kind, vf, keys):
 
371
    def add_mp_records(self, repo_kind, file_id, vf, revision_ids):
429
372
        """Add multi-parent diff records to a bundle"""
430
 
        ordered_keys = list(multiparent.topo_iter_keys(vf, keys))
431
 
        mpdiffs = vf.make_mpdiffs(ordered_keys)
432
 
        sha1s = vf.get_sha1s(ordered_keys)
433
 
        parent_map = vf.get_parent_map(ordered_keys)
434
 
        for mpdiff, item_key, in zip(mpdiffs, ordered_keys):
435
 
            sha1 = sha1s[item_key]
436
 
            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)
437
378
            text = ''.join(mpdiff.to_patch())
438
 
            # Infer file id records as appropriate.
439
 
            if len(item_key) == 2:
440
 
                file_id = item_key[0]
441
 
            else:
442
 
                file_id = None
443
379
            self.bundle.add_multiparent_record(text, sha1, parents, repo_kind,
444
 
                                               item_key[-1], file_id)
 
380
                                               revision_id, file_id)
445
381
 
446
382
 
447
383
class BundleInfoV4(object):
531
467
 
532
468
    def install(self):
533
469
        """Perform the installation.
534
 
 
 
470
        
535
471
        Must be called with the Repository locked.
536
472
        """
537
473
        self._repository.start_write_group()
554
490
        for bytes, metadata, repo_kind, revision_id, file_id in\
555
491
            self._container.iter_records():
556
492
            if repo_kind == 'info':
557
 
                if self._info is not None:
558
 
                    raise AssertionError()
 
493
                assert self._info is None
559
494
                self._handle_info(metadata)
560
 
            if (pending_file_records and
561
 
                (repo_kind, file_id) != ('file', current_file)):
562
 
                # Flush the data for a single file - prevents memory
563
 
                # spiking due to buffering all files in memory.
564
 
                self._install_mp_records_keys(self._repository.texts,
565
 
                    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)
566
499
                current_file = None
567
 
                del pending_file_records[:]
 
500
                current_versionedfile = None
 
501
                pending_file_records = []
568
502
            if len(pending_inventory_records) > 0 and repo_kind != 'inventory':
569
 
                self._install_inventory_records(pending_inventory_records)
 
503
                self._install_inventory_records(inventory_vf,
 
504
                                                pending_inventory_records)
570
505
                pending_inventory_records = []
571
506
            if repo_kind == 'inventory':
572
 
                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,
 
511
                                                      bytes))
573
512
            if repo_kind == 'revision':
574
513
                target_revision = revision_id
575
514
                self._install_revision(revision_id, metadata, bytes)
577
516
                self._install_signature(revision_id, metadata, bytes)
578
517
            if repo_kind == 'file':
579
518
                current_file = file_id
580
 
                pending_file_records.append(((file_id, revision_id), metadata, bytes))
581
 
        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:
 
525
                    continue
 
526
                pending_file_records.append((revision_id, metadata, bytes))
 
527
        self._install_mp_records(current_versionedfile, pending_file_records)
582
528
        return target_revision
583
529
 
584
530
    def _handle_info(self, info):
599
545
                      records if r not in versionedfile]
600
546
        versionedfile.add_mpdiffs(vf_records)
601
547
 
602
 
    def _install_mp_records_keys(self, versionedfile, records):
603
 
        d_func = multiparent.MultiParent.from_patch
604
 
        vf_records = []
605
 
        for key, meta, text in records:
606
 
            # Adapt to tuple interface: A length two key is a file_id,
607
 
            # revision_id pair, a length 1 key is a
608
 
            # revision/signature/inventory. We need to do this because
609
 
            # the metadata extraction from the bundle has not yet been updated
610
 
            # to use the consistent tuple interface itself.
611
 
            if len(key) == 2:
612
 
                prefix = key[:1]
613
 
            else:
614
 
                prefix = ()
615
 
            parents = [prefix + (parent,) for parent in meta['parents']]
616
 
            vf_records.append((key, parents, meta['sha1'], d_func(text)))
617
 
        versionedfile.add_mpdiffs(vf_records)
618
 
 
619
 
    def _get_parent_inventory_texts(self, inventory_text_cache,
620
 
                                    inventory_cache, parent_ids):
621
 
        cached_parent_texts = {}
622
 
        remaining_parent_ids = []
623
 
        for parent_id in parent_ids:
624
 
            p_text = inventory_text_cache.get(parent_id, None)
625
 
            if p_text is None:
626
 
                remaining_parent_ids.append(parent_id)
627
 
            else:
628
 
                cached_parent_texts[parent_id] = p_text
629
 
        ghosts = ()
630
 
        # TODO: Use inventory_cache to grab inventories we already have in
631
 
        #       memory
632
 
        if remaining_parent_ids:
633
 
            # first determine what keys are actually present in the local
634
 
            # inventories object (don't use revisions as they haven't been
635
 
            # installed yet.)
636
 
            parent_keys = [(r,) for r in remaining_parent_ids]
637
 
            present_parent_map = self._repository.inventories.get_parent_map(
638
 
                                        parent_keys)
639
 
            present_parent_ids = []
640
 
            ghosts = set()
641
 
            for p_id in remaining_parent_ids:
642
 
                if (p_id,) in present_parent_map:
643
 
                    present_parent_ids.append(p_id)
644
 
                else:
645
 
                    ghosts.add(p_id)
646
 
            to_string = self._source_serializer.write_inventory_to_string
647
 
            for parent_inv in self._repository.iter_inventories(
648
 
                                    present_parent_ids):
649
 
                p_text = to_string(parent_inv)
650
 
                inventory_cache[parent_inv.revision_id] = parent_inv
651
 
                cached_parent_texts[parent_inv.revision_id] = p_text
652
 
                inventory_text_cache[parent_inv.revision_id] = p_text
653
 
 
654
 
        parent_texts = [cached_parent_texts[parent_id]
655
 
                        for parent_id in parent_ids
656
 
                         if parent_id not in ghosts]
657
 
        return parent_texts
658
 
 
659
 
    def _install_inventory_records(self, records):
660
 
        if (self._info['serializer'] == self._repository._serializer.format_num
661
 
            and self._repository._serializer.support_altered_by_hack):
662
 
            return self._install_mp_records_keys(self._repository.inventories,
663
 
                records)
664
 
        # Use a 10MB text cache, since these are string xml inventories. Note
665
 
        # that 10MB is fairly small for large projects (a single inventory can
666
 
        # be >5MB). Another possibility is to cache 10-20 inventory texts
667
 
        # instead
668
 
        inventory_text_cache = lru_cache.LRUSizeCache(10*1024*1024)
669
 
        # Also cache the in-memory representation. This allows us to create
670
 
        # inventory deltas to apply rather than calling add_inventory from
671
 
        # scratch each time.
672
 
        inventory_cache = lru_cache.LRUCache(10)
673
 
        pb = ui.ui_factory.nested_progress_bar()
674
 
        try:
675
 
            num_records = len(records)
676
 
            for idx, (key, metadata, bytes) in enumerate(records):
677
 
                pb.update('installing inventory', idx, num_records)
678
 
                revision_id = key[-1]
679
 
                parent_ids = metadata['parents']
680
 
                # Note: This assumes the local ghosts are identical to the
681
 
                #       ghosts in the source, as the Bundle serialization
682
 
                #       format doesn't record ghosts.
683
 
                p_texts = self._get_parent_inventory_texts(inventory_text_cache,
684
 
                                                           inventory_cache,
685
 
                                                           parent_ids)
686
 
                # Why does to_lines() take strings as the source, it seems that
687
 
                # it would have to cast to a list of lines, which we get back
688
 
                # as lines and then cast back to a string.
689
 
                target_lines = multiparent.MultiParent.from_patch(bytes
690
 
                            ).to_lines(p_texts)
691
 
                inv_text = ''.join(target_lines)
692
 
                del target_lines
693
 
                sha1 = osutils.sha_string(inv_text)
694
 
                if sha1 != metadata['sha1']:
695
 
                    raise errors.BadBundle("Can't convert to target format")
696
 
                # Add this to the cache so we don't have to extract it again.
697
 
                inventory_text_cache[revision_id] = inv_text
698
 
                target_inv = self._source_serializer.read_inventory_from_string(
699
 
                    inv_text)
700
 
                self._handle_root(target_inv, parent_ids)
701
 
                parent_inv = None
702
 
                if parent_ids:
703
 
                    parent_inv = inventory_cache.get(parent_ids[0], None)
704
 
                try:
705
 
                    if parent_inv is None:
706
 
                        self._repository.add_inventory(revision_id, target_inv,
707
 
                                                       parent_ids)
708
 
                    else:
709
 
                        delta = target_inv._make_delta(parent_inv)
710
 
                        self._repository.add_inventory_by_delta(parent_ids[0],
711
 
                            delta, revision_id, parent_ids)
712
 
                except errors.UnsupportedInventoryKind:
713
 
                    raise errors.IncompatibleRevision(repr(self._repository))
714
 
                inventory_cache[revision_id] = target_inv
715
 
        finally:
716
 
            pb.finished()
 
548
    def _install_inventory_records(self, vf, records):
 
549
        if self._info['serializer'] == self._repository._serializer.format_num:
 
550
            return self._install_mp_records(vf, records)
 
551
        for revision_id, metadata, bytes in records:
 
552
            parent_ids = metadata['parents']
 
553
            parents = [self._repository.get_inventory(p)
 
554
                       for p in parent_ids]
 
555
            p_texts = [self._source_serializer.write_inventory_to_string(p)
 
556
                       for p in parents]
 
557
            target_lines = multiparent.MultiParent.from_patch(bytes).to_lines(
 
558
                p_texts)
 
559
            sha1 = osutils.sha_strings(target_lines)
 
560
            if sha1 != metadata['sha1']:
 
561
                raise errors.BadBundle("Can't convert to target format")
 
562
            target_inv = self._source_serializer.read_inventory_from_string(
 
563
                ''.join(target_lines))
 
564
            self._handle_root(target_inv, parent_ids)
 
565
            try:
 
566
                self._repository.add_inventory(revision_id, target_inv,
 
567
                                               parent_ids)
 
568
            except errors.UnsupportedInventoryKind:
 
569
                raise errors.IncompatibleRevision(repr(self._repository))
717
570
 
718
571
    def _handle_root(self, target_inv, parent_ids):
719
572
        revision_id = target_inv.revision_id
720
573
        if self.update_root:
721
 
            text_key = (target_inv.root.file_id, revision_id)
722
 
            parent_keys = [(target_inv.root.file_id, parent) for
723
 
                parent in parent_ids]
724
 
            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, [])
725
579
        elif not self._repository.supports_rich_root():
726
580
            if target_inv.root.revision != revision_id:
727
581
                raise errors.IncompatibleRevision(repr(self._repository))
728
582
 
 
583
 
729
584
    def _install_revision(self, revision_id, metadata, text):
730
585
        if self._repository.has_revision(revision_id):
731
586
            return
732
 
        revision = self._source_serializer.read_revision_from_string(text)
733
 
        self._repository.add_revision(revision.revision_id, revision)
 
587
        self._repository._add_revision_text(revision_id, text)
734
588
 
735
589
    def _install_signature(self, revision_id, metadata, text):
736
590
        transaction = self._repository.get_transaction()
737
 
        if self._repository.has_signature_for_revision_id(revision_id):
 
591
        if self._repository._revision_store.has_signature(revision_id,
 
592
                                                          transaction):
738
593
            return
739
 
        self._repository.add_signature_text(revision_id, text)
 
594
        self._repository._revision_store.add_revision_signature_text(
 
595
            revision_id, text, transaction)