~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-03-16 16:58:03 UTC
  • mfrom: (3224.3.1 news-typo)
  • Revision ID: pqm@pqm.ubuntu.com-20080316165803-tisoc9mpob9z544o
(Matt Nordhoff) Trivial NEWS typo fix

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2010 Canonical Ltd
 
1
# Copyright (C) 2007 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., 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
19
19
import re
20
20
 
21
21
from bzrlib import (
 
22
    diff,
22
23
    errors,
23
24
    iterablefile,
24
 
    lru_cache,
25
25
    multiparent,
26
26
    osutils,
27
27
    pack,
28
28
    revision as _mod_revision,
29
 
    serializer,
30
29
    trace,
31
 
    ui,
32
 
    versionedfile as _mod_versionedfile,
 
30
    xml_serializer,
33
31
    )
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
 
32
from bzrlib.bundle import bundle_data, serializer
 
33
from bzrlib.util import bencode
74
34
 
75
35
 
76
36
class BundleWriter(object):
94
54
 
95
55
    def begin(self):
96
56
        """Start writing the bundle"""
97
 
        self._fileobj.write(bundle_serializer._get_bundle_header(
98
 
            bundle_serializer.v4_string))
 
57
        self._fileobj.write(serializer._get_bundle_header(
 
58
            serializer.v4_string))
99
59
        self._fileobj.write('#\n')
100
60
        self._container.begin()
101
61
 
147
107
    @staticmethod
148
108
    def encode_name(content_kind, revision_id, file_id=None):
149
109
        """Encode semantic ids as a container name"""
150
 
        if content_kind not in ('revision', 'file', 'inventory', 'signature',
151
 
                'info'):
152
 
            raise ValueError(content_kind)
 
110
        assert content_kind in ('revision', 'file', 'inventory', 'signature',
 
111
                                'info')
 
112
 
153
113
        if content_kind == 'file':
154
 
            if file_id is None:
155
 
                raise AssertionError()
 
114
            assert file_id is not None
156
115
        else:
157
 
            if file_id is not None:
158
 
                raise AssertionError()
 
116
            assert file_id is None
159
117
        if content_kind == 'info':
160
 
            if revision_id is not None:
161
 
                raise AssertionError()
162
 
        elif revision_id is None:
163
 
            raise AssertionError()
 
118
            assert revision_id is None
 
119
        else:
 
120
            assert revision_id is not None
164
121
        names = [n.replace('/', '//') for n in
165
122
                 (content_kind, revision_id, file_id) if n is not None]
166
123
        return '/'.join(names)
258
215
            yield (bytes, metadata) + self.decode_name(names[0][0])
259
216
 
260
217
 
261
 
class BundleSerializerV4(bundle_serializer.BundleSerializer):
 
218
class BundleSerializerV4(serializer.BundleSerializer):
262
219
    """Implement the high-level bundle interface"""
263
220
 
264
221
    def write(self, repository, revision_ids, forced_bases, fileobj):
290
247
    @staticmethod
291
248
    def get_source_serializer(info):
292
249
        """Retrieve the serializer for a given info object"""
293
 
        return serializer.format_registry.get(info['serializer'])
 
250
        return xml_serializer.format_registry.get(info['serializer'])
294
251
 
295
252
 
296
253
class BundleWriteOperation(object):
310
267
        self.repository = repository
311
268
        bundle = BundleWriter(fileobj)
312
269
        self.bundle = bundle
 
270
        self.base_ancestry = set(repository.get_ancestry(base,
 
271
                                                         topo_sorted=False))
313
272
        if revision_ids is not None:
314
273
            self.revision_ids = revision_ids
315
274
        else:
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])
 
275
            revision_ids = set(repository.get_ancestry(target,
 
276
                                                       topo_sorted=False))
 
277
            self.revision_ids = revision_ids.difference(self.base_ancestry)
322
278
 
323
279
    def do_write(self):
324
280
        """Write all data to the bundle"""
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()
 
281
        self.bundle.begin()
 
282
        self.write_info()
 
283
        self.write_files()
 
284
        self.write_revisions()
 
285
        self.bundle.end()
336
286
        return self.revision_ids
337
287
 
338
288
    def write_info(self):
343
293
        self.bundle.add_info_record(serializer=serializer_format,
344
294
                                    supports_rich_root=supports_rich_root)
345
295
 
 
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
 
346
333
    def write_files(self):
347
334
        """Write bundle records for all revisions of all files"""
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)
 
335
        for vf, file_id, revision_ids in self.iter_file_revisions():
 
336
            self.add_mp_records('file', file_id, vf, revision_ids)
