~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

(jelmer) Support upgrading between the 2a and development-colo formats.
 (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007 Canonical Ltd
 
1
# Copyright (C) 2007-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
from cStringIO import StringIO
18
18
import bz2
19
19
import re
20
20
 
21
21
from bzrlib import (
22
 
    diff,
23
22
    errors,
24
23
    iterablefile,
 
24
    lru_cache,
25
25
    multiparent,
26
26
    osutils,
27
27
    pack,
28
28
    revision as _mod_revision,
 
29
    serializer,
29
30
    trace,
30
 
    xml_serializer,
 
31
    ui,
 
32
    versionedfile as _mod_versionedfile,
31
33
    )
32
 
from bzrlib.bundle import bundle_data, serializer
33
 
from bzrlib.util import bencode
 
34
from bzrlib.bundle import bundle_data, serializer as bundle_serializer
 
35
from bzrlib.i18n import ngettext
 
36
from bzrlib import bencode
 
37
 
 
38
 
 
39
class _MPDiffInventoryGenerator(_mod_versionedfile._MPDiffGenerator):
 
40
    """Generate Inventory diffs serialized inventories."""
 
41
 
 
42
    def __init__(self, repo, inventory_keys):
 
43
        super(_MPDiffInventoryGenerator, self).__init__(repo.inventories,
 
44
            inventory_keys)
 
45
        self.repo = repo
 
46
        self.sha1s = {}
 
47
 
 
48
    def iter_diffs(self):
 
49
        """Compute the diffs one at a time."""
 
50
        # This is instead of compute_diffs() since we guarantee our ordering of
 
51
        # inventories, we don't have to do any buffering
 
52
        self._find_needed_keys()
 
53
        # We actually use a slightly different ordering. We grab all of the
 
54
        # parents first, and then grab the ordered requests.
 
55
        needed_ids = [k[-1] for k in self.present_parents]
 
56
        needed_ids.extend([k[-1] for k in self.ordered_keys])
 
57
        inv_to_str = self.repo._serializer.write_inventory_to_string
 
58
        for inv in self.repo.iter_inventories(needed_ids):
 
59
            revision_id = inv.revision_id
 
60
            key = (revision_id,)
 
61
            if key in self.present_parents:
 
62
                # Not a key we will transmit, which is a shame, since because
 
63
                # of that bundles don't work with stacked branches
 
64
                parent_ids = None
 
65
            else:
 
66
                parent_ids = [k[-1] for k in self.parent_map[key]]
 
67
            as_bytes = inv_to_str(inv)
 
68
            self._process_one_record(key, (as_bytes,))
 
69
            if parent_ids is None:
 
70
                continue
 
71
            diff = self.diffs.pop(key)
 
72
            sha1 = osutils.sha_string(as_bytes)
 
73
            yield revision_id, parent_ids, sha1, diff
34
74
 
35
75
 
36
76
class BundleWriter(object):
54
94
 
55
95
    def begin(self):
56
96
        """Start writing the bundle"""
57
 
        self._fileobj.write(serializer._get_bundle_header(
58
 
            serializer.v4_string))
 
97
        self._fileobj.write(bundle_serializer._get_bundle_header(
 
98
            bundle_serializer.v4_string))
59
99
        self._fileobj.write('#\n')
60
100
        self._container.begin()
61
101
 
107
147
    @staticmethod
108
148
    def encode_name(content_kind, revision_id, file_id=None):
109
149
        """Encode semantic ids as a container name"""
110
 
        assert content_kind in ('revision', 'file', 'inventory', 'signature',
111
 
                                'info')
112
 
 
 
150
        if content_kind not in ('revision', 'file', 'inventory', 'signature',
 
151
                'info'):
 
152
            raise ValueError(content_kind)
113
153
        if content_kind == 'file':
114
 
            assert file_id is not None
 
154
            if file_id is None:
 
155
                raise AssertionError()
115
156
        else:
116
 
            assert file_id is None
 
157
            if file_id is not None:
 
158
                raise AssertionError()
117
159
        if content_kind == 'info':
118
 
            assert revision_id is None
119
 
        else:
120
 
            assert revision_id is not None
 
160
            if revision_id is not None:
 
161
                raise AssertionError()
 
162
        elif revision_id is None:
 
163
            raise AssertionError()
121
164
        names = [n.replace('/', '//') for n in
122
165
                 (content_kind, revision_id, file_id) if n is not None]
123
166
        return '/'.join(names)
215
258
            yield (bytes, metadata) + self.decode_name(names[0][0])
216
259
 
217
260
 
218
 
class BundleSerializerV4(serializer.BundleSerializer):
 
261
class BundleSerializerV4(bundle_serializer.BundleSerializer):
219
262
    """Implement the high-level bundle interface"""
220
263
 
221
264
    def write(self, repository, revision_ids, forced_bases, fileobj):
247
290
    @staticmethod
248
291
    def get_source_serializer(info):
249
292
        """Retrieve the serializer for a given info object"""
250
 
        return xml_serializer.format_registry.get(info['serializer'])
 
293
        return serializer.format_registry.get(info['serializer'])
251
294
 
252
295
 
253
296
class BundleWriteOperation(object):
267
310
        self.repository = repository
268
311
        bundle = BundleWriter(fileobj)
269
312
        self.bundle = bundle
270
 
        self.base_ancestry = set(repository.get_ancestry(base,
271
 
                                                         topo_sorted=False))
272
313
        if revision_ids is not None:
273
314
            self.revision_ids = revision_ids
274
315
        else:
275
 
            revision_ids = set(repository.get_ancestry(target,
276
 
                                                       topo_sorted=False))
277
 
            self.revision_ids = revision_ids.difference(self.base_ancestry)
 
316
            graph = repository.get_graph()
 
317
            revision_ids = graph.find_unique_ancestors(target, [base])
 
318
            # Strip ghosts
 
319
            parents = graph.get_parent_map(revision_ids)
 
320
            self.revision_ids = [r for r in revision_ids if r in parents]
 
321
        self.revision_keys = set([(revid,) for revid in self.revision_ids])
278
322
 
279
323
    def do_write(self):
280
324
        """Write all data to the bundle"""
281
 
        self.bundle.begin()
282
 
        self.write_info()
283
 
        self.write_files()
284
 
        self.write_revisions()
285
 
        self.bundle.end()
 
325
        trace.note(ngettext('Bundling %d revision.', 'Bundling %d revisions.',
 
326
                            len(self.revision_ids)), len(self.revision_ids))
 
327
        self.repository.lock_read()
 
328
        try:
 
329
            self.bundle.begin()
 
330
            self.write_info()
 
331
            self.write_files()
 
332
            self.write_revisions()
 
333
            self.bundle.end()
 
334
        finally:
 
335
            self.repository.unlock()
286
336
        return self.revision_ids
287
337
 
288
338
    def write_info(self):
293
343
        self.bundle.add_info_record(serializer=serializer_format,
294
344
                                    supports_rich_root=supports_rich_root)
295
345
 
296
 
    def iter_file_revisions(self):
297
 
        """Iterate through all relevant revisions of all files.
298
 
 
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.
302
 
        """
303
 
        transaction = self.repository.get_transaction()
304
 
        altered = self.repository.fileids_altered_by_revision_ids(
305
 
            self.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
309
 
 
310
 
    def iter_file_revisions_aggressive(self):
311
 
        """Iterate through all relevant revisions of all files.
312
 
 
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.
316
 
 
317
 
        All build dependencies which are not ancestors of the base revision
318
 
        are emitted.
319
 
        """
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:
326
 
                    continue
327
 
                if revision_id in self.base_ancestry:
328
 
                    continue
329
 
                new_revision_ids.add(revision_id)
330
 
                pending.extend(vf.get_parents(revision_id))
331
 
            yield vf, file_id, new_revision_ids
332
 
 
333
346
    def write_files(self):
334
347
        """Write bundle records for all revisions of all files"""
335
 
        for vf, file_id, revision_ids in self.iter_file_revisions():
336
 
            self.add_mp_records('file', file_id, vf, revision_ids)
 
348
        text_keys = []
 
349
        altered_fileids = self.repository.fileids_altered_by_revision_ids(
 
350
                self.revision_ids)
 
351
        for file_id, revision_ids in altered_fileids.iteritems():
 
352
            for revision_id in revision_ids:
 
353
                text_keys.append((file_id, revision_id))
 
354
        self._add_mp_records_keys('file', self.repository.texts, text_keys)
337
355
 
338
356
    def write_revisions(self):
339
357
        """Write bundle records for all revisions and signatures"""
340
 
        inv_vf = self.repository.get_inventory_weave()
341
 
        revision_order = list(multiparent.topo_iter(inv_vf, self.revision_ids))
 
358
        inv_vf = self.repository.inventories
 
359
        topological_order = [key[-1] for key in multiparent.topo_iter_keys(
 
360
                                inv_vf, self.revision_keys)]
 
361
        revision_order = topological_order
342
362
        if self.target is not None and self.target in self.revision_ids:
 
363
            # Make sure the target revision is always the last entry
 
364
            revision_order = list(topological_order)
343
365
            revision_order.remove(self.target)
344
366
            revision_order.append(self.target)
345
 
        self.add_mp_records('inventory', None, inv_vf, revision_order)
 
367
        if self.repository._serializer.support_altered_by_hack:
 
368
            # Repositories that support_altered_by_hack means that
 
369
            # inventories.make_mpdiffs() contains all the data about the tree
 
370
            # shape. Formats without support_altered_by_hack require
 
371
            # chk_bytes/etc, so we use a different code path.
 
372
            self._add_mp_records_keys('inventory', inv_vf,
 
373
                                      [(revid,) for revid in topological_order])
 
374
        else:
 
375
            # Inventories should always be added in pure-topological order, so
 
376
            # that we can apply the mpdiff for the child to the parent texts.
 
377
            self._add_inventory_mpdiffs_from_serializer(topological_order)
 
378
        self._add_revision_texts(revision_order)
 
379
 
 
380
    def _add_inventory_mpdiffs_from_serializer(self, revision_order):
 
381
        """Generate mpdiffs by serializing inventories.
 
382
 
 
383
        The current repository only has part of the tree shape information in
 
384
        the 'inventories' vf. So we use serializer.write_inventory_to_string to
 
385
        get a 'full' representation of the tree shape, and then generate
 
386
        mpdiffs on that data stream. This stream can then be reconstructed on
 
387
        the other side.
 
388
        """
 
389
        inventory_key_order = [(r,) for r in revision_order]
 
390
        generator = _MPDiffInventoryGenerator(self.repository,
 
391
                                              inventory_key_order)
 
392
        for revision_id, parent_ids, sha1, diff in generator.iter_diffs():
 
393
            text = ''.join(diff.to_patch())
 
394
            self.bundle.add_multiparent_record(text, sha1, parent_ids,
 
395
                                               'inventory', revision_id, None)
 
396
 
 
397
    def _add_revision_texts(self, revision_order):
346
398
        parent_map = self.repository.get_parent_map(revision_order)
347
 
        for revision_id in revision_order:
 
399
        revision_to_str = self.repository._serializer.write_revision_to_string
 
400
        revisions = self.repository.get_revisions(revision_order)
 
401
        for revision in revisions:
 
402
            revision_id = revision.revision_id
348
403
            parents = parent_map.get(revision_id, None)
349
 
            revision_text = self.repository.get_revision_xml(revision_id)
 
404
            revision_text = revision_to_str(revision)
350
405
            self.bundle.add_fulltext_record(revision_text, parents,
351
406
                                       'revision', revision_id)
352
407
            try:
371
426
                base = parents[0]
372
427
        return base, target
373
428
 
374
 
    def add_mp_records(self, repo_kind, file_id, vf, revision_ids):
 
429
    def _add_mp_records_keys(self, repo_kind, vf, keys):
375
430
        """Add multi-parent diff records to a bundle"""
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)
 
431
        ordered_keys = list(multiparent.topo_iter_keys(vf, keys))
 
432
        mpdiffs = vf.make_mpdiffs(ordered_keys)
 
433
        sha1s = vf.get_sha1s(ordered_keys)
 
434
        parent_map = vf.get_parent_map(ordered_keys)
 
435
        for mpdiff, item_key, in zip(mpdiffs, ordered_keys):
 
436
            sha1 = sha1s[item_key]
 
437
            parents = [key[-1] for key in parent_map[item_key]]
381
438
            text = ''.join(mpdiff.to_patch())
 
439
            # Infer file id records as appropriate.
 
440
            if len(item_key) == 2:
 
441
                file_id = item_key[0]
 
442
            else:
 
443
                file_id = None
382
444
            self.bundle.add_multiparent_record(text, sha1, parents, repo_kind,
383
 
                                               revision_id, file_id)
 
445
                                               item_key[-1], file_id)
