/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
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
17
"""Inventory delta serialisation.
18
4205.5.6 by Andrew Bennetts
Garden module docstring a little, cleanup unused imports and inaccurate __all__.
19
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.
20
21
In this module the interesting classes are:
4205.5.6 by Andrew Bennetts
Garden module docstring a little, cleanup unused imports and inaccurate __all__.
22
 - 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.
23
"""
24
4205.5.6 by Andrew Bennetts
Garden module docstring a little, cleanup unused imports and inaccurate __all__.
25
__all__ = ['InventoryDeltaSerializer']
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
26
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
27
from bzrlib import errors
4205.5.6 by Andrew Bennetts
Garden module docstring a little, cleanup unused imports and inaccurate __all__.
28
from bzrlib.osutils import basename
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
29
from bzrlib import inventory
30
from bzrlib.revision import NULL_REVISION
31
32
33
def _directory_content(entry):
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
34
    """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.
35
    
36
    :param entry: An InventoryDirectory.
37
    """
38
    return "dir"
39
40
41
def _file_content(entry):
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
42
    """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.
43
    
44
    :param entry: An InventoryFile.
45
    """
46
    if entry.executable:
47
        exec_bytes = 'Y'
48
    else:
49
        exec_bytes = ''
50
    size_exec_sha = (entry.text_size, exec_bytes, entry.text_sha1)
51
    if None in size_exec_sha:
52
        raise errors.BzrError('Missing size or sha for %s' % entry.file_id)
53
    return "file\x00%d\x00%s\x00%s" % size_exec_sha
54
55
56
def _link_content(entry):
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
57
    """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.
58
    
59
    :param entry: An InventoryLink.
60
    """
61
    target = entry.symlink_target
62
    if target is None:
63
        raise errors.BzrError('Missing target for %s' % entry.file_id)
64
    return "link\x00%s" % target.encode('utf8')
65
66
67
def _reference_content(entry):
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
68
    """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.
69
    
70
    :param entry: A TreeReference.
71
    """
72
    tree_revision = entry.reference_revision
73
    if tree_revision is None:
74
        raise errors.BzrError('Missing reference revision for %s' % entry.file_id)
75
    return "tree\x00%s" % tree_revision
76
77
78
def _dir_to_entry(content, name, parent_id, file_id, last_modified,
79
    _type=inventory.InventoryDirectory):
80
    """Convert a dir content record to an InventoryDirectory."""
81
    result = _type(file_id, name, parent_id)
82
    result.revision = last_modified
83
    return result
84
85
86
def _file_to_entry(content, name, parent_id, file_id, last_modified,
87
    _type=inventory.InventoryFile):
88
    """Convert a dir content record to an InventoryFile."""
89
    result = _type(file_id, name, parent_id)
90
    result.revision = last_modified
91
    result.text_size = int(content[1])
92
    result.text_sha1 = content[3]
93
    if content[2]:
94
        result.executable = True
95
    else:
96
        result.executable = False
97
    return result
98
99
100
def _link_to_entry(content, name, parent_id, file_id, last_modified,
101
    _type=inventory.InventoryLink):
102
    """Convert a link content record to an InventoryLink."""
103
    result = _type(file_id, name, parent_id)
104
    result.revision = last_modified
105
    result.symlink_target = content[1].decode('utf8')
106
    return result
107
108
109
def _tree_to_entry(content, name, parent_id, file_id, last_modified,
110
    _type=inventory.TreeReference):
111
    """Convert a tree content record to a TreeReference."""
112
    result = _type(file_id, name, parent_id)
113
    result.revision = last_modified
114
    result.reference_revision = content[1]
115
    return result
116
117
118
119
class InventoryDeltaSerializer(object):
120
    """Serialize and deserialize inventory deltas."""
121
4476.3.12 by Andrew Bennetts
Add some more comments and docstring text to inventory_delta.py.
122
    # XXX: really, the serializer and deserializer should be two separate
123
    # classes.
124
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
125
    FORMAT_1 = 'bzr inventory delta v1 (bzr 1.14)'
126
4476.3.4 by Andrew Bennetts
Network serialisation, and most tests passing with InterDifferingSerializer commented out.
127
    def __init__(self):
128
        """Create an InventoryDeltaSerializer."""
129
        self._versioned_root = None
130
        self._tree_references = None
131
        self._entry_to_content = {
132
            'directory': _directory_content,
133
            'file': _file_content,
134
            'symlink': _link_content,
135
        }
136
137
    def require_flags(self, versioned_root=None, tree_references=None):
