~bzr-pqm/bzr/bzr.dev

4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
1
# Copyright (C) 2008, 2009 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
6379.6.3 by Jelmer Vernooij
Use absolute_import.
17
from __future__ import absolute_import
18
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
19
"""Inventory delta serialisation.
20
4205.5.6 by Andrew Bennetts
Garden module docstring a little, cleanup unused imports and inaccurate __all__.
21
See doc/developers/inventory.txt for the description of the format.
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
22
23
In this module the interesting classes are:
4205.5.6 by Andrew Bennetts
Garden module docstring a little, cleanup unused imports and inaccurate __all__.
24
 - InventoryDeltaSerializer - object to read/write inventory deltas.
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
25
"""
26
4205.5.6 by Andrew Bennetts
Garden module docstring a little, cleanup unused imports and inaccurate __all__.
27
__all__ = ['InventoryDeltaSerializer']
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
28
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
29
from bzrlib import errors
4205.5.6 by Andrew Bennetts
Garden module docstring a little, cleanup unused imports and inaccurate __all__.
30
from bzrlib.osutils import basename
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
31
from bzrlib import inventory
32
from bzrlib.revision import NULL_REVISION
33
4476.3.76 by Andrew Bennetts
Split out InventoryDeltaDeserializer from InventoryDeltaSerializer.
34
FORMAT_1 = 'bzr inventory delta v1 (bzr 1.14)'
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
35
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
36
37
class InventoryDeltaError(errors.BzrError):
38
    """An error when serializing or deserializing an inventory delta."""
39
    
40
    # Most errors when serializing and deserializing are due to bugs, although
41
    # damaged input (i.e. a bug in a different process) could cause
42
    # deserialization errors too.
43
    internal_error = True
44
45
46
class IncompatibleInventoryDelta(errors.BzrError):
47
    """The delta could not be deserialised because its contents conflict with
48
    the allow_versioned_root or allow_tree_references flags of the
49
    deserializer.
50
    """
51
    internal_error = False
52
53
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
54
def _directory_content(entry):
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
55
    """Serialize the content component of entry which is a directory.
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
56
    
57
    :param entry: An InventoryDirectory.
58
    """
59
    return "dir"
60
61
62
def _file_content(entry):
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
63
    """Serialize the content component of entry which is a file.
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
64
    
65
    :param entry: An InventoryFile.
66
    """
67
    if entry.executable:
68
        exec_bytes = 'Y'
69
    else:
70
        exec_bytes = ''
71
    size_exec_sha = (entry.text_size, exec_bytes, entry.text_sha1)
72
    if None in size_exec_sha:
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
73
        raise InventoryDeltaError('Missing size or sha for %s' % entry.file_id)
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
74
    return "file\x00%d\x00%s\x00%s" % size_exec_sha
75
76
77
def _link_content(entry):
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
78
    """Serialize the content component of entry which is a symlink.
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
79
    
80
    :param entry: An InventoryLink.
81
    """
82
    target = entry.symlink_target
83
    if target is None:
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
84
        raise InventoryDeltaError('Missing target for %s' % entry.file_id)
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
85
    return "link\x00%s" % target.encode('utf8')
86
87
88
def _reference_content(entry):
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
89
    """Serialize the content component of entry which is a tree-reference.
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
90
    
91
    :param entry: A TreeReference.
92
    """
93
    tree_revision = entry.reference_revision
94
    if tree_revision is None:
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
95
        raise InventoryDeltaError(
96
            'Missing reference revision for %s' % entry.file_id)
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
97
    return "tree\x00%s" % tree_revision
98
99
100
def _dir_to_entry(content, name, parent_id, file_id, last_modified,
101
    _type=inventory.InventoryDirectory):
102
    """Convert a dir content record to an InventoryDirectory."""
103
    result = _type(file_id, name, parent_id)
104
    result.revision = last_modified
105
    return result
106
107
108
def _file_to_entry(content, name, parent_id, file_id, last_modified,
109
    _type=inventory.InventoryFile):
110
    """Convert a dir content record to an InventoryFile."""
111
    result = _type(file_id, name, parent_id)
112
    result.revision = last_modified
113
    result.text_size = int(content[1])
114
    result.text_sha1 = content[3]
115
    if content[2]:
116
        result.executable = True
117
    else:
118
        result.executable = False
119
    return result
120
121
122
def _link_to_entry(content, name, parent_id, file_id, last_modified,
123
    _type=inventory.InventoryLink):
124
    """Convert a link content record to an InventoryLink."""
