/brz/remove-bazaar

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