4476.3.12 by Andrew Bennetts
Add some more comments and docstring text to inventory_delta.py.
138
        """Set the versioned_root and/or tree_references flags for this
139
        (de)serializer.
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
140
141
        :param versioned_root: If True, any root entry that is seen is expected
142
            to be versioned, and root entries can have any fileid.
143
        :param tree_references: If True support tree-reference entries.
144
        """
4476.3.4 by Andrew Bennetts
Network serialisation, and most tests passing with InterDifferingSerializer commented out.
145
        if versioned_root is not None and self._versioned_root is not None:
146
            raise AssertionError(
147
                "require_flags(versioned_root=...) already called.")
148
        if tree_references is not None and self._tree_references is not None:
149
            raise AssertionError(
150
                "require_flags(tree_references=...) already called.")
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
151
        self._versioned_root = versioned_root
152
        self._tree_references = tree_references
153
        if tree_references:
154
            self._entry_to_content['tree-reference'] = _reference_content
155
156
    def delta_to_lines(self, old_name, new_name, delta_to_new):
157
        """Return a line sequence for delta_to_new.
158
4476.3.12 by Andrew Bennetts
Add some more comments and docstring text to inventory_delta.py.
159
        Both the versioned_root and tree_references flags must be set via
160
        require_flags before calling this.
161
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
162
        :param old_name: A UTF8 revision id for the old inventory.  May be
163
            NULL_REVISION if there is no older inventory and delta_to_new
164
            includes the entire inventory contents.
165
        :param new_name: The version name of the inventory we create with this
166
            delta.
167
        :param delta_to_new: An inventory delta such as Inventory.apply_delta
168
            takes.
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
169
        :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.
170
        """
4476.3.4 by Andrew Bennetts
Network serialisation, and most tests passing with InterDifferingSerializer commented out.
171
        if self._versioned_root is None or self._tree_references is None:
172
            raise AssertionError(
173
                "Cannot serialise unless versioned_root/tree_references flags "
174
                "are both set.")
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
175
        lines = ['', '', '', '', '']
176
        to_line = self._delta_item_to_line
177
        for delta_item in delta_to_new:
178
            lines.append(to_line(delta_item))
179
            if lines[-1].__class__ != str:
180
                raise errors.BzrError(
181
                    'to_line generated non-str output %r' % lines[-1])
182
        lines.sort()
183
        lines[0] = "format: %s\n" % InventoryDeltaSerializer.FORMAT_1
184
        lines[1] = "parent: %s\n" % old_name
185
        lines[2] = "version: %s\n" % new_name
186
        lines[3] = "versioned_root: %s\n" % self._serialize_bool(
187
            self._versioned_root)
188
        lines[4] = "tree_references: %s\n" % self._serialize_bool(
189
            self._tree_references)
190
        return lines
191
192
    def _serialize_bool(self, value):
193
        if value:
194
            return "true"
195
        else:
196
            return "false"
197
198
    def _delta_item_to_line(self, delta_item):
199
        """Convert delta_item to a line."""
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
200
        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.
201
        if newpath is None:
202
            # delete
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
203
            oldpath_utf8 = '/' + oldpath.encode('utf8')
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
204
            newpath_utf8 = 'None'
205
            parent_id = ''
206
            last_modified = NULL_REVISION
207
            content = 'deleted\x00\x00'
208
        else:
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
209
            if oldpath is None:
210
                oldpath_utf8 = 'None'
211
            else:
212
                oldpath_utf8 = '/' + oldpath.encode('utf8')
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
213
            # TODO: Test real-world utf8 cache hit rate. It may be a win.
214
            newpath_utf8 = '/' + newpath.encode('utf8')
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
215
            # Serialize None as ''
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
216
            parent_id = entry.parent_id or ''
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
217
            # 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.
218
            last_modified = entry.revision
219
            # special cases for /
220
            if newpath_utf8 == '/' and not self._versioned_root:
221
                if file_id != 'TREE_ROOT':
222
                    raise errors.BzrError(
223
                        'file_id %s is not TREE_ROOT for /' % file_id)
224
                if last_modified is not None:
225
                    raise errors.BzrError(
226
                        'Version present for / in %s' % file_id)
227
                last_modified = NULL_REVISION
228
            if last_modified is None:
229
                raise errors.BzrError("no version for fileid %s" % file_id)
230
            content = self._entry_to_content[entry.kind](entry)
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
231
        return ("%s\x00%s\x00%s\x00%s\x00%s\x00%s\n" %
232
            (oldpath_utf8, newpath_utf8, file_id, parent_id, last_modified,
233
                content))
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
234
235
    def _deserialize_bool(self, value):
236
        if value == "true":
237
            return True
238
        elif value == "false":
239
            return False
240
        else:
241
            raise errors.BzrError("value %r is not a bool" % (value,))
