37
37
class InventoryDeltaError(errors.BzrError):
38
38
"""An error when serializing or deserializing an inventory delta."""
40
40
# Most errors when serializing and deserializing are due to bugs, although
41
41
# damaged input (i.e. a bug in a different process) could cause
42
42
# deserialization errors too.
43
43
internal_error = True
45
def __init__(self, format_string, **kwargs):
46
# Let each error supply a custom format string and arguments.
47
self._fmt = format_string
48
super(InventoryDeltaError, self).__init__(**kwargs)
46
51
class IncompatibleInventoryDelta(errors.BzrError):
47
52
"""The delta could not be deserialised because its contents conflict with
71
76
size_exec_sha = (entry.text_size, exec_bytes, entry.text_sha1)
72
77
if None in size_exec_sha:
73
raise InventoryDeltaError('Missing size or sha for %s' % entry.file_id)
78
raise InventoryDeltaError(
79
'Missing size or sha for %(fileid)r', fileid=entry.file_id)
74
80
return "file\x00%d\x00%s\x00%s" % size_exec_sha
82
88
target = entry.symlink_target
84
raise InventoryDeltaError('Missing target for %s' % entry.file_id)
90
raise InventoryDeltaError(
91
'Missing target for %(fileid)r', fileid=entry.file_id)
85
92
return "link\x00%s" % target.encode('utf8')
93
100
tree_revision = entry.reference_revision
94
101
if tree_revision is None:
95
102
raise InventoryDeltaError(
96
'Missing reference revision for %s' % entry.file_id)
103
'Missing reference revision for %(fileid)r', fileid=entry.file_id)
97
104
return "tree\x00%s" % tree_revision
180
187
to_line = self._delta_item_to_line
181
188
for delta_item in delta_to_new:
182
189
line = to_line(delta_item, new_name)
183
if line.__class__ != str:
190
# GZ 2017-06-09: Not really worth asserting this here
191
if line.__class__ != bytes:
184
192
raise InventoryDeltaError(
185
'to_line generated non-str output %r' % lines[-1])
193
'to_line gave non-bytes output %(line)r', line=lines[-1])
186
194
lines.append(line)
188
196
lines[0] = "format: %s\n" % FORMAT_1
236
244
# xml5 serializer.
237
245
if last_modified != new_version:
238
246
raise InventoryDeltaError(
239
'Version present for / in %s (%s != %s)'
240
% (file_id, last_modified, new_version))
247
'Version present for / in %(fileid)r'
248
' (%(last)r != %(new)r)',
249
fileid=file_id, last=last_modified, new=new_version)
241
250
if last_modified is None:
242
raise InventoryDeltaError("no version for fileid %s" % file_id)
251
raise InventoryDeltaError(
252
"no version for fileid %(fileid)r", fileid=file_id)
243
253
content = self._entry_to_content[entry.kind](entry)
244
254
return ("%s\x00%s\x00%s\x00%s\x00%s\x00%s\n" %
245
255
(oldpath_utf8, newpath_utf8, file_id, parent_id, last_modified,
265
275
elif value == "false":
268
raise InventoryDeltaError("value %r is not a bool" % (value,))
278
raise InventoryDeltaError("value %(val)r is not a bool", val=value)
270
280
def parse_text_bytes(self, bytes):
271
281
"""Parse the text bytes of a serialized inventory delta.
282
292
if bytes[-1:] != '\n':
283
293
last_line = bytes.rsplit('\n', 1)[-1]
284
raise InventoryDeltaError('last line not empty: %r' % (last_line,))
294
raise InventoryDeltaError(
295
'last line not empty: %(line)r', line=last_line)
285
296
lines = bytes.split('\n')[:-1] # discard the last empty line
286
297
if not lines or lines[0] != 'format: %s' % FORMAT_1:
287
raise InventoryDeltaError('unknown format %r' % lines[0:1])
298
raise InventoryDeltaError(
299
'unknown format %(line)r', line=lines[0:1])
288
300
if len(lines) < 2 or not lines[1].startswith('parent: '):
289
301
raise InventoryDeltaError('missing parent: marker')
290
302
delta_parent_id = lines[1][8:]
310
322
parent_id = parent_id or None
311
323
if file_id in seen_ids:
312
324
raise InventoryDeltaError(
313
"duplicate file id in inventory delta %r" % lines)
325
"duplicate file id %(fileid)r", fileid=file_id)
314
326
seen_ids.add(file_id)
315
327
if (newpath_utf8 == '/' and not delta_versioned_root and
316
328
last_modified != delta_version_id):
317
329
# Delta claims to be not have a versioned root, yet here's
318
330
# a root entry with a non-default version.
319
raise InventoryDeltaError("Versioned root found: %r" % line)
331
raise InventoryDeltaError(
332
"Versioned root found: %(line)r", line=line)
320
333
elif newpath_utf8 != 'None' and last_modified[-1] == ':':
321
334
# Deletes have a last_modified of null:, but otherwise special
322
335
# revision ids should not occur.
323
raise InventoryDeltaError('special revisionid found: %r' % line)
336
raise InventoryDeltaError(
337
'special revisionid found: %(line)r', line=line)
324
338
if content.startswith('tree\x00'):
325
339
if delta_tree_references is False:
326
340
raise InventoryDeltaError(
327
341
"Tree reference found (but header said "
328
"tree_references: false): %r" % line)
342
"tree_references: false): %(line)r", line=line)
329
343
elif not self._allow_tree_references:
330
344
raise IncompatibleInventoryDelta(
331
345
"Tree reference not allowed")
334
348
elif oldpath_utf8[:1] != '/':
335
349
raise InventoryDeltaError(
336
"oldpath invalid (does not start with /): %r"
350
"oldpath invalid (does not start with /): %(path)r",
339
353
oldpath_utf8 = oldpath_utf8[1:]
340
354
oldpath = oldpath_utf8.decode('utf8')
343
357
elif newpath_utf8[:1] != '/':
344
358
raise InventoryDeltaError(
345
"newpath invalid (does not start with /): %r"
359
"newpath invalid (does not start with /): %(path)r",
348
362
# Trim leading slash
349
363
newpath_utf8 = newpath_utf8[1:]