125
    result = _type(file_id, name, parent_id)
126
    result.revision = last_modified
127
    result.symlink_target = content[1].decode('utf8')
128
    return result
129
130
131
def _tree_to_entry(content, name, parent_id, file_id, last_modified,
132
    _type=inventory.TreeReference):
133
    """Convert a tree content record to a TreeReference."""
134
    result = _type(file_id, name, parent_id)
135
    result.revision = last_modified
136
    result.reference_revision = content[1]
137
    return result
138
139
140
class InventoryDeltaSerializer(object):
4476.3.76 by Andrew Bennetts
Split out InventoryDeltaDeserializer from InventoryDeltaSerializer.
141
    """Serialize inventory deltas."""
142
143
    def __init__(self, versioned_root, tree_references):
144
        """Create an InventoryDeltaSerializer.
145
146
        :param versioned_root: If True, any root entry that is seen is expected
147
            to be versioned, and root entries can have any fileid.
148
        :param tree_references: If True support tree-reference entries.
149
        """
150
        self._versioned_root = versioned_root
151
        self._tree_references = tree_references
4476.3.4 by Andrew Bennetts
Network serialisation, and most tests passing with InterDifferingSerializer commented out.
152
        self._entry_to_content = {
153
            'directory': _directory_content,
154
            'file': _file_content,
155
            'symlink': _link_content,
156
        }
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
157
        if tree_references:
158
            self._entry_to_content['tree-reference'] = _reference_content
159
160
    def delta_to_lines(self, old_name, new_name, delta_to_new):
161
        """Return a line sequence for delta_to_new.
162
4476.3.12 by Andrew Bennetts
Add some more comments and docstring text to inventory_delta.py.
163
        Both the versioned_root and tree_references flags must be set via
164
        require_flags before calling this.
165
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
166
        :param old_name: A UTF8 revision id for the old inventory.  May be
167
            NULL_REVISION if there is no older inventory and delta_to_new
168
            includes the entire inventory contents.
169
        :param new_name: The version name of the inventory we create with this
170
            delta.
171
        :param delta_to_new: An inventory delta such as Inventory.apply_delta
172
            takes.
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
173
        :return: The serialized delta as lines.
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
174
        """
4476.3.48 by Andrew Bennetts
Trap mistakes like passing key tuples (rather than id strs) into delta_to_lines sooner.
175
        if type(old_name) is not str:
176
            raise TypeError('old_name should be str, got %r' % (old_name,))
177
        if type(new_name) is not str:
178
            raise TypeError('new_name should be str, got %r' % (new_name,))
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
179
        lines = ['', '', '', '', '']
180
        to_line = self._delta_item_to_line
181
        for delta_item in delta_to_new:
4476.3.46 by Andrew Bennetts
Make actual non-rich-root inventories (which have implicit root entries with non-None revisions) roundtrip through inventory-deltas correctly.
182
            line = to_line(delta_item, new_name)
183
            if line.__class__ != str:
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
184
                raise InventoryDeltaError(
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
185
                    'to_line generated non-str output %r' % lines[-1])
4476.3.46 by Andrew Bennetts
Make actual non-rich-root inventories (which have implicit root entries with non-None revisions) roundtrip through inventory-deltas correctly.
186
            lines.append(line)
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
187
        lines.sort()
4476.3.76 by Andrew Bennetts
Split out InventoryDeltaDeserializer from InventoryDeltaSerializer.
188
        lines[0] = "format: %s\n" % FORMAT_1
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
189
        lines[1] = "parent: %s\n" % old_name
190
        lines[2] = "version: %s\n" % new_name
191
        lines[3] = "versioned_root: %s\n" % self._serialize_bool(
192
            self._versioned_root)
193
        lines[4] = "tree_references: %s\n" % self._serialize_bool(
194
            self._tree_references)
195
        return lines
196
197
    def _serialize_bool(self, value):
198
        if value:
199
            return "true"
200
        else:
201
            return "false"
202
4476.3.46 by Andrew Bennetts
Make actual non-rich-root inventories (which have implicit root entries with non-None revisions) roundtrip through inventory-deltas correctly.
203
    def _delta_item_to_line(self, delta_item, new_version):
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
204
        """Convert delta_item to a line."""
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
205
        oldpath, newpath, file_id, entry = delta_item
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
206
        if newpath is None:
207
            # delete
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
208
            oldpath_utf8 = '/' + oldpath.encode('utf8')
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
209
            newpath_utf8 = 'None'
210
            parent_id = ''
211
            last_modified = NULL_REVISION
