~bzr-pqm/bzr/bzr.dev

5374.2.5 by John Arbash Meinel
Rework things a bit so the logic can be shared.
1
# Copyright (C) 2007-2010 Canonical Ltd
2520.4.85 by Aaron Bentley
Get all test passing (which just proves there aren't enough tests!)
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2520.4.85 by Aaron Bentley
Get all test passing (which just proves there aren't enough tests!)
16
2520.4.20 by Aaron Bentley
Compress and base64-encode bundle contents
17
from cStringIO import StringIO
2520.4.26 by Aaron Bentley
Make decompression reasonably memory-efficient
18
import bz2
2520.4.130 by Aaron Bentley
Finish tweaking decode_name
19
import re
2520.4.20 by Aaron Bentley
Compress and base64-encode bundle contents
20
2520.4.13 by Aaron Bentley
Use real container implementation
21
from bzrlib import (
2520.4.40 by Aaron Bentley
Add human-readable diff to bundles
22
    diff,
2520.4.34 by Aaron Bentley
Add signature support
23
    errors,
2520.4.26 by Aaron Bentley
Make decompression reasonably memory-efficient
24
    iterablefile,
4543.2.16 by John Arbash Meinel
Adding an inventory text cache.
25
    lru_cache,
2520.4.13 by Aaron Bentley
Use real container implementation
26
    multiparent,
2520.4.97 by Aaron Bentley
Hack in support for inventory conversion
27
    osutils,
2520.4.13 by Aaron Bentley
Use real container implementation
28
    pack,
2520.4.40 by Aaron Bentley
Add human-readable diff to bundles
29
    revision as _mod_revision,
4543.2.16 by John Arbash Meinel
Adding an inventory text cache.
30
    serializer,
2520.4.45 by Aaron Bentley
Handle inconsistencies in last-modified-revision between vf and inventory
31
    trace,
4543.2.16 by John Arbash Meinel
Adding an inventory text cache.
32
    ui,
5374.2.5 by John Arbash Meinel
Rework things a bit so the logic can be shared.
33
    versionedfile as _mod_versionedfile,
2520.4.13 by Aaron Bentley
Use real container implementation
34
    )
4237.3.1 by Jelmer Vernooij
Add new module with generic serializer information; keep XML-specific bits in
35
from bzrlib.bundle import bundle_data, serializer as bundle_serializer
6143.1.3 by Jonathan Riddell
more plurals
36
from bzrlib.i18n import gettext, ngettext
2694.5.1 by Alexander Belchenko
pyrex bencode (without benchmarks)
37
from bzrlib import bencode
2520.4.14 by Aaron Bentley
Get most tests passing, use format header
38
2520.4.4 by Aaron Bentley
Get basis support for a new bundle format in place
39
5374.2.5 by John Arbash Meinel
Rework things a bit so the logic can be shared.
40
class _MPDiffInventoryGenerator(_mod_versionedfile._MPDiffGenerator):
41
    """Generate Inventory diffs serialized inventories."""
42
43
    def __init__(self, repo, inventory_keys):
44
        super(_MPDiffInventoryGenerator, self).__init__(repo.inventories,
45
            inventory_keys)
46
        self.repo = repo
47
        self.sha1s = {}
48
49
    def iter_diffs(self):
50
        """Compute the diffs one at a time."""
51
        # This is instead of compute_diffs() since we guarantee our ordering of
52
        # inventories, we don't have to do any buffering
53
        self._find_needed_keys()
54
        # We actually use a slightly different ordering. We grab all of the
55
        # parents first, and then grab the ordered requests.
56
        needed_ids = [k[-1] for k in self.present_parents]
57
        needed_ids.extend([k[-1] for k in self.ordered_keys])
58
        inv_to_str = self.repo._serializer.write_inventory_to_string
59
        for inv in self.repo.iter_inventories(needed_ids):
60
            revision_id = inv.revision_id
61
            key = (revision_id,)
62
            if key in self.present_parents:
63
                # Not a key we will transmit, which is a shame, since because
64
                # of that bundles don't work with stacked branches
65
                parent_ids = None
66
            else:
67
                parent_ids = [k[-1] for k in self.parent_map[key]]
68
            as_bytes = inv_to_str(inv)
69
            self._process_one_record(key, (as_bytes,))
70
            if parent_ids is None:
71
                continue
72
            diff = self.diffs.pop(key)
73
            sha1 = osutils.sha_string(as_bytes)
74
            yield revision_id, parent_ids, sha1, diff
75
76
2520.4.25 by Aaron Bentley
Rename ContainerWriter/ContainerReader to BundleWriter/BundleReader
77
class BundleWriter(object):
2520.4.118 by Aaron Bentley
Add docs
78
    """Writer for bundle-format files.
79
80
    This serves roughly the same purpose as ContainerReader, but acts as a
81
    layer on top of it.
82
2520.4.123 by Aaron Bentley
Cleanup of bundle code
83
    Provides ways of writing the specific record types supported this bundle
2520.4.118 by Aaron Bentley
Add docs
84
    format.
85
    """
2520.4.123 by Aaron Bentley
Cleanup of bundle code
86
2520.4.23 by Aaron Bentley
Move responsability for encoding into container objects
87
    def __init__(self, fileobj):
2520.4.27 by Aaron Bentley
Use less memory when writing bzip-encoded files
88
        self._container = pack.ContainerWriter(self._write_encoded)
2520.4.23 by Aaron Bentley
Move responsability for encoding into container objects
89
        self._fileobj = fileobj
2520.4.27 by Aaron Bentley
Use less memory when writing bzip-encoded files
90
        self._compressor = bz2.BZ2Compressor()
2520.4.21 by Aaron Bentley
Finish turning ContainerWriter into a new layer
91
2520.4.118 by Aaron Bentley
Add docs
92
    def _write_encoded(self, bytes):
93
        """Write bzip2-encoded bytes to the file"""
94
        self._fileobj.write(self._compressor.compress(bytes))
95
2520.4.21 by Aaron Bentley
Finish turning ContainerWriter into a new layer
96
    def begin(self):
2520.4.118 by Aaron Bentley
Add docs
97
        """Start writing the bundle"""
4237.3.1 by Jelmer Vernooij
Add new module with generic serializer information; keep XML-specific bits in
98
        self._fileobj.write(bundle_serializer._get_bundle_header(
99
            bundle_serializer.v4_string))
2520.4.24 by Aaron Bentley
Move heading writing above container beginning
100
        self._fileobj.write('#\n')
2520.4.21 by Aaron Bentley
Finish turning ContainerWriter into a new layer
101
        self._container.begin()
102
103
    def end(self):
2520.4.118 by Aaron Bentley
Add docs
104
        """Finish writing the bundle"""
2520.4.21 by Aaron Bentley
Finish turning ContainerWriter into a new layer
105
        self._container.end()
2520.4.76 by Aaron Bentley
Move base64-encoding into merge directives
106
        self._fileobj.write(self._compressor.flush())
2520.4.21 by Aaron Bentley
Finish turning ContainerWriter into a new layer
107
2520.4.60 by Aaron Bentley
Add sha1 verification for mpdiffs
108
    def add_multiparent_record(self, mp_bytes, sha1, parents, repo_kind,
2520.4.21 by Aaron Bentley
Finish turning ContainerWriter into a new layer
109
                               revision_id, file_id):
2520.4.118 by Aaron Bentley
Add docs
110
        """Add a record for a multi-parent diff
111
112
        :mp_bytes: A multi-parent diff, as a bytestring
2520.4.123 by Aaron Bentley
Cleanup of bundle code
113
        :sha1: The sha1 hash of the fulltext
2520.4.118 by Aaron Bentley
Add docs
114
        :parents: a list of revision-ids of the parents
115
        :repo_kind: The kind of object in the repository.  May be 'file' or
116
            'inventory'
117
        :revision_id: The revision id of the mpdiff being added.
118
        :file_id: The file-id of the file, or None for inventories.
119
        """
2520.4.60 by Aaron Bentley
Add sha1 verification for mpdiffs
120
        metadata = {'parents': parents,
121
                    'storage_kind': 'mpdiff',
122
                    'sha1': sha1}
123
        self._add_record(mp_bytes, metadata, repo_kind, revision_id, file_id)
2520.4.21 by Aaron Bentley
Finish turning ContainerWriter into a new layer
124
2520.4.123 by Aaron Bentley
Cleanup of bundle code
125
    def add_fulltext_record(self, bytes, parents, repo_kind, revision_id):
2520.4.118 by Aaron Bentley
Add docs
126
        """Add a record for a fulltext
127
128
        :bytes: The fulltext, as a bytestring
129
        :parents: a list of revision-ids of the parents
130
        :repo_kind: The kind of object in the repository.  May be 'revision' or
131
            'signature'
132
        :revision_id: The revision id of the fulltext being added.
133
        """
134
        metadata = {'parents': parents,
2520.5.3 by Aaron Bentley
fix sha1 in bundle format 4
135
                    'storage_kind': 'mpdiff'}
2520.4.60 by Aaron Bentley
Add sha1 verification for mpdiffs
136
        self._add_record(bytes, {'parents': parents,
2520.4.123 by Aaron Bentley
Cleanup of bundle code
137
            'storage_kind': 'fulltext'}, repo_kind, revision_id, None)
2520.4.21 by Aaron Bentley
Finish turning ContainerWriter into a new layer
138
2520.4.95 by Aaron Bentley
Add support for header/info records
139
    def add_info_record(self, **kwargs):
2520.4.118 by Aaron Bentley
Add docs
140
        """Add an info record to the bundle
141
142
        Any parameters may be supplied, except 'self' and 'storage_kind'.
143
        Values must be lists, strings, integers, dicts, or a combination.
144
        """
2520.4.95 by Aaron Bentley
Add support for header/info records
145
        kwargs['storage_kind'] = 'header'
146
        self._add_record(None, kwargs, 'info', None, None)
147
2520.4.21 by Aaron Bentley
Finish turning ContainerWriter into a new layer
148
    @staticmethod
2520.4.68 by Aaron Bentley
Change name separators to all-slash
149
    def encode_name(content_kind, revision_id, file_id=None):
2520.4.118 by Aaron Bentley
Add docs
150
        """Encode semantic ids as a container name"""
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
151
        if content_kind not in ('revision', 'file', 'inventory', 'signature',
152
                'info'):
153
            raise ValueError(content_kind)
2520.4.118 by Aaron Bentley
Add docs
154
        if content_kind == 'file':
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
155
            if file_id is None:
156
                raise AssertionError()
2520.4.118 by Aaron Bentley
Add docs
157
        else:
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
158
            if file_id is not None:
159
                raise AssertionError()
2520.4.95 by Aaron Bentley
Add support for header/info records
160
        if content_kind == 'info':
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
161
            if revision_id is not None:
162
                raise AssertionError()
163
        elif revision_id is None:
164
            raise AssertionError()
2520.4.127 by Aaron Bentley
Fix up name encoding to handle revision-ids with slashes
165
        names = [n.replace('/', '//') for n in
166
                 (content_kind, revision_id, file_id) if n is not None]
2520.4.68 by Aaron Bentley
Change name separators to all-slash
167
        return '/'.join(names)
2520.4.21 by Aaron Bentley
Finish turning ContainerWriter into a new layer
168
2520.4.56 by Aaron Bentley
Begin adding support for arbitrary metadata
169
    def _add_record(self, bytes, metadata, repo_kind, revision_id, file_id):
2520.4.118 by Aaron Bentley
Add docs
170
        """Add a bundle record to the container.
171
172
        Most bundle records are recorded as header/body pairs, with the
173
        body being nameless.  Records with storage_kind 'header' have no
174
        body.
175
        """
2520.4.21 by Aaron Bentley
Finish turning ContainerWriter into a new layer
176
        name = self.encode_name(repo_kind, revision_id, file_id)
2520.4.95 by Aaron Bentley
Add support for header/info records
177
        encoded_metadata = bencode.bencode(metadata)
2682.1.1 by Robert Collins
* The ``bzrlib.pack`` interface has changed to use tuples of bytestrings
178
        self._container.add_bytes_record(encoded_metadata, [(name, )])
2520.4.95 by Aaron Bentley
Add support for header/info records
179
        if metadata['storage_kind'] != 'header':
180
            self._container.add_bytes_record(bytes, [])
2520.4.13 by Aaron Bentley
Use real container implementation
181
2520.4.7 by Aaron Bentley
Fix patch deserialization
182
2520.4.25 by Aaron Bentley
Rename ContainerWriter/ContainerReader to BundleWriter/BundleReader
183
class BundleReader(object):
2520.4.118 by Aaron Bentley
Add docs
184
    """Reader for bundle-format files.
185
186
    This serves roughly the same purpose as ContainerReader, but acts as a
187
    layer on top of it, providing metadata, a semantic name, and a record
188
    body
189
    """
2520.4.123 by Aaron Bentley
Cleanup of bundle code
190
4543.2.14 by John Arbash Meinel
Clarify some comments, fix up a debugging change.
191
    def __init__(self, fileobj, stream_input=True):
2520.4.145 by Aaron Bentley
Add memory_friendly toggle, be memory-unfriendly for merge directives
192
        """Constructor
193
194
        :param fileobj: a file containing a bzip-encoded container
2520.4.148 by Aaron Bentley
Updates from review
195
        :param stream_input: If True, the BundleReader stream input rather than
196
            reading it all into memory at once.  Reading it into memory all at
197
            once is (currently) faster.
2520.4.145 by Aaron Bentley
Add memory_friendly toggle, be memory-unfriendly for merge directives
198
        """
2520.4.23 by Aaron Bentley
Move responsability for encoding into container objects
199
        line = fileobj.readline()
200
        if line != '\n':
201
            fileobj.readline()
2520.4.40 by Aaron Bentley
Add human-readable diff to bundles
202
        self.patch_lines = []
2520.4.148 by Aaron Bentley
Updates from review
203
        if stream_input:
2520.4.145 by Aaron Bentley
Add memory_friendly toggle, be memory-unfriendly for merge directives
204
            source_file = iterablefile.IterableFile(self.iter_decode(fileobj))
205
        else:
206
            source_file = StringIO(bz2.decompress(fileobj.read()))
2916.2.18 by Andrew Bennetts
Use iter_records_from_file rather than ContainerReader.
207
        self._container_file = source_file
2520.4.26 by Aaron Bentley
Make decompression reasonably memory-efficient
208
209
    @staticmethod
210
    def iter_decode(fileobj):
2520.4.118 by Aaron Bentley
Add docs
211
        """Iterate through decoded fragments of the file"""
2520.4.26 by Aaron Bentley
Make decompression reasonably memory-efficient
212
        decompressor = bz2.BZ2Decompressor()
213
        for line in fileobj:
2916.2.18 by Andrew Bennetts
Use iter_records_from_file rather than ContainerReader.
214
            try:
215
                yield decompressor.decompress(line)
216
            except EOFError:
217
                return
2520.4.22 by Aaron Bentley
Create ContainerReader
218
219
    @staticmethod
220
    def decode_name(name):
2520.4.118 by Aaron Bentley
Add docs
221
        """Decode a name from its container form into a semantic form
222
223
        :retval: content_kind, revision_id, file_id
224
        """
2520.4.130 by Aaron Bentley
Finish tweaking decode_name
225
        segments = re.split('(//?)', name)
226
        names = ['']
2520.4.127 by Aaron Bentley
Fix up name encoding to handle revision-ids with slashes
227
        for segment in segments:
228
            if segment == '//':
229
                names[-1] += '/'
2520.4.130 by Aaron Bentley
Finish tweaking decode_name
230
            elif segment == '/':
2520.4.127 by Aaron Bentley
Fix up name encoding to handle revision-ids with slashes
231
                names.append('')
232
            else:
233
                names[-1] += segment
2520.4.130 by Aaron Bentley
Finish tweaking decode_name
234
        content_kind = names[0]
2520.4.95 by Aaron Bentley
Add support for header/info records
235
        revision_id = None
236
        file_id = None
237
        if len(names) > 1:
238
            revision_id = names[1]
2520.4.68 by Aaron Bentley
Change name separators to all-slash
239
        if len(names) > 2:
240
            file_id = names[2]
241
        return content_kind, revision_id, file_id
2520.4.22 by Aaron Bentley
Create ContainerReader
242
243
    def iter_records(self):
2520.4.118 by Aaron Bentley
Add docs
244
        """Iterate through bundle records
245
246
        :return: a generator of (bytes, metadata, content_kind, revision_id,
247
            file_id)
248
        """
2916.2.18 by Andrew Bennetts
Use iter_records_from_file rather than ContainerReader.
249
        iterator = pack.iter_records_from_file(self._container_file)
250
        for names, bytes in iterator:
2520.4.131 by Aaron Bentley
Raise BadBundle for records with wrong number of names
251
            if len(names) != 1:
252
                raise errors.BadBundle('Record has %d names instead of 1'
253
                                       % len(names))
2916.2.18 by Andrew Bennetts
Use iter_records_from_file rather than ContainerReader.
254
            metadata = bencode.bdecode(bytes)
2520.4.95 by Aaron Bentley
Add support for header/info records
255
            if metadata['storage_kind'] == 'header':
256
                bytes = None
257
            else:
258
                _unused, bytes = iterator.next()
2682.1.1 by Robert Collins
* The ``bzrlib.pack`` interface has changed to use tuples of bytestrings
259
            yield (bytes, metadata) + self.decode_name(names[0][0])
2520.4.22 by Aaron Bentley
Create ContainerReader
260
261
4237.3.1 by Jelmer Vernooij
Add new module with generic serializer information; keep XML-specific bits in
262
class BundleSerializerV4(bundle_serializer.BundleSerializer):
2520.4.118 by Aaron Bentley
Add docs
263
    """Implement the high-level bundle interface"""
2520.4.123 by Aaron Bentley
Cleanup of bundle code
264
2520.4.4 by Aaron Bentley
Get basis support for a new bundle format in place
265
    def write(self, repository, revision_ids, forced_bases, fileobj):
2520.4.118 by Aaron Bentley
Add docs
266
        """Write a bundle to a file-like object
267
268
        For backwards-compatibility only
269
        """
2520.4.50 by Aaron Bentley
Split write functionality out into a separate object
270
        write_op = BundleWriteOperation.from_old_args(repository, revision_ids,
271
                                                      forced_bases, fileobj)
2520.4.53 by Aaron Bentley
refactor bundle serialization to make write_bundle primary
272
        return write_op.do_write()
273
274
    def write_bundle(self, repository, target, base, fileobj):
2520.4.118 by Aaron Bentley
Add docs
275
        """Write a bundle to a file object
276
277
        :param repository: The repository to retrieve revision data from
278
        :param target: The head revision to include ancestors of
279
        :param base: The ancestor of the target to stop including acestors
280
            at.
281
        :param fileobj: The file-like object to write to
282
        """
2520.4.53 by Aaron Bentley
refactor bundle serialization to make write_bundle primary
283
        write_op =  BundleWriteOperation(base, target, repository, fileobj)
284
        return write_op.do_write()
2520.4.50 by Aaron Bentley
Split write functionality out into a separate object
285
286
    def read(self, file):
2520.4.118 by Aaron Bentley
Add docs
287
        """return a reader object for a given file"""
2520.4.72 by Aaron Bentley
Rename format to 4alpha
288
        bundle = BundleInfoV4(file, self)
2520.4.50 by Aaron Bentley
Split write functionality out into a separate object
289
        return bundle
290
2520.4.101 by Aaron Bentley
Use a registry to look up xml serializers by format
291
    @staticmethod
292
    def get_source_serializer(info):
2520.4.118 by Aaron Bentley
Add docs
293
        """Retrieve the serializer for a given info object"""
4237.3.1 by Jelmer Vernooij
Add new module with generic serializer information; keep XML-specific bits in
294
        return serializer.format_registry.get(info['serializer'])
2520.4.101 by Aaron Bentley
Use a registry to look up xml serializers by format
295
2520.4.50 by Aaron Bentley
Split write functionality out into a separate object
296
297
class BundleWriteOperation(object):
2520.4.118 by Aaron Bentley
Add docs
298
    """Perform the operation of writing revisions to a bundle"""
2520.4.123 by Aaron Bentley
Cleanup of bundle code
299
2520.4.50 by Aaron Bentley
Split write functionality out into a separate object
300
    @classmethod
301
    def from_old_args(cls, repository, revision_ids, forced_bases, fileobj):
2520.4.123 by Aaron Bentley
Cleanup of bundle code
302
        """Create a BundleWriteOperation from old-style arguments"""
2520.4.50 by Aaron Bentley
Split write functionality out into a separate object
303
        base, target = cls.get_base_target(revision_ids, forced_bases,
304
                                           repository)
305
        return BundleWriteOperation(base, target, repository, fileobj,
306
                                    revision_ids)
307
2520.4.53 by Aaron Bentley
refactor bundle serialization to make write_bundle primary
308
    def __init__(self, base, target, repository, fileobj, revision_ids=None):
2520.4.50 by Aaron Bentley
Split write functionality out into a separate object
309
        self.base = base
310
        self.target = target
311
        self.repository = repository
2520.4.39 by Aaron Bentley
Rename container => bundle(reader) where appropriate
312
        bundle = BundleWriter(fileobj)
2520.4.50 by Aaron Bentley
Split write functionality out into a separate object
313
        self.bundle = bundle
2520.4.53 by Aaron Bentley
refactor bundle serialization to make write_bundle primary
314
        if revision_ids is not None:
315
            self.revision_ids = revision_ids
316
        else:
4154.1.1 by Ian Clatworthy
make send use graph.find_difference() instead of walking all of history twice
317
            graph = repository.get_graph()
4154.1.3 by Ian Clatworthy
strip ghosts so test_bundle_with_ghosts works again
318
            revision_ids = graph.find_unique_ancestors(target, [base])
319
            # Strip ghosts
320
            parents = graph.get_parent_map(revision_ids)
321
            self.revision_ids = [r for r in revision_ids if r in parents]
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
322
        self.revision_keys = set([(revid,) for revid in self.revision_ids])
2520.4.50 by Aaron Bentley
Split write functionality out into a separate object
323
324
    def do_write(self):
2520.4.118 by Aaron Bentley
Add docs
325
        """Write all data to the bundle"""
6143.1.3 by Jonathan Riddell
more plurals
326
        trace.note(ngettext('Bundling %d revision.', 'Bundling %d revisions.',
327
                            len(self.revision_ids)), len(self.revision_ids))
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
328
        self.repository.lock_read()
329
        try:
330
            self.bundle.begin()
331
            self.write_info()
332
            self.write_files()
333
            self.write_revisions()
334
            self.bundle.end()
335
        finally:
336
            self.repository.unlock()
2520.4.53 by Aaron Bentley
refactor bundle serialization to make write_bundle primary
337
        return self.revision_ids
2520.4.50 by Aaron Bentley
Split write functionality out into a separate object
338
2520.4.97 by Aaron Bentley
Hack in support for inventory conversion
339
    def write_info(self):
2520.4.118 by Aaron Bentley
Add docs
340
        """Write format info"""
2520.4.113 by Aaron Bentley
Avoid peeking at Repository._serializer
341
        serializer_format = self.repository.get_serializer_format()
2520.4.99 by Aaron Bentley
Test conversion across models
342
        supports_rich_root = {True: 1, False: 0}[
343
            self.repository.supports_rich_root()]
2520.4.113 by Aaron Bentley
Avoid peeking at Repository._serializer
344
        self.bundle.add_info_record(serializer=serializer_format,
2520.4.99 by Aaron Bentley
Test conversion across models
345
                                    supports_rich_root=supports_rich_root)
2520.4.97 by Aaron Bentley
Hack in support for inventory conversion
346
2520.4.51 by Aaron Bentley
Split iteration through file revisions into a method, so we can vary it
347
    def write_files(self):
2520.4.118 by Aaron Bentley
Add docs
348
        """Write bundle records for all revisions of all files"""
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
349
        text_keys = []
3350.6.7 by Robert Collins
Review feedback, making things more clear, adding documentation on what is used where.
350
        altered_fileids = self.repository.fileids_altered_by_revision_ids(
351
                self.revision_ids)
352
        for file_id, revision_ids in altered_fileids.iteritems():
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
353
            for revision_id in revision_ids:
354
                text_keys.append((file_id, revision_id))
3350.6.10 by Martin Pool
VersionedFiles review cleanups
355
        self._add_mp_records_keys('file', self.repository.texts, text_keys)
2520.4.50 by Aaron Bentley
Split write functionality out into a separate object
356
357
    def write_revisions(self):
2520.4.118 by Aaron Bentley
Add docs
358
        """Write bundle records for all revisions and signatures"""
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
359
        inv_vf = self.repository.inventories
4543.2.12 by John Arbash Meinel
Always sort the inventories in pure topological order
360
        topological_order = [key[-1] for key in multiparent.topo_iter_keys(
361
                                inv_vf, self.revision_keys)]
362
        revision_order = topological_order
2520.4.75 by Aaron Bentley
Fix traceback on empty bundles.
363
        if self.target is not None and self.target in self.revision_ids:
4543.2.20 by John Arbash Meinel
Update from Martin's review feedback.
364
            # Make sure the target revision is always the last entry
4543.2.12 by John Arbash Meinel
Always sort the inventories in pure topological order
365
            revision_order = list(topological_order)
2520.4.50 by Aaron Bentley
Split write functionality out into a separate object
366
            revision_order.remove(self.target)
367
            revision_order.append(self.target)
4543.2.3 by John Arbash Meinel
Change the name to test_merge_directive
368
        if self.repository._serializer.support_altered_by_hack:
4543.2.20 by John Arbash Meinel
Update from Martin's review feedback.
369
            # Repositories that support_altered_by_hack means that
370
            # inventories.make_mpdiffs() contains all the data about the tree
371
            # shape. Formats without support_altered_by_hack require
372
            # chk_bytes/etc, so we use a different code path.
4543.2.3 by John Arbash Meinel
Change the name to test_merge_directive
373
            self._add_mp_records_keys('inventory', inv_vf,
4543.2.12 by John Arbash Meinel
Always sort the inventories in pure topological order
374
                                      [(revid,) for revid in topological_order])
4543.2.3 by John Arbash Meinel
Change the name to test_merge_directive
375
        else:
4543.2.20 by John Arbash Meinel
Update from Martin's review feedback.
376
            # Inventories should always be added in pure-topological order, so
377
            # that we can apply the mpdiff for the child to the parent texts.
4543.2.12 by John Arbash Meinel
Always sort the inventories in pure topological order
378
            self._add_inventory_mpdiffs_from_serializer(topological_order)
4543.2.3 by John Arbash Meinel
Change the name to test_merge_directive
379
        self._add_revision_texts(revision_order)
380
4543.2.4 by John Arbash Meinel
Start working on code that will use Repository._serializer.write_inventory_to_strig.
381
    def _add_inventory_mpdiffs_from_serializer(self, revision_order):
4543.2.20 by John Arbash Meinel
Update from Martin's review feedback.
382
        """Generate mpdiffs by serializing inventories.
383
384
        The current repository only has part of the tree shape information in
385
        the 'inventories' vf. So we use serializer.write_inventory_to_string to
386
        get a 'full' representation of the tree shape, and then generate
387
        mpdiffs on that data stream. This stream can then be reconstructed on
388
        the other side.
389
        """
4543.2.4 by John Arbash Meinel
Start working on code that will use Repository._serializer.write_inventory_to_strig.
390
        inventory_key_order = [(r,) for r in revision_order]
5374.2.5 by John Arbash Meinel
Rework things a bit so the logic can be shared.
391
        generator = _MPDiffInventoryGenerator(self.repository,
392
                                              inventory_key_order)
393
        for revision_id, parent_ids, sha1, diff in generator.iter_diffs():
4543.2.4 by John Arbash Meinel
Start working on code that will use Repository._serializer.write_inventory_to_strig.
394
            text = ''.join(diff.to_patch())
395
            self.bundle.add_multiparent_record(text, sha1, parent_ids,
396
                                               'inventory', revision_id, None)
397
4543.2.3 by John Arbash Meinel
Change the name to test_merge_directive
398
    def _add_revision_texts(self, revision_order):
3099.3.5 by John Arbash Meinel
Update the last couple of places that referred to Provider.get_parents() directly.
399
        parent_map = self.repository.get_parent_map(revision_order)
4202.3.1 by Andrew Bennetts
Don't use get_revision_xml when writing a bundle, instead get all the revisions together.
400
        revision_to_str = self.repository._serializer.write_revision_to_string
401
        revisions = self.repository.get_revisions(revision_order)
402
        for revision in revisions:
403
            revision_id = revision.revision_id
3099.3.5 by John Arbash Meinel
Update the last couple of places that referred to Provider.get_parents() directly.
404
            parents = parent_map.get(revision_id, None)
4202.3.1 by Andrew Bennetts
Don't use get_revision_xml when writing a bundle, instead get all the revisions together.
405
            revision_text = revision_to_str(revision)
2520.4.50 by Aaron Bentley
Split write functionality out into a separate object
406
            self.bundle.add_fulltext_record(revision_text, parents,
2520.4.123 by Aaron Bentley
Cleanup of bundle code
407
                                       'revision', revision_id)
2520.4.34 by Aaron Bentley
Add signature support
408
            try:
2520.4.50 by Aaron Bentley
Split write functionality out into a separate object
409
                self.bundle.add_fulltext_record(
410
                    self.repository.get_signature_text(
2520.4.123 by Aaron Bentley
Cleanup of bundle code
411
                    revision_id), parents, 'signature', revision_id)
2520.4.34 by Aaron Bentley
Add signature support
412
            except errors.NoSuchRevision:
413
                pass
414
2520.4.50 by Aaron Bentley
Split write functionality out into a separate object
415
    @staticmethod
416
    def get_base_target(revision_ids, forced_bases, repository):
2520.4.123 by Aaron Bentley
Cleanup of bundle code
417
        """Determine the base and target from old-style revision ids"""
2520.4.50 by Aaron Bentley
Split write functionality out into a separate object
418
        if len(revision_ids) == 0:
419
            return None, None
420
        target = revision_ids[0]
421
        base = forced_bases.get(target)
422
        if base is None:
423
            parents = repository.get_revision(target).parent_ids
424
            if len(parents) == 0:
425
                base = _mod_revision.NULL_REVISION
426
            else:
427
                base = parents[0]
428
        return base, target
429
3350.6.10 by Martin Pool
VersionedFiles review cleanups
430
    def _add_mp_records_keys(self, repo_kind, vf, keys):
2520.4.118 by Aaron Bentley
Add docs
431
        """Add multi-parent diff records to a bundle"""
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
432
        ordered_keys = list(multiparent.topo_iter_keys(vf, keys))
433
        mpdiffs = vf.make_mpdiffs(ordered_keys)
434
        sha1s = vf.get_sha1s(ordered_keys)
435
        parent_map = vf.get_parent_map(ordered_keys)
3350.8.3 by Robert Collins
VF.get_sha1s needed changing to be stackable.
436
        for mpdiff, item_key, in zip(mpdiffs, ordered_keys):
437
            sha1 = sha1s[item_key]
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
438
            parents = [key[-1] for key in parent_map[item_key]]
2520.4.41 by Aaron Bentley
Accelerate mpdiff generation
439
            text = ''.join(mpdiff.to_patch())
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
440
            # Infer file id records as appropriate.
441
            if len(item_key) == 2:
442
                file_id = item_key[0]
443
            else:
444
                file_id = None
2520.4.60 by Aaron Bentley
Add sha1 verification for mpdiffs
445
            self.bundle.add_multiparent_record(text, sha1, parents, repo_kind,
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
446
                                               item_key[-1], file_id)
2520.4.6 by Aaron Bentley
Get installation started
447
448
2520.4.72 by Aaron Bentley
Rename format to 4alpha
449
class BundleInfoV4(object):
2520.4.6 by Aaron Bentley
Get installation started
450
2520.4.118 by Aaron Bentley
Add docs
451
    """Provide (most of) the BundleInfo interface"""
2520.4.6 by Aaron Bentley
Get installation started
452
    def __init__(self, fileobj, serializer):
2520.4.14 by Aaron Bentley
Get most tests passing, use format header
453
        self._fileobj = fileobj
454
        self._serializer = serializer
455
        self.__real_revisions = None
456
        self.__revisions = None
457
458
    def install(self, repository):
459
        return self.install_revisions(repository)
460
2520.4.148 by Aaron Bentley
Updates from review
461
    def install_revisions(self, repository, stream_input=True):
462
        """Install this bundle's revisions into the specified repository
463
464
        :param target_repo: The repository to install into
465
        :param stream_input: If True, will stream input rather than reading it
466
            all into memory at once.  Reading it into memory all at once is
467
            (currently) faster.
468
        """
2520.4.18 by Aaron Bentley
Generate mpdiffs for inventory
469
        repository.lock_write()
470
        try:
2520.4.148 by Aaron Bentley
Updates from review
471
            ri = RevisionInstaller(self.get_bundle_reader(stream_input),
2520.4.21 by Aaron Bentley
Finish turning ContainerWriter into a new layer
472
                                   self._serializer, repository)
2520.4.18 by Aaron Bentley
Generate mpdiffs for inventory
473
            return ri.install()
474
        finally:
475
            repository.unlock()
2520.4.14 by Aaron Bentley
Get most tests passing, use format header
476
2520.4.109 by Aaron Bentley
start work on directive cherry-picking
477
    def get_merge_request(self, target_repo):
478
        """Provide data for performing a merge
479
480
        Returns suggested base, suggested target, and patch verification status
481
        """
482
        return None, self.target, 'inapplicable'
483
2520.4.148 by Aaron Bentley
Updates from review
484
    def get_bundle_reader(self, stream_input=True):
485
        """Return a new BundleReader for the associated bundle
486
487
        :param stream_input: If True, the BundleReader stream input rather than
488
            reading it all into memory at once.  Reading it into memory all at
489
            once is (currently) faster.
490
        """
2520.4.21 by Aaron Bentley
Finish turning ContainerWriter into a new layer
491
        self._fileobj.seek(0)
2520.4.148 by Aaron Bentley
Updates from review
492
        return BundleReader(self._fileobj, stream_input)
2520.4.21 by Aaron Bentley
Finish turning ContainerWriter into a new layer
493
2520.4.14 by Aaron Bentley
Get most tests passing, use format header
494
    def _get_real_revisions(self):
495
        if self.__real_revisions is None:
496
            self.__real_revisions = []
2520.4.39 by Aaron Bentley
Rename container => bundle(reader) where appropriate
497
            bundle_reader = self.get_bundle_reader()
2520.4.102 by Aaron Bentley
rename parents to metadata
498
            for bytes, metadata, repo_kind, revision_id, file_id in \
2520.4.39 by Aaron Bentley
Rename container => bundle(reader) where appropriate
499
                bundle_reader.iter_records():
2520.4.101 by Aaron Bentley
Use a registry to look up xml serializers by format
500
                if repo_kind == 'info':
501
                    serializer =\
2520.4.102 by Aaron Bentley
rename parents to metadata
502
                        self._serializer.get_source_serializer(metadata)
2520.4.22 by Aaron Bentley
Create ContainerReader
503
                if repo_kind == 'revision':
2520.4.101 by Aaron Bentley
Use a registry to look up xml serializers by format
504
                    rev = serializer.read_revision_from_string(bytes)
2520.4.14 by Aaron Bentley
Get most tests passing, use format header
505
                    self.__real_revisions.append(rev)
506
        return self.__real_revisions
507
    real_revisions = property(_get_real_revisions)
508
509
    def _get_revisions(self):
510
        if self.__revisions is None:
511
            self.__revisions = []
512
            for revision in self.real_revisions:
2520.4.33 by Aaron Bentley
remove test dependencies on serialization minutia
513
                self.__revisions.append(
514
                    bundle_data.RevisionInfo.from_revision(revision))
2520.4.14 by Aaron Bentley
Get most tests passing, use format header
515
        return self.__revisions
516
517
    revisions = property(_get_revisions)
518
2520.4.29 by Aaron Bentley
Reactivate some testing, fix topo_iter
519
    def _get_target(self):
520
        return self.revisions[-1].revision_id
521
522
    target = property(_get_target)
523
2520.4.14 by Aaron Bentley
Get most tests passing, use format header
524
525
class RevisionInstaller(object):
2520.4.123 by Aaron Bentley
Cleanup of bundle code
526
    """Installs revisions into a repository"""
2520.4.14 by Aaron Bentley
Get most tests passing, use format header
527
2520.4.21 by Aaron Bentley
Finish turning ContainerWriter into a new layer
528
    def __init__(self, container, serializer, repository):
529
        self._container = container
2520.4.6 by Aaron Bentley
Get installation started
530
        self._serializer = serializer
2520.4.14 by Aaron Bentley
Get most tests passing, use format header
531
        self._repository = repository
2520.4.97 by Aaron Bentley
Hack in support for inventory conversion
532
        self._info = None
2520.4.99 by Aaron Bentley
Test conversion across models
533
2520.4.14 by Aaron Bentley
Get most tests passing, use format header
534
    def install(self):
2592.4.1 by Martin Pool
RevisionInstaller now creates a write group for its work
535
        """Perform the installation.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
536
2592.4.1 by Martin Pool
RevisionInstaller now creates a write group for its work
537
        Must be called with the Repository locked.
538
        """
539
        self._repository.start_write_group()
540
        try:
2856.1.2 by Robert Collins
Review feedback.
541
            result = self._install_in_write_group()
2592.4.1 by Martin Pool
RevisionInstaller now creates a write group for its work
542
        except:
543
            self._repository.abort_write_group()
544
            raise
545
        self._repository.commit_write_group()
546
        return result
547
2856.1.2 by Robert Collins
Review feedback.
548
    def _install_in_write_group(self):
2520.4.6 by Aaron Bentley
Get installation started
549
        current_file = None
550
        current_versionedfile = None
551
        pending_file_records = []
2520.4.142 by Aaron Bentley
Clean up installation of inventory records
552
        inventory_vf = None
553
        pending_inventory_records = []
2520.4.8 by Aaron Bentley
Serialize inventory
554
        added_inv = set()
2520.4.29 by Aaron Bentley
Reactivate some testing, fix topo_iter
555
        target_revision = None
2520.4.58 by Aaron Bentley
Propogate support for metadata to iter_revisions, add storage kind
556
        for bytes, metadata, repo_kind, revision_id, file_id in\
2520.4.22 by Aaron Bentley
Create ContainerReader
557
            self._container.iter_records():
2520.4.97 by Aaron Bentley
Hack in support for inventory conversion
558
            if repo_kind == 'info':
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
559
                if self._info is not None:
560
                    raise AssertionError()
2520.4.123 by Aaron Bentley
Cleanup of bundle code
561
                self._handle_info(metadata)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
562
            if (pending_file_records and
563
                (repo_kind, file_id) != ('file', current_file)):
564
                # Flush the data for a single file - prevents memory
565
                # spiking due to buffering all files in memory.
566
                self._install_mp_records_keys(self._repository.texts,
567
                    pending_file_records)
2520.4.8 by Aaron Bentley
Serialize inventory
568
                current_file = None
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
569
                del pending_file_records[:]
2520.4.142 by Aaron Bentley
Clean up installation of inventory records
570
            if len(pending_inventory_records) > 0 and repo_kind != 'inventory':
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
571
                self._install_inventory_records(pending_inventory_records)
2520.4.142 by Aaron Bentley
Clean up installation of inventory records
572
                pending_inventory_records = []
573
            if repo_kind == 'inventory':
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
574
                pending_inventory_records.append(((revision_id,), metadata, bytes))
2520.4.142 by Aaron Bentley
Clean up installation of inventory records
575
            if repo_kind == 'revision':
576
                target_revision = revision_id
577
                self._install_revision(revision_id, metadata, bytes)
578
            if repo_kind == 'signature':
579
                self._install_signature(revision_id, metadata, bytes)
2520.4.22 by Aaron Bentley
Create ContainerReader
580
            if repo_kind == 'file':
2520.4.142 by Aaron Bentley
Clean up installation of inventory records
581
                current_file = file_id
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
582
                pending_file_records.append(((file_id, revision_id), metadata, bytes))
583
        self._install_mp_records_keys(self._repository.texts, pending_file_records)
2520.4.14 by Aaron Bentley
Get most tests passing, use format header
584
        return target_revision
2520.4.6 by Aaron Bentley
Get installation started
585
2520.4.123 by Aaron Bentley
Cleanup of bundle code
586
    def _handle_info(self, info):
587
        """Extract data from an info record"""
588
        self._info = info
589
        self._source_serializer = self._serializer.get_source_serializer(info)
590
        if (info['supports_rich_root'] == 0 and
591
            self._repository.supports_rich_root()):
592
            self.update_root = True
593
        else:
594
            self.update_root = False
595
2520.4.60 by Aaron Bentley
Add sha1 verification for mpdiffs
596
    def _install_mp_records(self, versionedfile, records):
2520.4.61 by Aaron Bentley
Do bulk insertion of records
597
        if len(records) == 0:
598
            return
599
        d_func = multiparent.MultiParent.from_patch
600
        vf_records = [(r, m['parents'], m['sha1'], d_func(t)) for r, m, t in
601
                      records if r not in versionedfile]
602
        versionedfile.add_mpdiffs(vf_records)
2520.4.8 by Aaron Bentley
Serialize inventory
603
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
604
    def _install_mp_records_keys(self, versionedfile, records):
605
        d_func = multiparent.MultiParent.from_patch
606
        vf_records = []
607
        for key, meta, text in records:
3350.6.7 by Robert Collins
Review feedback, making things more clear, adding documentation on what is used where.
608
            # Adapt to tuple interface: A length two key is a file_id,
609
            # revision_id pair, a length 1 key is a
610
            # revision/signature/inventory. We need to do this because
611
            # the metadata extraction from the bundle has not yet been updated
612
            # to use the consistent tuple interface itself.
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
613
            if len(key) == 2:
614
                prefix = key[:1]
615
            else:
616
                prefix = ()
617
            parents = [prefix + (parent,) for parent in meta['parents']]
618
            vf_records.append((key, parents, meta['sha1'], d_func(text)))
619
        versionedfile.add_mpdiffs(vf_records)
620
4543.2.17 by John Arbash Meinel
Adding a parent inventory cache, and then using add_inventory_by_delta.
621
    def _get_parent_inventory_texts(self, inventory_text_cache,
622
                                    inventory_cache, parent_ids):
4543.2.16 by John Arbash Meinel
Adding an inventory text cache.
623
        cached_parent_texts = {}
624
        remaining_parent_ids = []
625
        for parent_id in parent_ids:
626
            p_text = inventory_text_cache.get(parent_id, None)
627
            if p_text is None:
628
                remaining_parent_ids.append(parent_id)
629
            else:
630
                cached_parent_texts[parent_id] = p_text
631
        ghosts = ()
4543.2.17 by John Arbash Meinel
Adding a parent inventory cache, and then using add_inventory_by_delta.
632
        # TODO: Use inventory_cache to grab inventories we already have in
633
        #       memory
4543.2.16 by John Arbash Meinel
Adding an inventory text cache.
634
        if remaining_parent_ids:
635
            # first determine what keys are actually present in the local
636
            # inventories object (don't use revisions as they haven't been
637
            # installed yet.)
638
            parent_keys = [(r,) for r in remaining_parent_ids]
639
            present_parent_map = self._repository.inventories.get_parent_map(
640
                                        parent_keys)
641
            present_parent_ids = []
642
            ghosts = set()
643
            for p_id in remaining_parent_ids:
644
                if (p_id,) in present_parent_map:
645
                    present_parent_ids.append(p_id)
646
                else:
647
                    ghosts.add(p_id)
648
            to_string = self._source_serializer.write_inventory_to_string
649
            for parent_inv in self._repository.iter_inventories(
650
                                    present_parent_ids):
651
                p_text = to_string(parent_inv)
4543.2.17 by John Arbash Meinel
Adding a parent inventory cache, and then using add_inventory_by_delta.
652
                inventory_cache[parent_inv.revision_id] = parent_inv
4543.2.16 by John Arbash Meinel
Adding an inventory text cache.
653
                cached_parent_texts[parent_inv.revision_id] = p_text
654
                inventory_text_cache[parent_inv.revision_id] = p_text
655
656
        parent_texts = [cached_parent_texts[parent_id]
657
                        for parent_id in parent_ids
658
                         if parent_id not in ghosts]
659
        return parent_texts
660
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
661
    def _install_inventory_records(self, records):
4543.2.3 by John Arbash Meinel
Change the name to test_merge_directive
662
        if (self._info['serializer'] == self._repository._serializer.format_num
663
            and self._repository._serializer.support_altered_by_hack):
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
664
            return self._install_mp_records_keys(self._repository.inventories,
665
                records)
4543.2.16 by John Arbash Meinel
Adding an inventory text cache.
666
        # Use a 10MB text cache, since these are string xml inventories. Note
667
        # that 10MB is fairly small for large projects (a single inventory can
668
        # be >5MB). Another possibility is to cache 10-20 inventory texts
669
        # instead
670
        inventory_text_cache = lru_cache.LRUSizeCache(10*1024*1024)
4543.2.21 by John Arbash Meinel
A few more tiny tweaks to comments, etc.
671
        # Also cache the in-memory representation. This allows us to create
672
        # inventory deltas to apply rather than calling add_inventory from
673
        # scratch each time.
4543.2.17 by John Arbash Meinel
Adding a parent inventory cache, and then using add_inventory_by_delta.
674
        inventory_cache = lru_cache.LRUCache(10)
4543.2.16 by John Arbash Meinel
Adding an inventory text cache.
675
        pb = ui.ui_factory.nested_progress_bar()
676
        try:
677
            num_records = len(records)
678
            for idx, (key, metadata, bytes) in enumerate(records):
679
                pb.update('installing inventory', idx, num_records)
680
                revision_id = key[-1]
681
                parent_ids = metadata['parents']
682
                # Note: This assumes the local ghosts are identical to the
683
                #       ghosts in the source, as the Bundle serialization
684
                #       format doesn't record ghosts.
685
                p_texts = self._get_parent_inventory_texts(inventory_text_cache,
4543.2.17 by John Arbash Meinel
Adding a parent inventory cache, and then using add_inventory_by_delta.
686
                                                           inventory_cache,
4543.2.16 by John Arbash Meinel
Adding an inventory text cache.
687
                                                           parent_ids)
688
                # Why does to_lines() take strings as the source, it seems that
689
                # it would have to cast to a list of lines, which we get back
690
                # as lines and then cast back to a string.
691
                target_lines = multiparent.MultiParent.from_patch(bytes
692
                            ).to_lines(p_texts)
693
                inv_text = ''.join(target_lines)
694
                del target_lines
695
                sha1 = osutils.sha_string(inv_text)
696
                if sha1 != metadata['sha1']:
697
                    raise errors.BadBundle("Can't convert to target format")
698
                # Add this to the cache so we don't have to extract it again.
699
                inventory_text_cache[revision_id] = inv_text
700
                target_inv = self._source_serializer.read_inventory_from_string(
701
                    inv_text)
702
                self._handle_root(target_inv, parent_ids)
4543.2.17 by John Arbash Meinel
Adding a parent inventory cache, and then using add_inventory_by_delta.
703
                parent_inv = None
704
                if parent_ids:
705
                    parent_inv = inventory_cache.get(parent_ids[0], None)
4543.2.16 by John Arbash Meinel
Adding an inventory text cache.
706
                try:
4543.2.17 by John Arbash Meinel
Adding a parent inventory cache, and then using add_inventory_by_delta.
707
                    if parent_inv is None:
708
                        self._repository.add_inventory(revision_id, target_inv,
709
                                                       parent_ids)
710
                    else:
711
                        delta = target_inv._make_delta(parent_inv)
712
                        self._repository.add_inventory_by_delta(parent_ids[0],
713
                            delta, revision_id, parent_ids)
4543.2.16 by John Arbash Meinel
Adding an inventory text cache.
714
                except errors.UnsupportedInventoryKind:
715
                    raise errors.IncompatibleRevision(repr(self._repository))
4543.2.17 by John Arbash Meinel
Adding a parent inventory cache, and then using add_inventory_by_delta.
716
                inventory_cache[revision_id] = target_inv
4543.2.16 by John Arbash Meinel
Adding an inventory text cache.
717
        finally:
718
            pb.finished()
2520.4.99 by Aaron Bentley
Test conversion across models
719
720
    def _handle_root(self, target_inv, parent_ids):
721
        revision_id = target_inv.revision_id
722
        if self.update_root:
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
723
            text_key = (target_inv.root.file_id, revision_id)
724
            parent_keys = [(target_inv.root.file_id, parent) for
725
                parent in parent_ids]
726
            self._repository.texts.add_lines(text_key, parent_keys, [])
2520.4.99 by Aaron Bentley
Test conversion across models
727
        elif not self._repository.supports_rich_root():
728
            if target_inv.root.revision != revision_id:
729
                raise errors.IncompatibleRevision(repr(self._repository))
730
2520.4.59 by Aaron Bentley
Push metadata down the stack
731
    def _install_revision(self, revision_id, metadata, text):
2520.4.14 by Aaron Bentley
Get most tests passing, use format header
732
        if self._repository.has_revision(revision_id):
733
            return
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
734
        revision = self._source_serializer.read_revision_from_string(text)
735
        self._repository.add_revision(revision.revision_id, revision)
2520.4.34 by Aaron Bentley
Add signature support
736
2520.4.59 by Aaron Bentley
Push metadata down the stack
737
    def _install_signature(self, revision_id, metadata, text):
2520.4.100 by Aaron Bentley
Fix repeat signature installs
738
        transaction = self._repository.get_transaction()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
739
        if self._repository.has_signature_for_revision_id(revision_id):
2520.4.100 by Aaron Bentley
Fix repeat signature installs
740
            return
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
741
        self._repository.add_signature_text(revision_id, text)