355
337
 
356
338
    def write_revisions(self):
357
339
        """Write bundle records for all revisions and signatures"""
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
 
340
        inv_vf = self.repository.get_inventory_weave()
 
341
        revision_order = list(multiparent.topo_iter(inv_vf, self.revision_ids))
362
342
        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)
365
343
            revision_order.remove(self.target)
366
344
            revision_order.append(self.target)
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):
 
345
        self.add_mp_records('inventory', None, inv_vf, revision_order)
398
346
        parent_map = self.repository.get_parent_map(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
 
347
        for revision_id in revision_order:
403
348
            parents = parent_map.get(revision_id, None)
404
 
            revision_text = revision_to_str(revision)
 
349
            revision_text = self.repository.get_revision_xml(revision_id)
405
350
            self.bundle.add_fulltext_record(revision_text, parents,
406
351
                                       'revision', revision_id)
407
352
            try:
426
371
                base = parents[0]
427
372
        return base, target
428
373
 
429
 
    def _add_mp_records_keys(self, repo_kind, vf, keys):
 
374
    def add_mp_records(self, repo_kind, file_id, vf, revision_ids):
430
375
        """Add multi-parent diff records to a bundle"""
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]]
 
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)
438
381
            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
444
382
            self.bundle.add_multiparent_record(text, sha1, parents, repo_kind,
445
 
                                               item_key[-1], file_id)
 
383
                                               revision_id, file_id)
446
384
 
447
385
 
448
386
class BundleInfoV4(object):
532
470
 
533
471
    def install(self):
534
472
        """Perform the installation.
535
 
 
 
473
        
536
474
        Must be called with the Repository locked.
537
475
        """
538
476
        self._repository.start_write_group()
555
493
        for bytes, metadata, repo_kind, revision_id, file_id in\
556
494
            self._container.iter_records():
557
495
            if repo_kind == 'info':
558
 
                if self._info is not None:
559
 
                    raise AssertionError()
 
496
                assert self._info is None
560
497
                self._handle_info(metadata)
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)
 
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)
567
502
                current_file = None
568
 
                del pending_file_records[:]
 
503
                current_versionedfile = None
 
504
                pending_file_records = []
569
505
            if len(pending_inventory_records) > 0 and repo_kind != 'inventory':
570
 
                self._install_inventory_records(pending_inventory_records)
 
506
                self._install_inventory_records(inventory_vf,
 
507
                                                pending_inventory_records)
571
508
                pending_inventory_records = []
572
509
            if repo_kind == 'inventory':
573
 
                pending_inventory_records.append(((revision_id,), metadata, bytes))
 
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))
574
515
            if repo_kind == 'revision':
575
516
                target_revision = revision_id
576
517
                self._install_revision(revision_id, metadata, bytes)
578
519
                self._install_signature(revision_id, metadata, bytes)
579
520
            if repo_kind == 'file':
580
521
                current_file = file_id
581
 
                pending_file_records.append(((file_id, revision_id), metadata, bytes))
582
 
        self._install_mp_records_keys(self._repository.texts, pending_file_records)
 
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)
583
531
        return target_revision
584
532
 
585
533
    def _handle_info(self, info):
600
548
                      records if r not in versionedfile]
601
549
        versionedfile.add_mpdiffs(vf_records)
602
550
 
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()
 
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))
718
573
 
719
574
    def _handle_root(self, target_inv, parent_ids):
720
575
        revision_id = target_inv.revision_id
721
576
        if self.update_root:
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, [])
 
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, [])
726
582
        elif not self._repository.supports_rich_root():
727
583
            if target_inv.root.revision != revision_id:
728
584
                raise errors.IncompatibleRevision(repr(self._repository))
729
585
 
 
586
 
730
587
    def _install_revision(self, revision_id, metadata, text):
731
588
        if self._repository.has_revision(revision_id):
732
589
            return
733
 
        revision = self._source_serializer.read_revision_from_string(text)
734
 
        self._repository.add_revision(revision.revision_id, revision)
 
590
        self._repository._add_revision_text(revision_id, text)
735
591
 
736
592
    def _install_signature(self, revision_id, metadata, text):
737
593
        transaction = self._repository.get_transaction()
738
 
        if self._repository.has_signature_for_revision_id(revision_id):
 
594
        if self._repository._revision_store.has_signature(revision_id,
 
595
                                                          transaction):
739
596
            return
740
 
        self._repository.add_signature_text(revision_id, text)
 
597
        self._repository._revision_store.add_revision_signature_text(
 
598
            revision_id, text, transaction)