212
            content = 'deleted\x00\x00'
213
        else:
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
214
            if oldpath is None:
215
                oldpath_utf8 = 'None'
216
            else:
217
                oldpath_utf8 = '/' + oldpath.encode('utf8')
4476.3.26 by Andrew Bennetts
Stricter (de)serialization of leading slashes in paths in inventory deltas.
218
            if newpath == '/':
219
                raise AssertionError(
220
                    "Bad inventory delta: '/' is not a valid newpath "
221
                    "(should be '') in delta item %r" % (delta_item,))
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
222
            # TODO: Test real-world utf8 cache hit rate. It may be a win.
223
            newpath_utf8 = '/' + newpath.encode('utf8')
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
224
            # Serialize None as ''
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
225
            parent_id = entry.parent_id or ''
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
226
            # Serialize unknown revisions as NULL_REVISION
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
227
            last_modified = entry.revision
228
            # special cases for /
229
            if newpath_utf8 == '/' and not self._versioned_root:
4476.3.46 by Andrew Bennetts
Make actual non-rich-root inventories (which have implicit root entries with non-None revisions) roundtrip through inventory-deltas correctly.
230
                # This is an entry for the root, this inventory does not
231
                # support versioned roots.  So this must be an unversioned
4476.3.51 by Andrew Bennetts
Fix deserialization of deletes in inventory deltas, don't give up on serialized deltas from a supports_tree_refs repo unless the delta contains tree refs, and experiment to add automatic_root param to branchbuilder.
232
                # root, i.e. last_modified == new revision.  Otherwise, this
233
                # delta is invalid.
234
                # Note: the non-rich-root repositories *can* have roots with
235
                # file-ids other than TREE_ROOT, e.g. repo formats that use the
236
                # xml5 serializer.
4476.3.46 by Andrew Bennetts
Make actual non-rich-root inventories (which have implicit root entries with non-None revisions) roundtrip through inventory-deltas correctly.
237
                if last_modified != new_version:
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
238
                    raise InventoryDeltaError(
4476.3.46 by Andrew Bennetts
Make actual non-rich-root inventories (which have implicit root entries with non-None revisions) roundtrip through inventory-deltas correctly.
239
                        'Version present for / in %s (%s != %s)'
240
                        % (file_id, last_modified, new_version))
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
241
            if last_modified is None:
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
242
                raise InventoryDeltaError("no version for fileid %s" % file_id)
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
243
            content = self._entry_to_content[entry.kind](entry)
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
244
        return ("%s\x00%s\x00%s\x00%s\x00%s\x00%s\n" %
245
            (oldpath_utf8, newpath_utf8, file_id, parent_id, last_modified,
246
                content))
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
247
4476.3.76 by Andrew Bennetts
Split out InventoryDeltaDeserializer from InventoryDeltaSerializer.
248
249
class InventoryDeltaDeserializer(object):
250
    """Deserialize inventory deltas."""
251
4476.3.77 by Andrew Bennetts
Replace require_flags method with allow_versioned_root and allow_tree_references flags on InventoryDeltaSerializer.__init__, and shift some checking of delta compatibility from StreamSink to InventoryDeltaSerializer.
252
    def __init__(self, allow_versioned_root=True, allow_tree_references=True):
253
        """Create an InventoryDeltaDeserializer.
4476.3.76 by Andrew Bennetts
Split out InventoryDeltaDeserializer from InventoryDeltaSerializer.
254
255
        :param versioned_root: If True, any root entry that is seen is expected
256
            to be versioned, and root entries can have any fileid.
257
        :param tree_references: If True support tree-reference entries.
258
        """
4476.3.77 by Andrew Bennetts
Replace require_flags method with allow_versioned_root and allow_tree_references flags on InventoryDeltaSerializer.__init__, and shift some checking of delta compatibility from StreamSink to InventoryDeltaSerializer.
259
        self._allow_versioned_root = allow_versioned_root
260
        self._allow_tree_references = allow_tree_references
4476.3.76 by Andrew Bennetts
Split out InventoryDeltaDeserializer from InventoryDeltaSerializer.
261
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
262
    def _deserialize_bool(self, value):
263
        if value == "true":
264
            return True
265
        elif value == "false":
266
            return False
