/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/inventory_delta.py

  • Committer: John Arbash Meinel
  • Date: 2009-06-17 19:08:25 UTC
  • mto: This revision was merged to the branch mainline in revision 4460.
  • Revision ID: john@arbash-meinel.com-20090617190825-ktfk82li57rf2im6
It seems that fetch() no longer returns the number of revisions fetched.
It still does for *some* InterRepository fetch paths, but the generic one does not.
It is also not easy to get it to, since the Source and Sink are the ones
that would know how many keys were transmitted, and they are potentially 'remote'
objects.

This was also only tested to occur as a by-product in a random 'test_commit' test.
I assume if we really wanted the assurance, we would have a per_repo or interrepo
test for it.

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
from bzrlib import inventory
30
30
from bzrlib.revision import NULL_REVISION
31
31
 
32
 
FORMAT_1 = 'bzr inventory delta v1 (bzr 1.14)'
33
 
 
34
 
 
35
 
class InventoryDeltaError(errors.BzrError):
36
 
    """An error when serializing or deserializing an inventory delta."""
37
 
    
38
 
    # Most errors when serializing and deserializing are due to bugs, although
39
 
    # damaged input (i.e. a bug in a different process) could cause
40
 
    # deserialization errors too.
41
 
    internal_error = True
42
 
 
43
 
 
44
 
class IncompatibleInventoryDelta(errors.BzrError):
45
 
    """The delta could not be deserialised because its contents conflict with
46
 
    the allow_versioned_root or allow_tree_references flags of the
47
 
    deserializer.
48
 
    """
49
 
    internal_error = False
50
 
 
51
32
 
52
33
def _directory_content(entry):
53
34
    """Serialize the content component of entry which is a directory.
68
49
        exec_bytes = ''
69
50
    size_exec_sha = (entry.text_size, exec_bytes, entry.text_sha1)
70
51
    if None in size_exec_sha:
71
 
        raise InventoryDeltaError('Missing size or sha for %s' % entry.file_id)
 
52
        raise errors.BzrError('Missing size or sha for %s' % entry.file_id)
72
53
    return "file\x00%d\x00%s\x00%s" % size_exec_sha
73
54
 
74
55
 
79
60
    """
80
61
    target = entry.symlink_target
81
62
    if target is None:
82
 
        raise InventoryDeltaError('Missing target for %s' % entry.file_id)
 
63
        raise errors.BzrError('Missing target for %s' % entry.file_id)
83
64
    return "link\x00%s" % target.encode('utf8')
84
65
 
85
66
 
90
71
    """
91
72
    tree_revision = entry.reference_revision
92
73
    if tree_revision is None:
93
 
        raise InventoryDeltaError(
94
 
            'Missing reference revision for %s' % entry.file_id)
 
74
        raise errors.BzrError('Missing reference revision for %s' % entry.file_id)
95
75
    return "tree\x00%s" % tree_revision
96
76
 
97
77
 
135
115
    return result
136
116
 
137
117
 
 
118
 
138
119
class InventoryDeltaSerializer(object):
139
 
    """Serialize inventory deltas."""
 
120
    """Serialize and deserialize inventory deltas."""
 
121
 
 
122
    FORMAT_1 = 'bzr inventory delta v1 (bzr 1.14)'
140
123
 
141
124
    def __init__(self, versioned_root, tree_references):
142
125
        """Create an InventoryDeltaSerializer.
158
141
    def delta_to_lines(self, old_name, new_name, delta_to_new):
159
142
        """Return a line sequence for delta_to_new.
160
143
 
161
 
        Both the versioned_root and tree_references flags must be set via
162
 
        require_flags before calling this.
163
 
 
164
144
        :param old_name: A UTF8 revision id for the old inventory.  May be
165
145
            NULL_REVISION if there is no older inventory and delta_to_new
166
146
            includes the entire inventory contents.
170
150
            takes.
171
151
        :return: The serialized delta as lines.
172
152
        """
173
 
        if type(old_name) is not str:
174
 
            raise TypeError('old_name should be str, got %r' % (old_name,))
175
 
        if type(new_name) is not str:
176
 
            raise TypeError('new_name should be str, got %r' % (new_name,))
177
153
        lines = ['', '', '', '', '']
178
154
        to_line = self._delta_item_to_line
179
155
        for delta_item in delta_to_new:
180
 
            line = to_line(delta_item, new_name)
181
 
            if line.__class__ != str:
182
 
                raise InventoryDeltaError(
 
156
            lines.append(to_line(delta_item))
 
157
            if lines[-1].__class__ != str:
 
158
                raise errors.BzrError(
183
159
                    'to_line generated non-str output %r' % lines[-1])
184
 
            lines.append(line)
185
160
        lines.sort()
186
 
        lines[0] = "format: %s\n" % FORMAT_1
 
161
        lines[0] = "format: %s\n" % InventoryDeltaSerializer.FORMAT_1
187
162
        lines[1] = "parent: %s\n" % old_name
188
163
        lines[2] = "version: %s\n" % new_name
189
164
        lines[3] = "versioned_root: %s\n" % self._serialize_bool(
198
173
        else:
199
174
            return "false"
200
175
 
201
 
    def _delta_item_to_line(self, delta_item, new_version):
 
176
    def _delta_item_to_line(self, delta_item):
202
177
        """Convert delta_item to a line."""
203
178
        oldpath, newpath, file_id, entry = delta_item
204
179
        if newpath is None:
213
188
                oldpath_utf8 = 'None'
214
189
            else:
215
190
                oldpath_utf8 = '/' + oldpath.encode('utf8')
216
 
            if newpath == '/':
217
 
                raise AssertionError(
218
 
                    "Bad inventory delta: '/' is not a valid newpath "
219
 
                    "(should be '') in delta item %r" % (delta_item,))
220
191
            # TODO: Test real-world utf8 cache hit rate. It may be a win.
221
192
            newpath_utf8 = '/' + newpath.encode('utf8')
222
193
            # Serialize None as ''
225
196
            last_modified = entry.revision
226
197
            # special cases for /
227
198
            if newpath_utf8 == '/' and not self._versioned_root:
228
 
                # This is an entry for the root, this inventory does not
229
 
                # support versioned roots.  So this must be an unversioned
230
 
                # root, i.e. last_modified == new revision.  Otherwise, this
231
 
                # delta is invalid.
232
 
                # Note: the non-rich-root repositories *can* have roots with
233
 
                # file-ids other than TREE_ROOT, e.g. repo formats that use the
234
 
                # xml5 serializer.
235
 
                if last_modified != new_version:
236
 
                    raise InventoryDeltaError(
237
 
                        'Version present for / in %s (%s != %s)'
238
 
                        % (file_id, last_modified, new_version))
 
199
                if file_id != 'TREE_ROOT':
 
200
                    raise errors.BzrError(
 
201
                        'file_id %s is not TREE_ROOT for /' % file_id)
 
202
                if last_modified is not None:
 
203
                    raise errors.BzrError(
 
204
                        'Version present for / in %s' % file_id)
 
205
                last_modified = NULL_REVISION
239
206
            if last_modified is None:
240
 
                raise InventoryDeltaError("no version for fileid %s" % file_id)
 
207
                raise errors.BzrError("no version for fileid %s" % file_id)
241
208
            content = self._entry_to_content[entry.kind](entry)
242
209
        return ("%s\x00%s\x00%s\x00%s\x00%s\x00%s\n" %
243
210
            (oldpath_utf8, newpath_utf8, file_id, parent_id, last_modified,
244
211
                content))
245
212
 
246
 
 
247
 
class InventoryDeltaDeserializer(object):
248
 
    """Deserialize inventory deltas."""
249
 
 
250
 
    def __init__(self, allow_versioned_root=True, allow_tree_references=True):
251
 
        """Create an InventoryDeltaDeserializer.
252
 
 
253
 
        :param versioned_root: If True, any root entry that is seen is expected
254
 
            to be versioned, and root entries can have any fileid.
255
 
        :param tree_references: If True support tree-reference entries.
256
 
        """
257
 
        self._allow_versioned_root = allow_versioned_root
258
 
        self._allow_tree_references = allow_tree_references
259
 
 
260
213
    def _deserialize_bool(self, value):
261
214
        if value == "true":
262
215
            return True
263
216
        elif value == "false":
264
217
            return False
265
218
        else:
266
 
            raise InventoryDeltaError("value %r is not a bool" % (value,))
 
219
            raise errors.BzrError("value %r is not a bool" % (value,))
267
220
 
268
221
    def parse_text_bytes(self, bytes):
269
222
        """Parse the text bytes of a serialized inventory delta.
270
223
 
271
 
        If versioned_root and/or tree_references flags were set via
272
 
        require_flags, then the parsed flags must match or a BzrError will be
273
 
        raised.
274
 
 
275
224
        :param bytes: The bytes to parse. This can be obtained by calling
276
225
            delta_to_lines and then doing ''.join(delta_lines).
277
 
        :return: (parent_id, new_id, versioned_root, tree_references,
278
 
            inventory_delta)
 
226
        :return: (parent_id, new_id, inventory_delta)
279
227
        """
280
 
        if bytes[-1:] != '\n':
281
 
            last_line = bytes.rsplit('\n', 1)[-1]
282
 
            raise InventoryDeltaError('last line not empty: %r' % (last_line,))
283
228
        lines = bytes.split('\n')[:-1] # discard the last empty line
284
 
        if not lines or lines[0] != 'format: %s' % FORMAT_1:
285
 
            raise InventoryDeltaError('unknown format %r' % lines[0:1])
 
229
        if not lines or lines[0] != 'format: %s' % InventoryDeltaSerializer.FORMAT_1:
 
230
            raise errors.BzrError('unknown format %r' % lines[0:1])
286
231
        if len(lines) < 2 or not lines[1].startswith('parent: '):
287
 
            raise InventoryDeltaError('missing parent: marker')
 
232
            raise errors.BzrError('missing parent: marker')
288
233
        delta_parent_id = lines[1][8:]
289
234
        if len(lines) < 3 or not lines[2].startswith('version: '):
290
 
            raise InventoryDeltaError('missing version: marker')
 
235
            raise errors.BzrError('missing version: marker')
291
236
        delta_version_id = lines[2][9:]
292
237
        if len(lines) < 4 or not lines[3].startswith('versioned_root: '):
293
 
            raise InventoryDeltaError('missing versioned_root: marker')
 
238
            raise errors.BzrError('missing versioned_root: marker')
294
239
        delta_versioned_root = self._deserialize_bool(lines[3][16:])
295
240
        if len(lines) < 5 or not lines[4].startswith('tree_references: '):
296
 
            raise InventoryDeltaError('missing tree_references: marker')
 
241
            raise errors.BzrError('missing tree_references: marker')
297
242
        delta_tree_references = self._deserialize_bool(lines[4][17:])
298
 
        if (not self._allow_versioned_root and delta_versioned_root):
299
 
            raise IncompatibleInventoryDelta("versioned_root not allowed")
 
243
        if delta_versioned_root != self._versioned_root:
 
244
            raise errors.BzrError(
 
245
                "serialized versioned_root flag is wrong: %s" %
 
246
                (delta_versioned_root,))
 
247
        if delta_tree_references != self._tree_references:
 
248
            raise errors.BzrError(
 
249
                "serialized tree_references flag is wrong: %s" %
 
250
                (delta_tree_references,))
300
251
        result = []
301
252
        seen_ids = set()
302
253
        line_iter = iter(lines)
307
258
                content) = line.split('\x00', 5)
308
259
            parent_id = parent_id or None
309
260
            if file_id in seen_ids:
310
 
                raise InventoryDeltaError(
 
261
                raise errors.BzrError(
311
262
                    "duplicate file id in inventory delta %r" % lines)
312
263
            seen_ids.add(file_id)
313
 
            if (newpath_utf8 == '/' and not delta_versioned_root and
314
 
                last_modified != delta_version_id):
315
 
                    # Delta claims to be not have a versioned root, yet here's
316
 
                    # a root entry with a non-default version.
317
 
                    raise InventoryDeltaError("Versioned root found: %r" % line)
318
 
            elif newpath_utf8 != 'None' and last_modified[-1] == ':':
319
 
                # Deletes have a last_modified of null:, but otherwise special
320
 
                # revision ids should not occur.
321
 
                raise InventoryDeltaError('special revisionid found: %r' % line)
322
 
            if content.startswith('tree\x00'):
323
 
                if delta_tree_references is False:
324
 
                    raise InventoryDeltaError(
325
 
                            "Tree reference found (but header said "
326
 
                            "tree_references: false): %r" % line)
327
 
                elif not self._allow_tree_references:
328
 
                    raise IncompatibleInventoryDelta(
329
 
                        "Tree reference not allowed")
 
264
            if newpath_utf8 == '/' and not delta_versioned_root and (
 
265
                last_modified != 'null:' or file_id != 'TREE_ROOT'):
 
266
                    raise errors.BzrError("Versioned root found: %r" % line)
 
267
            elif last_modified[-1] == ':':
 
268
                    raise errors.BzrError('special revisionid found: %r' % line)
 
269
            if not delta_tree_references and content.startswith('tree\x00'):
 
270
                raise errors.BzrError("Tree reference found: %r" % line)
 
271
            content_tuple = tuple(content.split('\x00'))
 
272
            entry = _parse_entry(
 
273
                newpath_utf8, file_id, parent_id, last_modified, content_tuple)
330
274
            if oldpath_utf8 == 'None':
331
275
                oldpath = None
332
 
            elif oldpath_utf8[:1] != '/':
333
 
                raise InventoryDeltaError(
334
 
                    "oldpath invalid (does not start with /): %r"
335
 
                    % (oldpath_utf8,))
336
276
            else:
337
 
                oldpath_utf8 = oldpath_utf8[1:]
338
277
                oldpath = oldpath_utf8.decode('utf8')
339
278
            if newpath_utf8 == 'None':
340
279
                newpath = None
341
 
            elif newpath_utf8[:1] != '/':
342
 
                raise InventoryDeltaError(
343
 
                    "newpath invalid (does not start with /): %r"
344
 
                    % (newpath_utf8,))
345
280
            else:
346
 
                # Trim leading slash
347
 
                newpath_utf8 = newpath_utf8[1:]
348
281
                newpath = newpath_utf8.decode('utf8')
349
 
            content_tuple = tuple(content.split('\x00'))
350
 
            if content_tuple[0] == 'deleted':
351
 
                entry = None
352
 
            else:
353
 
                entry = _parse_entry(
354
 
                    newpath, file_id, parent_id, last_modified, content_tuple)
355
282
            delta_item = (oldpath, newpath, file_id, entry)
356
283
            result.append(delta_item)
357
 
        return (delta_parent_id, delta_version_id, delta_versioned_root,
358
 
                delta_tree_references, result)
359
 
 
360
 
 
361
 
def _parse_entry(path, file_id, parent_id, last_modified, content):
 
284
        return delta_parent_id, delta_version_id, result
 
285
 
 
286
 
 
287
def _parse_entry(utf8_path, file_id, parent_id, last_modified, content):
362
288
    entry_factory = {
363
289
        'dir': _dir_to_entry,
364
290
        'file': _file_to_entry,
366
292
        'tree': _tree_to_entry,
367
293
    }
368
294
    kind = content[0]
369
 
    if path.startswith('/'):
370
 
        raise AssertionError
 
295
    path = utf8_path[1:].decode('utf8')
371
296
    name = basename(path)
372
297
    return entry_factory[content[0]](
373
298
            content, name, parent_id, file_id, last_modified)
374
299
 
375