384
446
 
385
447
 
386
448
class BundleInfoV4(object):
470
532
 
471
533
    def install(self):
472
534
        """Perform the installation.
473
 
        
 
535
 
474
536
        Must be called with the Repository locked.
475
537
        """
476
538
        self._repository.start_write_group()
493
555
        for bytes, metadata, repo_kind, revision_id, file_id in\
494
556
            self._container.iter_records():
495
557
            if repo_kind == 'info':
496
 
                assert self._info is None
 
558
                if self._info is not None:
 
559
                    raise AssertionError()
497
560
                self._handle_info(metadata)
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)
 
561
            if (pending_file_records and
 
562
                (repo_kind, file_id) != ('file', current_file)):
 
563
                # Flush the data for a single file - prevents memory
 
564
                # spiking due to buffering all files in memory.
 
565
                self._install_mp_records_keys(self._repository.texts,
 
566
                    pending_file_records)
502
567
                current_file = None
503
 
                current_versionedfile = None
504
 
                pending_file_records = []
 
568
                del pending_file_records[:]
505
569
            if len(pending_inventory_records) > 0 and repo_kind != 'inventory':
506
 
                self._install_inventory_records(inventory_vf,
507
 
                                                pending_inventory_records)
 
570
                self._install_inventory_records(pending_inventory_records)