242
243
    def parse_text_bytes(self, bytes):
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
244
        """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.
245
4476.3.12 by Andrew Bennetts
Add some more comments and docstring text to inventory_delta.py.
246
        If versioned_root and/or tree_references flags were set via
247
        require_flags, then the parsed flags must match or a BzrError will be
248
        raised.
249
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
250
        :param bytes: The bytes to parse. This can be obtained by calling
251
            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.
252
        :return: (parent_id, new_id, versioned_root, tree_references,
253
            inventory_delta)
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
254
        """
255
        lines = bytes.split('\n')[:-1] # discard the last empty line
256
        if not lines or lines[0] != 'format: %s' % InventoryDeltaSerializer.FORMAT_1:
257
            raise errors.BzrError('unknown format %r' % lines[0:1])
258
        if len(lines) < 2 or not lines[1].startswith('parent: '):
259
            raise errors.BzrError('missing parent: marker')
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
260
        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.
261
        if len(lines) < 3 or not lines[2].startswith('version: '):
262
            raise errors.BzrError('missing version: marker')
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
263
        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.
264
        if len(lines) < 4 or not lines[3].startswith('versioned_root: '):
265
            raise errors.BzrError('missing versioned_root: marker')
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
266
        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.
267
        if len(lines) < 5 or not lines[4].startswith('tree_references: '):
268
            raise errors.BzrError('missing tree_references: marker')
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
269
        delta_tree_references = self._deserialize_bool(lines[4][17:])
4476.3.4 by Andrew Bennetts
Network serialisation, and most tests passing with InterDifferingSerializer commented out.
270
        if (self._versioned_root is not None and
271
            delta_versioned_root != self._versioned_root):
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
272
            raise errors.BzrError(
273
                "serialized versioned_root flag is wrong: %s" %
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
274
                (delta_versioned_root,))
4476.3.4 by Andrew Bennetts
Network serialisation, and most tests passing with InterDifferingSerializer commented out.
275
        if (self._tree_references is not None
276
            and delta_tree_references != self._tree_references):
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
277
            raise errors.BzrError(
278
                "serialized tree_references flag is wrong: %s" %
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
279
                (delta_tree_references,))
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
280
        result = []
281
        seen_ids = set()
282
        line_iter = iter(lines)
283
        for i in range(5):
284
            line_iter.next()
285
        for line in line_iter:
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
286
            (oldpath_utf8, newpath_utf8, file_id, parent_id, last_modified,
287
                content) = line.split('\x00', 5)
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
288
            parent_id = parent_id or None
289
            if file_id in seen_ids:
290
                raise errors.BzrError(
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
291
                    "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.
292
            seen_ids.add(file_id)
4205.5.7 by Andrew Bennetts
Fix nits in spelling and naming.
293
            if newpath_utf8 == '/' and not delta_versioned_root and (
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
294
                last_modified != 'null:' or file_id != 'TREE_ROOT'):
295
                    raise errors.BzrError("Versioned root found: %r" % line)
296
            elif last_modified[-1] == ':':
297
                    raise errors.BzrError('special revisionid found: %r' % line)
4476.3.4 by Andrew Bennetts
Network serialisation, and most tests passing with InterDifferingSerializer commented out.
298
            if delta_tree_references is False and content.startswith('tree\x00'):
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
299
                raise errors.BzrError("Tree reference found: %r" % line)
300
            content_tuple = tuple(content.split('\x00'))
301
            entry = _parse_entry(
302
                newpath_utf8, file_id, parent_id, last_modified, content_tuple)
4205.5.3 by Andrew Bennetts
Include oldpath in the the serialised delta
303
            if oldpath_utf8 == 'None':
304
                oldpath = None
305
            else:
306
                oldpath = oldpath_utf8.decode('utf8')
307
            if newpath_utf8 == 'None':
308
                newpath = None
309
            else:
310
                newpath = newpath_utf8.decode('utf8')
311
            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.
312
            result.append(delta_item)
4476.3.4 by Andrew Bennetts
Network serialisation, and most tests passing with InterDifferingSerializer commented out.
313
        return (delta_parent_id, delta_version_id, delta_versioned_root,
314
                delta_tree_references, result)
4205.5.1 by Andrew Bennetts
Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.
315
316
317
def _parse_entry(utf8_path, file_id, parent_id, last_modified, content):
318
    entry_factory = {
319
        'dir': _dir_to_entry,
320
        'file': _file_to_entry,
321
        'link': _link_to_entry,
322
        'tree': _tree_to_entry,
323
    }
324
    kind = content[0]
325
    path = utf8_path[1:].decode('utf8')
326
    name = basename(path)
327
    return entry_factory[content[0]](
328
            content, name, parent_id, file_id, last_modified)
329