2520.4.85
by Aaron Bentley
Get all test passing (which just proves there aren't enough tests!) |
1 |
# Copyright (C) 2007 Canonical Ltd
|
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
|
|
15 |
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
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, |
2520.4.13
by Aaron Bentley
Use real container implementation |
25 |
multiparent, |
2520.4.97
by Aaron Bentley
Hack in support for inventory conversion |
26 |
osutils, |
2520.4.13
by Aaron Bentley
Use real container implementation |
27 |
pack, |
2520.4.40
by Aaron Bentley
Add human-readable diff to bundles |
28 |
revision as _mod_revision, |
2520.4.45
by Aaron Bentley
Handle inconsistencies in last-modified-revision between vf and inventory |
29 |
trace, |
2520.4.101
by Aaron Bentley
Use a registry to look up xml serializers by format |
30 |
xml_serializer, |
2520.4.13
by Aaron Bentley
Use real container implementation |
31 |
)
|
2520.4.14
by Aaron Bentley
Get most tests passing, use format header |
32 |
from bzrlib.bundle import bundle_data, serializer |
2520.4.56
by Aaron Bentley
Begin adding support for arbitrary metadata |
33 |
from bzrlib.util import bencode |
2520.4.14
by Aaron Bentley
Get most tests passing, use format header |
34 |
|
2520.4.4
by Aaron Bentley
Get basis support for a new bundle format in place |
35 |
|
2520.4.25
by Aaron Bentley
Rename ContainerWriter/ContainerReader to BundleWriter/BundleReader |
36 |
class BundleWriter(object): |
2520.4.118
by Aaron Bentley
Add docs |
37 |
"""Writer for bundle-format files.
|
38 |
||
39 |
This serves roughly the same purpose as ContainerReader, but acts as a
|
|
40 |
layer on top of it.
|
|
41 |
||
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
42 |
Provides ways of writing the specific record types supported this bundle
|
2520.4.118
by Aaron Bentley
Add docs |
43 |
format.
|
44 |
"""
|
|
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
45 |
|
2520.4.23
by Aaron Bentley
Move responsability for encoding into container objects |
46 |
def __init__(self, fileobj): |
2520.4.27
by Aaron Bentley
Use less memory when writing bzip-encoded files |
47 |
self._container = pack.ContainerWriter(self._write_encoded) |
2520.4.23
by Aaron Bentley
Move responsability for encoding into container objects |
48 |
self._fileobj = fileobj |
2520.4.27
by Aaron Bentley
Use less memory when writing bzip-encoded files |
49 |
self._compressor = bz2.BZ2Compressor() |
2520.4.21
by Aaron Bentley
Finish turning ContainerWriter into a new layer |
50 |
|
2520.4.118
by Aaron Bentley
Add docs |
51 |
def _write_encoded(self, bytes): |
52 |
"""Write bzip2-encoded bytes to the file"""
|
|
53 |
self._fileobj.write(self._compressor.compress(bytes)) |
|
54 |
||
2520.4.21
by Aaron Bentley
Finish turning ContainerWriter into a new layer |
55 |
def begin(self): |
2520.4.118
by Aaron Bentley
Add docs |
56 |
"""Start writing the bundle"""
|
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
57 |
self._fileobj.write(serializer._get_bundle_header( |
58 |
serializer.v4_string)) |
|
2520.4.24
by Aaron Bentley
Move heading writing above container beginning |
59 |
self._fileobj.write('#\n') |
2520.4.21
by Aaron Bentley
Finish turning ContainerWriter into a new layer |
60 |
self._container.begin() |
61 |
||
62 |
def end(self): |
|
2520.4.118
by Aaron Bentley
Add docs |
63 |
"""Finish writing the bundle"""
|
2520.4.21
by Aaron Bentley
Finish turning ContainerWriter into a new layer |
64 |
self._container.end() |
2520.4.76
by Aaron Bentley
Move base64-encoding into merge directives |
65 |
self._fileobj.write(self._compressor.flush()) |
2520.4.21
by Aaron Bentley
Finish turning ContainerWriter into a new layer |
66 |
|
2520.4.60
by Aaron Bentley
Add sha1 verification for mpdiffs |
67 |
def add_multiparent_record(self, mp_bytes, sha1, parents, repo_kind, |
2520.4.21
by Aaron Bentley
Finish turning ContainerWriter into a new layer |
68 |
revision_id, file_id): |
2520.4.118
by Aaron Bentley
Add docs |
69 |
"""Add a record for a multi-parent diff
|
70 |
||
71 |
:mp_bytes: A multi-parent diff, as a bytestring
|
|
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
72 |
:sha1: The sha1 hash of the fulltext
|
2520.4.118
by Aaron Bentley
Add docs |
73 |
:parents: a list of revision-ids of the parents
|
74 |
:repo_kind: The kind of object in the repository. May be 'file' or
|
|
75 |
'inventory'
|
|
76 |
:revision_id: The revision id of the mpdiff being added.
|
|
77 |
:file_id: The file-id of the file, or None for inventories.
|
|
78 |
"""
|
|
2520.4.60
by Aaron Bentley
Add sha1 verification for mpdiffs |
79 |
metadata = {'parents': parents, |
80 |
'storage_kind': 'mpdiff', |
|
81 |
'sha1': sha1} |
|
82 |
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 |
83 |
|
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
84 |
def add_fulltext_record(self, bytes, parents, repo_kind, revision_id): |
2520.4.118
by Aaron Bentley
Add docs |
85 |
"""Add a record for a fulltext
|
86 |
||
87 |
:bytes: The fulltext, as a bytestring
|
|
88 |
:parents: a list of revision-ids of the parents
|
|
89 |
:repo_kind: The kind of object in the repository. May be 'revision' or
|
|
90 |
'signature'
|
|
91 |
:revision_id: The revision id of the fulltext being added.
|
|
92 |
"""
|
|
93 |
metadata = {'parents': parents, |
|
2520.5.3
by Aaron Bentley
fix sha1 in bundle format 4 |
94 |
'storage_kind': 'mpdiff'} |
2520.4.60
by Aaron Bentley
Add sha1 verification for mpdiffs |
95 |
self._add_record(bytes, {'parents': parents, |
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
96 |
'storage_kind': 'fulltext'}, repo_kind, revision_id, None) |
2520.4.21
by Aaron Bentley
Finish turning ContainerWriter into a new layer |
97 |
|
2520.4.95
by Aaron Bentley
Add support for header/info records |
98 |
def add_info_record(self, **kwargs): |
2520.4.118
by Aaron Bentley
Add docs |
99 |
"""Add an info record to the bundle
|
100 |
||
101 |
Any parameters may be supplied, except 'self' and 'storage_kind'.
|
|
102 |
Values must be lists, strings, integers, dicts, or a combination.
|
|
103 |
"""
|
|
2520.4.95
by Aaron Bentley
Add support for header/info records |
104 |
kwargs['storage_kind'] = 'header' |
105 |
self._add_record(None, kwargs, 'info', None, None) |
|
106 |
||
2520.4.21
by Aaron Bentley
Finish turning ContainerWriter into a new layer |
107 |
@staticmethod
|
2520.4.68
by Aaron Bentley
Change name separators to all-slash |
108 |
def encode_name(content_kind, revision_id, file_id=None): |
2520.4.118
by Aaron Bentley
Add docs |
109 |
"""Encode semantic ids as a container name"""
|
2520.4.95
by Aaron Bentley
Add support for header/info records |
110 |
assert content_kind in ('revision', 'file', 'inventory', 'signature', |
111 |
'info') |
|
2520.4.118
by Aaron Bentley
Add docs |
112 |
|
113 |
if content_kind == 'file': |
|
114 |
assert file_id is not None |
|
115 |
else: |
|
2520.4.21
by Aaron Bentley
Finish turning ContainerWriter into a new layer |
116 |
assert file_id is None |
2520.4.95
by Aaron Bentley
Add support for header/info records |
117 |
if content_kind == 'info': |
118 |
assert revision_id is None |
|
119 |
else: |
|
120 |
assert revision_id is not None |
|
2520.4.127
by Aaron Bentley
Fix up name encoding to handle revision-ids with slashes |
121 |
names = [n.replace('/', '//') for n in |
122 |
(content_kind, revision_id, file_id) if n is not None] |
|
2520.4.68
by Aaron Bentley
Change name separators to all-slash |
123 |
return '/'.join(names) |
2520.4.21
by Aaron Bentley
Finish turning ContainerWriter into a new layer |
124 |
|
2520.4.56
by Aaron Bentley
Begin adding support for arbitrary metadata |
125 |
def _add_record(self, bytes, metadata, repo_kind, revision_id, file_id): |
2520.4.118
by Aaron Bentley
Add docs |
126 |
"""Add a bundle record to the container.
|
127 |
||
128 |
Most bundle records are recorded as header/body pairs, with the
|
|
129 |
body being nameless. Records with storage_kind 'header' have no
|
|
130 |
body.
|
|
131 |
"""
|
|
2520.4.21
by Aaron Bentley
Finish turning ContainerWriter into a new layer |
132 |
name = self.encode_name(repo_kind, revision_id, file_id) |
2520.4.95
by Aaron Bentley
Add support for header/info records |
133 |
encoded_metadata = bencode.bencode(metadata) |
2682.1.1
by Robert Collins
* The ``bzrlib.pack`` interface has changed to use tuples of bytestrings |
134 |
self._container.add_bytes_record(encoded_metadata, [(name, )]) |
2520.4.95
by Aaron Bentley
Add support for header/info records |
135 |
if metadata['storage_kind'] != 'header': |
136 |
self._container.add_bytes_record(bytes, []) |
|
2520.4.13
by Aaron Bentley
Use real container implementation |
137 |
|
2520.4.7
by Aaron Bentley
Fix patch deserialization |
138 |
|
2520.4.25
by Aaron Bentley
Rename ContainerWriter/ContainerReader to BundleWriter/BundleReader |
139 |
class BundleReader(object): |
2520.4.118
by Aaron Bentley
Add docs |
140 |
"""Reader for bundle-format files.
|
141 |
||
142 |
This serves roughly the same purpose as ContainerReader, but acts as a
|
|
143 |
layer on top of it, providing metadata, a semantic name, and a record
|
|
144 |
body
|
|
145 |
"""
|
|
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
146 |
|
2520.4.148
by Aaron Bentley
Updates from review |
147 |
def __init__(self, fileobj, stream_input=True): |
2520.4.145
by Aaron Bentley
Add memory_friendly toggle, be memory-unfriendly for merge directives |
148 |
"""Constructor
|
149 |
||
150 |
:param fileobj: a file containing a bzip-encoded container
|
|
2520.4.148
by Aaron Bentley
Updates from review |
151 |
:param stream_input: If True, the BundleReader stream input rather than
|
152 |
reading it all into memory at once. Reading it into memory all at
|
|
153 |
once is (currently) faster.
|
|
2520.4.145
by Aaron Bentley
Add memory_friendly toggle, be memory-unfriendly for merge directives |
154 |
"""
|
2520.4.23
by Aaron Bentley
Move responsability for encoding into container objects |
155 |
line = fileobj.readline() |
156 |
if line != '\n': |
|
157 |
fileobj.readline() |
|
2520.4.40
by Aaron Bentley
Add human-readable diff to bundles |
158 |
self.patch_lines = [] |
2520.4.148
by Aaron Bentley
Updates from review |
159 |
if stream_input: |
2520.4.145
by Aaron Bentley
Add memory_friendly toggle, be memory-unfriendly for merge directives |
160 |
source_file = iterablefile.IterableFile(self.iter_decode(fileobj)) |
161 |
else: |
|
162 |
source_file = StringIO(bz2.decompress(fileobj.read())) |
|
163 |
self._container = pack.ContainerReader(source_file) |
|
2520.4.26
by Aaron Bentley
Make decompression reasonably memory-efficient |
164 |
|
165 |
@staticmethod
|
|
166 |
def iter_decode(fileobj): |
|
2520.4.118
by Aaron Bentley
Add docs |
167 |
"""Iterate through decoded fragments of the file"""
|
2520.4.26
by Aaron Bentley
Make decompression reasonably memory-efficient |
168 |
decompressor = bz2.BZ2Decompressor() |
169 |
for line in fileobj: |
|
2520.4.117
by Aaron Bentley
Update for new pack interface |
170 |
yield decompressor.decompress(line) |
2520.4.22
by Aaron Bentley
Create ContainerReader |
171 |
|
172 |
@staticmethod
|
|
173 |
def decode_name(name): |
|
2520.4.118
by Aaron Bentley
Add docs |
174 |
"""Decode a name from its container form into a semantic form
|
175 |
||
176 |
:retval: content_kind, revision_id, file_id
|
|
177 |
"""
|
|
2520.4.130
by Aaron Bentley
Finish tweaking decode_name |
178 |
segments = re.split('(//?)', name) |
179 |
names = [''] |
|
2520.4.127
by Aaron Bentley
Fix up name encoding to handle revision-ids with slashes |
180 |
for segment in segments: |
181 |
if segment == '//': |
|
182 |
names[-1] += '/' |
|
2520.4.130
by Aaron Bentley
Finish tweaking decode_name |
183 |
elif segment == '/': |
2520.4.127
by Aaron Bentley
Fix up name encoding to handle revision-ids with slashes |
184 |
names.append('') |
185 |
else: |
|
186 |
names[-1] += segment |
|
2520.4.130
by Aaron Bentley
Finish tweaking decode_name |
187 |
content_kind = names[0] |
2520.4.95
by Aaron Bentley
Add support for header/info records |
188 |
revision_id = None |
189 |
file_id = None |
|
190 |
if len(names) > 1: |
|
191 |
revision_id = names[1] |
|
2520.4.68
by Aaron Bentley
Change name separators to all-slash |
192 |
if len(names) > 2: |
193 |
file_id = names[2] |
|
194 |
return content_kind, revision_id, file_id |
|
2520.4.22
by Aaron Bentley
Create ContainerReader |
195 |
|
196 |
def iter_records(self): |
|
2520.4.118
by Aaron Bentley
Add docs |
197 |
"""Iterate through bundle records
|
198 |
||
199 |
:return: a generator of (bytes, metadata, content_kind, revision_id,
|
|
200 |
file_id)
|
|
201 |
"""
|
|
2520.4.69
by Aaron Bentley
Simplify encoding by storing bodies in anonymous records |
202 |
iterator = self._container.iter_records() |
2520.4.131
by Aaron Bentley
Raise BadBundle for records with wrong number of names |
203 |
for names, meta_bytes in iterator: |
204 |
if len(names) != 1: |
|
205 |
raise errors.BadBundle('Record has %d names instead of 1' |
|
206 |
% len(names)) |
|
2520.4.69
by Aaron Bentley
Simplify encoding by storing bodies in anonymous records |
207 |
metadata = bencode.bdecode(meta_bytes(None)) |
2520.4.95
by Aaron Bentley
Add support for header/info records |
208 |
if metadata['storage_kind'] == 'header': |
209 |
bytes = None |
|
210 |
else: |
|
211 |
_unused, bytes = iterator.next() |
|
212 |
bytes = bytes(None) |
|
2682.1.1
by Robert Collins
* The ``bzrlib.pack`` interface has changed to use tuples of bytestrings |
213 |
yield (bytes, metadata) + self.decode_name(names[0][0]) |
2520.4.22
by Aaron Bentley
Create ContainerReader |
214 |
|
215 |
||
2520.4.72
by Aaron Bentley
Rename format to 4alpha |
216 |
class BundleSerializerV4(serializer.BundleSerializer): |
2520.4.118
by Aaron Bentley
Add docs |
217 |
"""Implement the high-level bundle interface"""
|
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
218 |
|
2520.4.4
by Aaron Bentley
Get basis support for a new bundle format in place |
219 |
def write(self, repository, revision_ids, forced_bases, fileobj): |
2520.4.118
by Aaron Bentley
Add docs |
220 |
"""Write a bundle to a file-like object
|
221 |
||
222 |
For backwards-compatibility only
|
|
223 |
"""
|
|
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
224 |
write_op = BundleWriteOperation.from_old_args(repository, revision_ids, |
225 |
forced_bases, fileobj) |
|
2520.4.53
by Aaron Bentley
refactor bundle serialization to make write_bundle primary |
226 |
return write_op.do_write() |
227 |
||
228 |
def write_bundle(self, repository, target, base, fileobj): |
|
2520.4.118
by Aaron Bentley
Add docs |
229 |
"""Write a bundle to a file object
|
230 |
||
231 |
:param repository: The repository to retrieve revision data from
|
|
232 |
:param target: The head revision to include ancestors of
|
|
233 |
:param base: The ancestor of the target to stop including acestors
|
|
234 |
at.
|
|
235 |
:param fileobj: The file-like object to write to
|
|
236 |
"""
|
|
2520.4.53
by Aaron Bentley
refactor bundle serialization to make write_bundle primary |
237 |
write_op = BundleWriteOperation(base, target, repository, fileobj) |
238 |
return write_op.do_write() |
|
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
239 |
|
240 |
def read(self, file): |
|
2520.4.118
by Aaron Bentley
Add docs |
241 |
"""return a reader object for a given file"""
|
2520.4.72
by Aaron Bentley
Rename format to 4alpha |
242 |
bundle = BundleInfoV4(file, self) |
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
243 |
return bundle |
244 |
||
2520.4.101
by Aaron Bentley
Use a registry to look up xml serializers by format |
245 |
@staticmethod
|
246 |
def get_source_serializer(info): |
|
2520.4.118
by Aaron Bentley
Add docs |
247 |
"""Retrieve the serializer for a given info object"""
|
2520.4.101
by Aaron Bentley
Use a registry to look up xml serializers by format |
248 |
return xml_serializer.format_registry.get(info['serializer']) |
249 |
||
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
250 |
|
251 |
class BundleWriteOperation(object): |
|
2520.4.118
by Aaron Bentley
Add docs |
252 |
"""Perform the operation of writing revisions to a bundle"""
|
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
253 |
|
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
254 |
@classmethod
|
255 |
def from_old_args(cls, repository, revision_ids, forced_bases, fileobj): |
|
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
256 |
"""Create a BundleWriteOperation from old-style arguments"""
|
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
257 |
base, target = cls.get_base_target(revision_ids, forced_bases, |
258 |
repository) |
|
259 |
return BundleWriteOperation(base, target, repository, fileobj, |
|
260 |
revision_ids) |
|
261 |
||
2520.4.53
by Aaron Bentley
refactor bundle serialization to make write_bundle primary |
262 |
def __init__(self, base, target, repository, fileobj, revision_ids=None): |
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
263 |
self.base = base |
264 |
self.target = target |
|
265 |
self.repository = repository |
|
2520.4.39
by Aaron Bentley
Rename container => bundle(reader) where appropriate |
266 |
bundle = BundleWriter(fileobj) |
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
267 |
self.bundle = bundle |
2520.4.64
by Aaron Bentley
Avoid topo sort for v10 bundles |
268 |
self.base_ancestry = set(repository.get_ancestry(base, |
269 |
topo_sorted=False)) |
|
2520.4.53
by Aaron Bentley
refactor bundle serialization to make write_bundle primary |
270 |
if revision_ids is not None: |
271 |
self.revision_ids = revision_ids |
|
272 |
else: |
|
2520.4.64
by Aaron Bentley
Avoid topo sort for v10 bundles |
273 |
revision_ids = set(repository.get_ancestry(target, |
274 |
topo_sorted=False)) |
|
2520.4.55
by Aaron Bentley
Fix file revision selection to grab all dependencies properly |
275 |
self.revision_ids = revision_ids.difference(self.base_ancestry) |
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
276 |
|
277 |
def do_write(self): |
|
2520.4.118
by Aaron Bentley
Add docs |
278 |
"""Write all data to the bundle"""
|
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
279 |
self.bundle.begin() |
2520.4.97
by Aaron Bentley
Hack in support for inventory conversion |
280 |
self.write_info() |
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
281 |
self.write_files() |
282 |
self.write_revisions() |
|
283 |
self.bundle.end() |
|
2520.4.53
by Aaron Bentley
refactor bundle serialization to make write_bundle primary |
284 |
return self.revision_ids |
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
285 |
|
2520.4.97
by Aaron Bentley
Hack in support for inventory conversion |
286 |
def write_info(self): |
2520.4.118
by Aaron Bentley
Add docs |
287 |
"""Write format info"""
|
2520.4.113
by Aaron Bentley
Avoid peeking at Repository._serializer |
288 |
serializer_format = self.repository.get_serializer_format() |
2520.4.99
by Aaron Bentley
Test conversion across models |
289 |
supports_rich_root = {True: 1, False: 0}[ |
290 |
self.repository.supports_rich_root()] |
|
2520.4.113
by Aaron Bentley
Avoid peeking at Repository._serializer |
291 |
self.bundle.add_info_record(serializer=serializer_format, |
2520.4.99
by Aaron Bentley
Test conversion across models |
292 |
supports_rich_root=supports_rich_root) |
2520.4.97
by Aaron Bentley
Hack in support for inventory conversion |
293 |
|
2520.4.51
by Aaron Bentley
Split iteration through file revisions into a method, so we can vary it |
294 |
def iter_file_revisions(self): |
2520.4.118
by Aaron Bentley
Add docs |
295 |
"""Iterate through all relevant revisions of all files.
|
2520.4.51
by Aaron Bentley
Split iteration through file revisions into a method, so we can vary it |
296 |
|
2520.4.118
by Aaron Bentley
Add docs |
297 |
This is the correct implementation, but is not compatible with bzr.dev,
|
298 |
because certain old revisions were not converted correctly, and have
|
|
299 |
the wrong "revision" marker in inventories.
|
|
2520.4.51
by Aaron Bentley
Split iteration through file revisions into a method, so we can vary it |
300 |
"""
|
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
301 |
transaction = self.repository.get_transaction() |
302 |
altered = self.repository.fileids_altered_by_revision_ids( |
|
303 |
self.revision_ids) |
|
2520.4.6
by Aaron Bentley
Get installation started |
304 |
for file_id, file_revision_ids in altered.iteritems(): |
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
305 |
vf = self.repository.weave_store.get_weave(file_id, transaction) |
2520.4.51
by Aaron Bentley
Split iteration through file revisions into a method, so we can vary it |
306 |
yield vf, file_id, file_revision_ids |
307 |
||
2520.4.55
by Aaron Bentley
Fix file revision selection to grab all dependencies properly |
308 |
def iter_file_revisions_aggressive(self): |
2520.4.118
by Aaron Bentley
Add docs |
309 |
"""Iterate through all relevant revisions of all files.
|
2520.4.55
by Aaron Bentley
Fix file revision selection to grab all dependencies properly |
310 |
|
311 |
This uses the standard iter_file_revisions to determine what revisions
|
|
312 |
are referred to by inventories, but then uses the versionedfile to
|
|
313 |
determine what the build-dependencies of each required revision.
|
|
314 |
||
315 |
All build dependencies which are not ancestors of the base revision
|
|
316 |
are emitted.
|
|
317 |
"""
|
|
318 |
for vf, file_id, file_revision_ids in self.iter_file_revisions(): |
|
319 |
new_revision_ids = set() |
|
320 |
pending = list(file_revision_ids) |
|
321 |
while len(pending) > 0: |
|
322 |
revision_id = pending.pop() |
|
323 |
if revision_id in new_revision_ids: |
|
324 |
continue
|
|
325 |
if revision_id in self.base_ancestry: |
|
326 |
continue
|
|
327 |
new_revision_ids.add(revision_id) |
|
328 |
pending.extend(vf.get_parents(revision_id)) |
|
329 |
yield vf, file_id, new_revision_ids |
|
330 |
||
2520.4.51
by Aaron Bentley
Split iteration through file revisions into a method, so we can vary it |
331 |
def write_files(self): |
2520.4.118
by Aaron Bentley
Add docs |
332 |
"""Write bundle records for all revisions of all files"""
|
2520.4.55
by Aaron Bentley
Fix file revision selection to grab all dependencies properly |
333 |
for vf, file_id, revision_ids in self.iter_file_revisions_aggressive(): |
2520.4.51
by Aaron Bentley
Split iteration through file revisions into a method, so we can vary it |
334 |
self.add_mp_records('file', file_id, vf, revision_ids) |
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
335 |
|
336 |
def write_revisions(self): |
|
2520.4.118
by Aaron Bentley
Add docs |
337 |
"""Write bundle records for all revisions and signatures"""
|
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
338 |
inv_vf = self.repository.get_inventory_weave() |
339 |
revision_order = list(multiparent.topo_iter(inv_vf, self.revision_ids)) |
|
2520.4.75
by Aaron Bentley
Fix traceback on empty bundles. |
340 |
if self.target is not None and self.target in self.revision_ids: |
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
341 |
revision_order.remove(self.target) |
342 |
revision_order.append(self.target) |
|
343 |
self.add_mp_records('inventory', None, inv_vf, revision_order) |
|
2520.4.114
by Aaron Bentley
Avoid deprecated method get_parent_names method |
344 |
parents_list = self.repository.get_parents(revision_order) |
345 |
for parents, revision_id in zip(parents_list, revision_order): |
|
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
346 |
revision_text = self.repository.get_revision_xml(revision_id) |
347 |
self.bundle.add_fulltext_record(revision_text, parents, |
|
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
348 |
'revision', revision_id) |
2520.4.34
by Aaron Bentley
Add signature support |
349 |
try: |
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
350 |
self.bundle.add_fulltext_record( |
351 |
self.repository.get_signature_text( |
|
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
352 |
revision_id), parents, 'signature', revision_id) |
2520.4.34
by Aaron Bentley
Add signature support |
353 |
except errors.NoSuchRevision: |
354 |
pass
|
|
355 |
||
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
356 |
@staticmethod
|
357 |
def get_base_target(revision_ids, forced_bases, repository): |
|
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
358 |
"""Determine the base and target from old-style revision ids"""
|
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
359 |
if len(revision_ids) == 0: |
360 |
return None, None |
|
361 |
target = revision_ids[0] |
|
362 |
base = forced_bases.get(target) |
|
363 |
if base is None: |
|
364 |
parents = repository.get_revision(target).parent_ids |
|
365 |
if len(parents) == 0: |
|
366 |
base = _mod_revision.NULL_REVISION |
|
367 |
else: |
|
368 |
base = parents[0] |
|
369 |
return base, target |
|
370 |
||
371 |
def add_mp_records(self, repo_kind, file_id, vf, revision_ids): |
|
2520.4.118
by Aaron Bentley
Add docs |
372 |
"""Add multi-parent diff records to a bundle"""
|
2520.4.41
by Aaron Bentley
Accelerate mpdiff generation |
373 |
revision_ids = list(multiparent.topo_iter(vf, revision_ids)) |
374 |
mpdiffs = vf.make_mpdiffs(revision_ids) |
|
2520.4.88
by Aaron Bentley
Retrieve all sha1s at once (ftw) |
375 |
sha1s = vf.get_sha1s(revision_ids) |
376 |
for mpdiff, revision_id, sha1, in zip(mpdiffs, revision_ids, sha1s): |
|
2520.4.21
by Aaron Bentley
Finish turning ContainerWriter into a new layer |
377 |
parents = vf.get_parents(revision_id) |
2520.4.41
by Aaron Bentley
Accelerate mpdiff generation |
378 |
text = ''.join(mpdiff.to_patch()) |
2520.4.60
by Aaron Bentley
Add sha1 verification for mpdiffs |
379 |
self.bundle.add_multiparent_record(text, sha1, parents, repo_kind, |
2520.4.50
by Aaron Bentley
Split write functionality out into a separate object |
380 |
revision_id, file_id) |
2520.4.6
by Aaron Bentley
Get installation started |
381 |
|
382 |
||
2520.4.72
by Aaron Bentley
Rename format to 4alpha |
383 |
class BundleInfoV4(object): |
2520.4.6
by Aaron Bentley
Get installation started |
384 |
|
2520.4.118
by Aaron Bentley
Add docs |
385 |
"""Provide (most of) the BundleInfo interface"""
|
2520.4.6
by Aaron Bentley
Get installation started |
386 |
def __init__(self, fileobj, serializer): |
2520.4.14
by Aaron Bentley
Get most tests passing, use format header |
387 |
self._fileobj = fileobj |
388 |
self._serializer = serializer |
|
389 |
self.__real_revisions = None |
|
390 |
self.__revisions = None |
|
391 |
||
392 |
def install(self, repository): |
|
393 |
return self.install_revisions(repository) |
|
394 |
||
2520.4.148
by Aaron Bentley
Updates from review |
395 |
def install_revisions(self, repository, stream_input=True): |
396 |
"""Install this bundle's revisions into the specified repository
|
|
397 |
||
398 |
:param target_repo: The repository to install into
|
|
399 |
:param stream_input: If True, will stream input rather than reading it
|
|
400 |
all into memory at once. Reading it into memory all at once is
|
|
401 |
(currently) faster.
|
|
402 |
"""
|
|
2520.4.18
by Aaron Bentley
Generate mpdiffs for inventory |
403 |
repository.lock_write() |
404 |
try: |
|
2520.4.148
by Aaron Bentley
Updates from review |
405 |
ri = RevisionInstaller(self.get_bundle_reader(stream_input), |
2520.4.21
by Aaron Bentley
Finish turning ContainerWriter into a new layer |
406 |
self._serializer, repository) |
2520.4.18
by Aaron Bentley
Generate mpdiffs for inventory |
407 |
return ri.install() |
408 |
finally: |
|
409 |
repository.unlock() |
|
2520.4.14
by Aaron Bentley
Get most tests passing, use format header |
410 |
|
2520.4.109
by Aaron Bentley
start work on directive cherry-picking |
411 |
def get_merge_request(self, target_repo): |
412 |
"""Provide data for performing a merge
|
|
413 |
||
414 |
Returns suggested base, suggested target, and patch verification status
|
|
415 |
"""
|
|
416 |
return None, self.target, 'inapplicable' |
|
417 |
||
2520.4.148
by Aaron Bentley
Updates from review |
418 |
def get_bundle_reader(self, stream_input=True): |
419 |
"""Return a new BundleReader for the associated bundle
|
|
420 |
||
421 |
:param stream_input: If True, the BundleReader stream input rather than
|
|
422 |
reading it all into memory at once. Reading it into memory all at
|
|
423 |
once is (currently) faster.
|
|
424 |
"""
|
|
2520.4.21
by Aaron Bentley
Finish turning ContainerWriter into a new layer |
425 |
self._fileobj.seek(0) |
2520.4.148
by Aaron Bentley
Updates from review |
426 |
return BundleReader(self._fileobj, stream_input) |
2520.4.21
by Aaron Bentley
Finish turning ContainerWriter into a new layer |
427 |
|
2520.4.14
by Aaron Bentley
Get most tests passing, use format header |
428 |
def _get_real_revisions(self): |
429 |
if self.__real_revisions is None: |
|
430 |
self.__real_revisions = [] |
|
2520.4.39
by Aaron Bentley
Rename container => bundle(reader) where appropriate |
431 |
bundle_reader = self.get_bundle_reader() |
2520.4.102
by Aaron Bentley
rename parents to metadata |
432 |
for bytes, metadata, repo_kind, revision_id, file_id in \ |
2520.4.39
by Aaron Bentley
Rename container => bundle(reader) where appropriate |
433 |
bundle_reader.iter_records(): |
2520.4.101
by Aaron Bentley
Use a registry to look up xml serializers by format |
434 |
if repo_kind == 'info': |
435 |
serializer =\ |
|
2520.4.102
by Aaron Bentley
rename parents to metadata |
436 |
self._serializer.get_source_serializer(metadata) |
2520.4.22
by Aaron Bentley
Create ContainerReader |
437 |
if repo_kind == 'revision': |
2520.4.101
by Aaron Bentley
Use a registry to look up xml serializers by format |
438 |
rev = serializer.read_revision_from_string(bytes) |
2520.4.14
by Aaron Bentley
Get most tests passing, use format header |
439 |
self.__real_revisions.append(rev) |
440 |
return self.__real_revisions |
|
441 |
real_revisions = property(_get_real_revisions) |
|
442 |
||
443 |
def _get_revisions(self): |
|
444 |
if self.__revisions is None: |
|
445 |
self.__revisions = [] |
|
446 |
for revision in self.real_revisions: |
|
2520.4.33
by Aaron Bentley
remove test dependencies on serialization minutia |
447 |
self.__revisions.append( |
448 |
bundle_data.RevisionInfo.from_revision(revision)) |
|
2520.4.14
by Aaron Bentley
Get most tests passing, use format header |
449 |
return self.__revisions |
450 |
||
451 |
revisions = property(_get_revisions) |
|
452 |
||
2520.4.29
by Aaron Bentley
Reactivate some testing, fix topo_iter |
453 |
def _get_target(self): |
454 |
return self.revisions[-1].revision_id |
|
455 |
||
456 |
target = property(_get_target) |
|
457 |
||
2520.4.14
by Aaron Bentley
Get most tests passing, use format header |
458 |
|
459 |
class RevisionInstaller(object): |
|
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
460 |
"""Installs revisions into a repository"""
|
2520.4.14
by Aaron Bentley
Get most tests passing, use format header |
461 |
|
2520.4.21
by Aaron Bentley
Finish turning ContainerWriter into a new layer |
462 |
def __init__(self, container, serializer, repository): |
463 |
self._container = container |
|
2520.4.6
by Aaron Bentley
Get installation started |
464 |
self._serializer = serializer |
2520.4.14
by Aaron Bentley
Get most tests passing, use format header |
465 |
self._repository = repository |
2520.4.97
by Aaron Bentley
Hack in support for inventory conversion |
466 |
self._info = None |
2520.4.99
by Aaron Bentley
Test conversion across models |
467 |
|
2520.4.14
by Aaron Bentley
Get most tests passing, use format header |
468 |
def install(self): |
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
469 |
"""Perform the installation"""
|
2520.4.6
by Aaron Bentley
Get installation started |
470 |
current_file = None |
471 |
current_versionedfile = None |
|
472 |
pending_file_records = [] |
|
2520.4.142
by Aaron Bentley
Clean up installation of inventory records |
473 |
inventory_vf = None |
474 |
pending_inventory_records = [] |
|
2520.4.8
by Aaron Bentley
Serialize inventory |
475 |
added_inv = set() |
2520.4.29
by Aaron Bentley
Reactivate some testing, fix topo_iter |
476 |
target_revision = None |
2520.4.58
by Aaron Bentley
Propogate support for metadata to iter_revisions, add storage kind |
477 |
for bytes, metadata, repo_kind, revision_id, file_id in\ |
2520.4.22
by Aaron Bentley
Create ContainerReader |
478 |
self._container.iter_records(): |
2520.4.97
by Aaron Bentley
Hack in support for inventory conversion |
479 |
if repo_kind == 'info': |
480 |
assert self._info is None |
|
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
481 |
self._handle_info(metadata) |
2520.6.2
by Aaron Bentley
Fix bundle installation wrong-versionedfile bug |
482 |
if (repo_kind, file_id) != ('file', current_file): |
483 |
if len(pending_file_records) > 0: |
|
484 |
self._install_mp_records(current_versionedfile, |
|
485 |
pending_file_records) |
|
2520.4.8
by Aaron Bentley
Serialize inventory |
486 |
current_file = None |
487 |
current_versionedfile = None |
|
488 |
pending_file_records = [] |
|
2520.4.142
by Aaron Bentley
Clean up installation of inventory records |
489 |
if len(pending_inventory_records) > 0 and repo_kind != 'inventory': |
490 |
self._install_inventory_records(inventory_vf, |
|
491 |
pending_inventory_records) |
|
492 |
pending_inventory_records = [] |
|
493 |
if repo_kind == 'inventory': |
|
494 |
if inventory_vf is None: |
|
495 |
inventory_vf = self._repository.get_inventory_weave() |
|
496 |
if revision_id not in inventory_vf: |
|
497 |
pending_inventory_records.append((revision_id, metadata, |
|
498 |
bytes)) |
|
499 |
if repo_kind == 'revision': |
|
500 |
target_revision = revision_id |
|
501 |
self._install_revision(revision_id, metadata, bytes) |
|
502 |
if repo_kind == 'signature': |
|
503 |
self._install_signature(revision_id, metadata, bytes) |
|
2520.4.22
by Aaron Bentley
Create ContainerReader |
504 |
if repo_kind == 'file': |
2520.4.142
by Aaron Bentley
Clean up installation of inventory records |
505 |
current_file = file_id |
506 |
if current_versionedfile is None: |
|
2520.4.6
by Aaron Bentley
Get installation started |
507 |
current_versionedfile = \ |
2520.4.14
by Aaron Bentley
Get most tests passing, use format header |
508 |
self._repository.weave_store.get_weave_or_empty( |
509 |
file_id, self._repository.get_transaction()) |
|
2520.4.6
by Aaron Bentley
Get installation started |
510 |
pending_file_records = [] |
511 |
if revision_id in current_versionedfile: |
|
512 |
continue
|
|
2520.4.59
by Aaron Bentley
Push metadata down the stack |
513 |
pending_file_records.append((revision_id, metadata, bytes)) |
2520.4.18
by Aaron Bentley
Generate mpdiffs for inventory |
514 |
self._install_mp_records(current_versionedfile, pending_file_records) |
2520.4.14
by Aaron Bentley
Get most tests passing, use format header |
515 |
return target_revision |
2520.4.6
by Aaron Bentley
Get installation started |
516 |
|
2520.4.123
by Aaron Bentley
Cleanup of bundle code |
517 |
def _handle_info(self, info): |
518 |
"""Extract data from an info record"""
|
|
519 |
self._info = info |
|
520 |
self._source_serializer = self._serializer.get_source_serializer(info) |
|
521 |
if (info['supports_rich_root'] == 0 and |
|
522 |
self._repository.supports_rich_root()): |
|
523 |
self.update_root = True |
|
524 |
else: |
|
525 |
self.update_root = False |
|
526 |
||
2520.4.60
by Aaron Bentley
Add sha1 verification for mpdiffs |
527 |
def _install_mp_records(self, versionedfile, records): |
2520.4.61
by Aaron Bentley
Do bulk insertion of records |
528 |
if len(records) == 0: |
529 |
return
|
|
530 |
d_func = multiparent.MultiParent.from_patch |
|
531 |
vf_records = [(r, m['parents'], m['sha1'], d_func(t)) for r, m, t in |
|
532 |
records if r not in versionedfile] |
|
533 |
versionedfile.add_mpdiffs(vf_records) |
|
2520.4.8
by Aaron Bentley
Serialize inventory |
534 |
|
2520.4.142
by Aaron Bentley
Clean up installation of inventory records |
535 |
def _install_inventory_records(self, vf, records): |
2520.4.97
by Aaron Bentley
Hack in support for inventory conversion |
536 |
if self._info['serializer'] == self._repository._serializer.format_num: |
2520.4.142
by Aaron Bentley
Clean up installation of inventory records |
537 |
return self._install_mp_records(vf, records) |
538 |
for revision_id, metadata, bytes in records: |
|
539 |
parent_ids = metadata['parents'] |
|
540 |
parents = [self._repository.get_inventory(p) |
|
541 |
for p in parent_ids] |
|
542 |
p_texts = [self._source_serializer.write_inventory_to_string(p) |
|
543 |
for p in parents] |
|
544 |
target_lines = multiparent.MultiParent.from_patch(bytes).to_lines( |
|
545 |
p_texts) |
|
546 |
sha1 = osutils.sha_strings(target_lines) |
|
547 |
if sha1 != metadata['sha1']: |
|
548 |
raise errors.BadBundle("Can't convert to target format") |
|
549 |
target_inv = self._source_serializer.read_inventory_from_string( |
|
550 |
''.join(target_lines)) |
|
551 |
self._handle_root(target_inv, parent_ids) |
|
552 |
try: |
|
553 |
self._repository.add_inventory(revision_id, target_inv, |
|
554 |
parent_ids) |
|
555 |
except errors.UnsupportedInventoryKind: |
|
556 |
raise errors.IncompatibleRevision(repr(self._repository)) |
|
2520.4.99
by Aaron Bentley
Test conversion across models |
557 |
|
558 |
def _handle_root(self, target_inv, parent_ids): |
|
559 |
revision_id = target_inv.revision_id |
|
560 |
if self.update_root: |
|
561 |
target_inv.root.revision = revision_id |
|
562 |
store = self._repository.weave_store |
|
563 |
transaction = self._repository.get_transaction() |
|
564 |
vf = store.get_weave_or_empty(target_inv.root.file_id, transaction) |
|
565 |
vf.add_lines(revision_id, parent_ids, []) |
|
566 |
elif not self._repository.supports_rich_root(): |
|
567 |
if target_inv.root.revision != revision_id: |
|
568 |
raise errors.IncompatibleRevision(repr(self._repository)) |
|
569 |
||
2520.4.10
by Aaron Bentley
Enable installation of revisions |
570 |
|
2520.4.59
by Aaron Bentley
Push metadata down the stack |
571 |
def _install_revision(self, revision_id, metadata, text): |
2520.4.14
by Aaron Bentley
Get most tests passing, use format header |
572 |
if self._repository.has_revision(revision_id): |
573 |
return
|
|
574 |
self._repository._add_revision_text(revision_id, text) |
|
2520.4.34
by Aaron Bentley
Add signature support |
575 |
|
2520.4.59
by Aaron Bentley
Push metadata down the stack |
576 |
def _install_signature(self, revision_id, metadata, text): |
2520.4.100
by Aaron Bentley
Fix repeat signature installs |
577 |
transaction = self._repository.get_transaction() |
578 |
if self._repository._revision_store.has_signature(revision_id, |
|
579 |
transaction): |
|
580 |
return
|
|
2520.4.34
by Aaron Bentley
Add signature support |
581 |
self._repository._revision_store.add_revision_signature_text( |
2520.4.100
by Aaron Bentley
Fix repeat signature installs |
582 |
revision_id, text, transaction) |