508
571
                pending_inventory_records = []
509
572
            if repo_kind == 'inventory':
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,
514
 
                                                      bytes))
 
573
                pending_inventory_records.append(((revision_id,), metadata, bytes))
515
574
            if repo_kind == 'revision':
516
575
                target_revision = revision_id
517
576
                self._install_revision(revision_id, metadata, bytes)
519
578
                self._install_signature(revision_id, metadata, bytes)
520
579
            if repo_kind == 'file':
521
580
                current_file = file_id
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:
528
 
                    continue
529
 
                pending_file_records.append((revision_id, metadata, bytes))
530
 
        self._install_mp_records(current_versionedfile, pending_file_records)
 
581
                pending_file_records.append(((file_id, revision_id), metadata, bytes))
 
582
        self._install_mp_records_keys(self._repository.texts, pending_file_records)
531
583
        return target_revision
532
584
 
533
585
    def _handle_info(self, info):
548
600
                      records if r not in versionedfile]
549
601
        versionedfile.add_mpdiffs(vf_records)
550
602
 
551
 
    def _install_inventory_records(self, vf, records):
552
 
        if self._info['serializer'] == self._repository._serializer.format_num:
553
 
            return self._install_mp_records(vf, records)
554
 
        for revision_id, metadata, bytes in records:
555
 
            parent_ids = metadata['parents']
556
 
            parents = [self._repository.get_inventory(p)
557
 
                       for p in parent_ids]
558
 
            p_texts = [self._source_serializer.write_inventory_to_string(p)
559
 
                       for p in parents]
560
 
            target_lines = multiparent.MultiParent.from_patch(bytes).to_lines(
561
 
                p_texts)
562
 
            sha1 = osutils.sha_strings(target_lines)
563
 
            if sha1 != metadata['sha1']:
564
 
                raise errors.BadBundle("Can't convert to target format")
565
 
            target_inv = self._source_serializer.read_inventory_from_string(
566
 
                ''.join(target_lines))
567
 
            self._handle_root(target_inv, parent_ids)
568
 
            try:
569
 
                self._repository.add_inventory(revision_id, target_inv,
570
 
                                               parent_ids)
571
 
            except errors.UnsupportedInventoryKind:
572
 
                raise errors.IncompatibleRevision(repr(self._repository))
 
603
    def _install_mp_records_keys(self, versionedfile, records):
 
604
        d_func = multiparent.MultiParent.from_patch
 
605
        vf_records = []
 
606
        for key, meta, text in records:
 
607
            # Adapt to tuple interface: A length two key is a file_id,
 
608
            # revision_id pair, a length 1 key is a
 