267
        else:
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
268
            raise InventoryDeltaError("value %r is not a bool" % (value,))
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
269
270
    def parse_text_bytes(self, bytes):
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
271
        """Parse the text bytes of a serialized inventory delta.
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
272
4476.3.12 by Andrew Bennetts
Add some more comments and docstring text to inventory_delta.py.
273
        If versioned_root and/or tree_references flags were set via
274
        require_flags, then the parsed flags must match or a BzrError will be
275
        raised.
276
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
277
        :param bytes: The bytes to parse. This can be obtained by calling
278
            delta_to_lines and then doing ''.join(delta_lines).
4476.3.4 by Andrew Bennetts
Network serialisation, and most tests passing with InterDifferingSerializer commented out.
279
        :return: (parent_id, new_id, versioned_root, tree_references,
280
            inventory_delta)
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
281
        """
4476.3.26 by Andrew Bennetts
Stricter (de)serialization of leading slashes in paths in inventory deltas.
282
        if bytes[-1:] != '\n':
283
            last_line = bytes.rsplit('\n', 1)[-1]
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
284
            raise InventoryDeltaError('last line not empty: %r' % (last_line,))
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
285
        lines = bytes.split('\n')[:-1] # discard the last empty line
4476.3.76 by Andrew Bennetts
Split out InventoryDeltaDeserializer from InventoryDeltaSerializer.
286
        if not lines or lines[0] != 'format: %s' % FORMAT_1:
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
287
            raise InventoryDeltaError('unknown format %r' % lines[0:1])
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
288
        if len(lines) < 2 or not lines[1].startswith('parent: '):
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
289
            raise InventoryDeltaError('missing parent: marker')
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
290
        delta_parent_id = lines[1][8:]
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
291
        if len(lines) < 3 or not lines[2].startswith('version: '):
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
292
            raise InventoryDeltaError('missing version: marker')
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
293
        delta_version_id = lines[2][9:]
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
294
        if len(lines) < 4 or not lines[3].startswith('versioned_root: '):
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
295
            raise InventoryDeltaError('missing versioned_root: marker')
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
296
        delta_versioned_root = self._deserialize_bool(lines[3][16:])
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
297
        if len(lines) < 5 or not lines[4].startswith('tree_references: '):
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
298
            raise InventoryDeltaError('missing tree_references: marker')
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
299
        delta_tree_references = self._deserialize_bool(lines[4][17:])
4476.3.77 by Andrew Bennetts
Replace require_flags method with allow_versioned_root and allow_tree_references flags on InventoryDeltaSerializer.__init__, and shift some checking of delta compatibility from StreamSink to InventoryDeltaSerializer.
300
        if (not self._allow_versioned_root and delta_versioned_root):
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
301
            raise IncompatibleInventoryDelta("versioned_root not allowed")
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
302
        result = []
303
        seen_ids = set()
304
        line_iter = iter(lines)
305
        for i in range(5):
306
            line_iter.next()
307
        for line in line_iter:
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
308
            (oldpath_utf8, newpath_utf8, file_id, parent_id, last_modified,
309
                content) = line.split('\x00', 5)
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
310
            parent_id = parent_id or None
311
            if file_id in seen_ids:
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
312
                raise InventoryDeltaError(
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
313
                    "duplicate file id in inventory delta %r" % lines)
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
314
            seen_ids.add(file_id)
4476.3.51 by Andrew Bennetts
Fix deserialization of deletes in inventory deltas, don't give up on serialized deltas from a supports_tree_refs repo unless the delta contains tree refs, and experiment to add automatic_root param to branchbuilder.
315
            if (newpath_utf8 == '/' and not delta_versioned_root and
316
                last_modified != delta_version_id):
4476.3.77 by Andrew Bennetts
Replace require_flags method with allow_versioned_root and allow_tree_references flags on InventoryDeltaSerializer.__init__, and shift some checking of delta compatibility from StreamSink to InventoryDeltaSerializer.
317
                    # Delta claims to be not have a versioned root, yet here's
318
                    # a root entry with a non-default version.
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
319
                    raise InventoryDeltaError("Versioned root found: %r" % line)
4476.3.51 by Andrew Bennetts
Fix deserialization of deletes in inventory deltas, don't give up on serialized deltas from a supports_tree_refs repo unless the delta contains tree refs, and experiment to add automatic_root param to branchbuilder.
320
            elif newpath_utf8 != 'None' and last_modified[-1] == ':':
321
                # Deletes have a last_modified of null:, but otherwise special
322
                # revision ids should not occur.
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
323
                raise InventoryDeltaError('special revisionid found: %r' % line)
4476.3.77 by Andrew Bennetts
Replace require_flags method with allow_versioned_root and allow_tree_references flags on InventoryDeltaSerializer.__init__, and shift some checking of delta compatibility from StreamSink to InventoryDeltaSerializer.
324
            if content.startswith('tree\x00'):