609
            # revision/signature/inventory. We need to do this because
 
610
            # the metadata extraction from the bundle has not yet been updated
 
611
            # to use the consistent tuple interface itself.
 
612
            if len(key) == 2:
 
613
                prefix = key[:1]
 
614
            else:
 
615
                prefix = ()
 
616
            parents = [prefix + (parent,) for parent in meta['parents']]
 
617
            vf_records.append((key, parents, meta['sha1'], d_func(text)))
 
618
        versionedfile.add_mpdiffs(vf_records)
 
619
 
 
620
    def _get_parent_inventory_texts(self, inventory_text_cache,
 
621
                                    inventory_cache, parent_ids):
 
622
        cached_parent_texts = {}
 
623
        remaining_parent_ids = []
 
624
        for parent_id in parent_ids:
 
625
            p_text = inventory_text_cache.get(parent_id, None)
 
626
            if p_text is None:
 
627
                remaining_parent_ids.append(parent_id)
 
628
            else:
 
629
                cached_parent_texts[parent_id] = p_text
 
630
        ghosts = ()
 
631
        # TODO: Use inventory_cache to grab inventories we already have in
 
632
        #       memory
 
633
        if remaining_parent_ids:
 
634
            # first determine what keys are actually present in the local
 
635
            # inventories object (don't use revisions as they haven't been
 
636
            # installed yet.)
 
637
            parent_keys = [(r,) for r in remaining_parent_ids]
 
638
            present_parent_map = self._repository.inventories.get_parent_map(
 
639
                                        parent_keys)
 
640
            present_parent_ids = []
 
641
            ghosts = set()
 
642
            for p_id in remaining_parent_ids:
 
643
                if (p_id,) in present_parent_map:
 
644
                    present_parent_ids.append(p_id)
 
645
                else:
 
646
                    ghosts.add(p_id)
 
647
            to_string = self._source_serializer.write_inventory_to_string
 
648
            for parent_inv in self._repository.iter_inventories(
 
649
                                    present_parent_ids):
 
650
                p_text = to_string(parent_inv)
 
651
                inventory_cache[parent_inv.revision_id] = parent_inv
 
652
                cached_parent_texts[parent_inv.revision_id] = p_text
 
653
                inventory_text_cache[parent_inv.revision_id] = p_text
 
654
 
 
655
        parent_texts = [cached_parent_texts[parent_id]
 
656
                        for parent_id in parent_ids
 
657
                         if parent_id not in ghosts]
 
658
        return parent_texts
 
659
 
 
660
    def _install_inventory_records(self, records):
 
661
        if (self._info['serializer'] == self._repository._serializer.format_num
 
662
            and self._repository._serializer.support_altered_by_hack):
 
663
            return self._install_mp_records_keys(self._repository.inventories,
 
664
                records)
 
665
        # Use a 10MB text cache, since these are string xml inventories. Note
 
666
        # that 10MB is fairly small for large projects (a single inventory can
 
667
        # be >5MB). Another possibility is to cache 10-20 inventory texts
 
668
        # instead
 
669
        inventory_text_cache = lru_cache.LRUSizeCache(10*1024*1024)
 