325
                if delta_tree_references is False:
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
326
                    raise InventoryDeltaError(
4476.3.77 by Andrew Bennetts
Replace require_flags method with allow_versioned_root and allow_tree_references flags on InventoryDeltaSerializer.__init__, and shift some checking of delta compatibility from StreamSink to InventoryDeltaSerializer.
327
                            "Tree reference found (but header said "
328
                            "tree_references: false): %r" % line)
329
                elif not self._allow_tree_references:
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
330
                    raise IncompatibleInventoryDelta(
331
                        "Tree reference not allowed")
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
332
            if oldpath_utf8 == 'None':
333
                oldpath = None
4476.3.26 by Andrew Bennetts
Stricter (de)serialization of leading slashes in paths in inventory deltas.
334
            elif oldpath_utf8[:1] != '/':
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
335
                raise InventoryDeltaError(
4476.3.26 by Andrew Bennetts
Stricter (de)serialization of leading slashes in paths in inventory deltas.
336
                    "oldpath invalid (does not start with /): %r"
337
                    % (oldpath_utf8,))
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
338
            else:
4476.3.26 by Andrew Bennetts
Stricter (de)serialization of leading slashes in paths in inventory deltas.
339
                oldpath_utf8 = oldpath_utf8[1:]
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
340
                oldpath = oldpath_utf8.decode('utf8')
341
            if newpath_utf8 == 'None':
342
                newpath = None
4476.3.26 by Andrew Bennetts
Stricter (de)serialization of leading slashes in paths in inventory deltas.
343
            elif newpath_utf8[:1] != '/':
4476.3.78 by Andrew Bennetts
Raise InventoryDeltaErrors, not generic BzrErrors, from inventory_delta.py.
344
                raise InventoryDeltaError(
4476.3.26 by Andrew Bennetts
Stricter (de)serialization of leading slashes in paths in inventory deltas.
345
                    "newpath invalid (does not start with /): %r"
346
                    % (newpath_utf8,))
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
347
            else:
4476.3.26 by Andrew Bennetts
Stricter (de)serialization of leading slashes in paths in inventory deltas.
348
                # Trim leading slash
349
                newpath_utf8 = newpath_utf8[1:]
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
350
                newpath = newpath_utf8.decode('utf8')
4476.3.26 by Andrew Bennetts
Stricter (de)serialization of leading slashes in paths in inventory deltas.
351
            content_tuple = tuple(content.split('\x00'))
4476.3.51 by Andrew Bennetts
Fix deserialization of deletes in inventory deltas, don't give up on serialized deltas from a supports_tree_refs repo unless the delta contains tree refs, and experiment to add automatic_root param to branchbuilder.
352
            if content_tuple[0] == 'deleted':
353
                entry = None
354
            else:
355
                entry = _parse_entry(
4476.3.65 by Andrew Bennetts
Cleaner and faster handling of newpath(_utf8) in inventory_delta.py.
356
                    newpath, file_id, parent_id, last_modified, content_tuple)
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
357
            delta_item = (oldpath, newpath, file_id, entry)
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
358
            result.append(delta_item)
4476.3.4 by Andrew Bennetts
Network serialisation, and most tests passing with InterDifferingSerializer commented out.
359
        return (delta_parent_id, delta_version_id, delta_versioned_root,
360
                delta_tree_references, result)
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
361
362
4476.3.65 by Andrew Bennetts
Cleaner and faster handling of newpath(_utf8) in inventory_delta.py.
363
def _parse_entry(path, file_id, parent_id, last_modified, content):
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
364
    entry_factory = {
365
        'dir': _dir_to_entry,
366
        'file': _file_to_entry,
367
        'link': _link_to_entry,
368
        'tree': _tree_to_entry,
369
    }
370
    kind = content[0]
4476.3.65 by Andrew Bennetts
Cleaner and faster handling of newpath(_utf8) in inventory_delta.py.
371
    if path.startswith('/'):
4476.3.49 by Andrew Bennetts
Start reworking inventory-delta streaming to use a separate substream.
372
        raise AssertionError
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
373
    name = basename(path)
374
    return entry_factory[content[0]](
375
            content, name, parent_id, file_id, last_modified)
376
4476.3.77 by Andrew Bennetts
Replace require_flags method with allow_versioned_root and allow_tree_references flags on InventoryDeltaSerializer.__init__, and shift some checking of delta compatibility from StreamSink to InventoryDeltaSerializer.
377