670
        # Also cache the in-memory representation. This allows us to create
 
671
        # inventory deltas to apply rather than calling add_inventory from
 
672
        # scratch each time.
 
673
        inventory_cache = lru_cache.LRUCache(10)
 
674
        pb = ui.ui_factory.nested_progress_bar()
 
675
        try:
 
676
            num_records = len(records)
 
677
            for idx, (key, metadata, bytes) in enumerate(records):
 
678
                pb.update('installing inventory', idx, num_records)
 
679
                revision_id = key[-1]
 
680
                parent_ids = metadata['parents']
 
681
                # Note: This assumes the local ghosts are identical to the
 
682
                #       ghosts in the source, as the Bundle serialization
 
683
                #       format doesn't record ghosts.
 
684
                p_texts = self._get_parent_inventory_texts(inventory_text_cache,
 
685
                                                           inventory_cache,
 
686
                                                           parent_ids)
 
687
                # Why does to_lines() take strings as the source, it seems that
 
688
                # it would have to cast to a list of lines, which we get back
 
689
                # as lines and then cast back to a string.
 
690
                target_lines = multiparent.MultiParent.from_patch(bytes
 
691
                            ).to_lines(p_texts)
 
692
                inv_text = ''.join(target_lines)
 
693
                del target_lines
 
694
                sha1 = osutils.sha_string(inv_text)
 
695
                if sha1 != metadata['sha1']:
 
696
                    raise errors.BadBundle("Can't convert to target format")
 
697
                # Add this to the cache so we don't have to extract it again.
 
698
                inventory_text_cache[revision_id] = inv_text
 
699
                target_inv = self._source_serializer.read_inventory_from_string(
 
700
                    inv_text)
 
701
                self._handle_root(target_inv, parent_ids)
 
702
                parent_inv = None
 
703
                if parent_ids:
 
704
                    parent_inv = inventory_cache.get(parent_ids[0], None)
 
705
                try:
 
706
                    if parent_inv is None:
 
707
                        self._repository.add_inventory(revision_id, target_inv,
 
708
                                                       parent_ids)
 
709
                    else:
 
710
                        delta = target_inv._make_delta(parent_inv)
 
711
                        self._repository.add_inventory_by_delta(parent_ids[0],
 
712
                            delta, revision_id, parent_ids)
 
713
                except errors.UnsupportedInventoryKind:
 
714
                    raise errors.IncompatibleRevision(repr(self._repository))
 
715
                inventory_cache[revision_id] = target_inv
 
716
        finally:
 
717
            pb.finished()
573
718
 
574
719
    def _handle_root(self, target_inv, parent_ids):
575
720
        revision_id = target_inv.revision_id
576
721
        if self.update_root:
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, [])
 
722
            text_key = (target_inv.root.file_id, revision_id)
 
723
            parent_keys = [(target_inv.root.file_id, parent) for
 
724
                parent in parent_ids]
 
725
            self._repository.texts.add_lines(text_key, parent_keys, [])
582
726
        elif not self._repository.supports_rich_root():
583
727
            if target_inv.root.revision != revision_id:
584
728
                raise errors.IncompatibleRevision(repr(self._repository))
585
729
 
586
 
 
587
730
    def _install_revision(self, revision_id, metadata, text):
588
731
        if self._repository.has_revision(revision_id):
589
732
            return
590
 
        self._repository._add_revision_text(revision_id, text)
 
733
        revision = self._source_serializer.read_revision_from_string(text)
 
734
        self._repository.add_revision(revision.revision_id, revision)
591
735
 
592
736
    def _install_signature(self, revision_id, metadata, text):
593
737
        transaction = self._repository.get_transaction()
594
 
        if self._repository._revision_store.has_signature(revision_id,
595
 
                                                          transaction):
 
738
        if self._repository.has_signature_for_revision_id(revision_id):
596
739
            return
597
 
        self._repository._revision_store.add_revision_signature_text(
598
 
            revision_id, text, transaction)
 
740
        self._repository.add_signature_text(revision_id, text)