260
262
def versionable_kind(kind):
261
263
return (kind in ('file', 'directory', 'symlink', 'tree-reference'))
263
def check(self, checker, rev_id, inv):
265
def check(self, checker, rev_id, inv, tree):
264
266
"""Check this inventory entry is intact.
266
268
This is a template method, override _check for kind specific
272
274
:param rev_id: Revision id from which this InventoryEntry was loaded.
273
275
Not necessarily the last-changed revision for this file.
274
276
:param inv: Inventory from which the entry was loaded.
277
:param tree: RevisionTree for this entry.
276
279
if self.parent_id is not None:
277
280
if not inv.has_id(self.parent_id):
278
281
raise BzrCheckError('missing parent {%s} in inventory for revision {%s}'
279
282
% (self.parent_id, rev_id))
280
checker._add_entry_to_text_key_references(inv, self)
281
self._check(checker, rev_id)
283
self._check(checker, rev_id, tree)
283
def _check(self, checker, rev_id):
285
def _check(self, checker, rev_id, tree):
284
286
"""Check this inventory entry for kind specific errors."""
285
checker._report_items.append(
286
'unknown entry kind %r in revision {%s}' % (self.kind, rev_id))
287
raise BzrCheckError('unknown entry kind %r in revision {%s}' %
289
291
"""Clone this inventory entry."""
402
404
'text_id', 'parent_id', 'children', 'executable',
403
405
'revision', 'symlink_target', 'reference_revision']
405
def _check(self, checker, rev_id):
407
def _check(self, checker, rev_id, tree):
406
408
"""See InventoryEntry._check"""
408
410
def __init__(self, file_id):
431
433
'text_id', 'parent_id', 'children', 'executable',
432
434
'revision', 'symlink_target', 'reference_revision']
434
def _check(self, checker, rev_id):
436
def _check(self, checker, rev_id, tree):
435
437
"""See InventoryEntry._check"""
436
if (self.text_sha1 is not None or self.text_size is not None or
437
self.text_id is not None):
438
checker._report_items.append('directory {%s} has text in revision {%s}'
438
if self.text_sha1 is not None or self.text_size is not None or self.text_id is not None:
439
raise BzrCheckError('directory {%s} has text in revision {%s}'
439
440
% (self.file_id, rev_id))
440
# Directories are stored as ''.
441
checker.add_pending_item(rev_id,
442
('texts', self.file_id, self.revision), 'text',
443
'da39a3ee5e6b4b0d3255bfef95601890afd80709')
446
443
other = InventoryDirectory(self.file_id, self.name, self.parent_id)
479
476
'text_id', 'parent_id', 'children', 'executable',
480
477
'revision', 'symlink_target', 'reference_revision']
482
def _check(self, checker, tree_revision_id):
479
def _check(self, checker, tree_revision_id, tree):
483
480
"""See InventoryEntry._check"""
484
# TODO: check size too.
485
checker.add_pending_item(tree_revision_id,
486
('texts', self.file_id, self.revision), 'text',
488
if self.text_size is None:
489
checker._report_items.append(
490
'fileid {%s} in {%s} has None for text_size' % (self.file_id,
481
key = (self.file_id, self.revision)
482
if key in checker.checked_texts:
483
prev_sha = checker.checked_texts[key]
484
if prev_sha != self.text_sha1:
486
'mismatched sha1 on {%s} in {%s} (%s != %s) %r' %
487
(self.file_id, tree_revision_id, prev_sha, self.text_sha1,
490
checker.repeated_text_cnt += 1
493
checker.checked_text_cnt += 1
494
# We can't check the length, because Weave doesn't store that
495
# information, and the whole point of looking at the weave's
496
# sha1sum is that we don't have to extract the text.
497
if (self.text_sha1 != tree._repository.texts.get_sha1s([key])[key]):
498
raise BzrCheckError('text {%s} version {%s} wrong sha1' % key)
499
checker.checked_texts[key] = self.text_sha1
494
502
other = InventoryFile(self.file_id, self.name, self.parent_id)
592
600
'text_id', 'parent_id', 'children', 'executable',
593
601
'revision', 'symlink_target', 'reference_revision']
595
def _check(self, checker, tree_revision_id):
603
def _check(self, checker, rev_id, tree):
596
604
"""See InventoryEntry._check"""
597
605
if self.text_sha1 is not None or self.text_size is not None or self.text_id is not None:
598
checker._report_items.append(
599
'symlink {%s} has text in revision {%s}'
600
% (self.file_id, tree_revision_id))
606
raise BzrCheckError('symlink {%s} has text in revision {%s}'
607
% (self.file_id, rev_id))
601
608
if self.symlink_target is None:
602
checker._report_items.append(
603
'symlink {%s} has no target in revision {%s}'
604
% (self.file_id, tree_revision_id))
605
# Symlinks are stored as ''
606
checker.add_pending_item(tree_revision_id,
607
('texts', self.file_id, self.revision), 'text',
608
'da39a3ee5e6b4b0d3255bfef95601890afd80709')
609
raise BzrCheckError('symlink {%s} has no target in revision {%s}'
610
% (self.file_id, rev_id))
611
613
other = InventoryLink(self.file_id, self.name, self.parent_id)
714
716
class CommonInventory(object):
715
"""Basic inventory logic, defined in terms of primitives like has_id.
717
An inventory is the metadata about the contents of a tree.
719
This is broadly a map from file_id to entries such as directories, files,
720
symlinks and tree references. Each entry maintains its own metadata like
721
SHA1 and length for files, or children for a directory.
723
Entries can be looked up either by path or by file_id.
725
InventoryEntry objects must not be modified after they are
726
inserted, other than through the Inventory API.
717
"""Basic inventory logic, defined in terms of primitives like has_id."""
729
719
def __contains__(self, file_id):
730
720
"""True if this entry contains a file with given id.
757
747
[parent.name for parent in
758
748
self._iter_file_id_parents(file_id)][:-1]))
760
def iter_entries(self, from_dir=None, recursive=True):
761
"""Return (path, entry) pairs, in order by name.
763
:param from_dir: if None, start from the root,
764
otherwise start from this directory (either file-id or entry)
765
:param recursive: recurse into directories or not
750
def iter_entries(self, from_dir=None):
751
"""Return (path, entry) pairs, in order by name."""
767
752
if from_dir is None:
768
753
if self.root is None:
1033
1014
class Inventory(CommonInventory):
1034
"""Mutable dict based in-memory inventory.
1036
We never store the full path to a file, because renaming a directory
1037
implicitly moves all of its contents. This class internally maintains a
1015
"""Inventory of versioned files in a tree.
1017
This describes which file_id is present at each point in the tree,
1018
and possibly the SHA-1 or other information about the file.
1019
Entries can be looked up either by path or by file_id.
1021
The inventory represents a typical unix file tree, with
1022
directories containing files and subdirectories. We never store
1023
the full path to a file, because renaming a directory implicitly
1024
moves all of its contents. This class internally maintains a
1038
1025
lookup tree that allows the children under a directory to be
1039
1026
returned quickly.
1028
InventoryEntry objects must not be modified after they are
1029
inserted, other than through the Inventory API.
1041
1031
>>> inv = Inventory()
1042
1032
>>> inv.add(InventoryFile('123-123', 'hello.c', ROOT_ID))
1043
1033
InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT', sha1=None, len=None, revision=None)
1044
1034
>>> inv['123-123'].name
1047
Id's may be looked up from paths:
1037
May be treated as an iterator or set to look up file ids:
1049
>>> inv.path2id('hello.c')
1039
>>> bool(inv.path2id('hello.c'))
1051
1041
>>> '123-123' in inv
1054
There are iterators over the contents:
1044
May also look up by name:
1056
>>> [entry[0] for entry in inv.iter_entries()]
1046
>>> [x[0] for x in inv.iter_entries()]
1057
1047
['', u'hello.c']
1048
>>> inv = Inventory('TREE_ROOT-12345678-12345678')
1049
>>> inv.add(InventoryFile('123-123', 'hello.c', ROOT_ID))
1050
Traceback (most recent call last):
1051
BzrError: parent_id {TREE_ROOT} not in inventory
1052
>>> inv.add(InventoryFile('123-123', 'hello.c', 'TREE_ROOT-12345678-12345678'))
1053
InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT-12345678-12345678', sha1=None, len=None, revision=None)
1060
1055
def __init__(self, root_id=ROOT_ID, revision_id=None):
1061
1056
"""Create or read an inventory.
1086
1081
def apply_delta(self, delta):
1087
1082
"""Apply a delta to this inventory.
1089
See the inventory developers documentation for the theory behind
1092
If delta application fails the inventory is left in an indeterminate
1093
state and must not be used.
1095
1084
:param delta: A list of changes to apply. After all the changes are
1096
1085
applied the final inventory must be internally consistent, but it
1097
1086
is ok to supply changes which, if only half-applied would have an
1129
1118
# Check that the delta is legal. It would be nice if this could be
1130
1119
# done within the loops below but it's safer to validate the delta
1131
# before starting to mutate the inventory, as there isn't a rollback
1133
list(_check_delta_unique_ids(_check_delta_unique_new_paths(
1134
_check_delta_unique_old_paths(_check_delta_ids_match_entry(
1135
_check_delta_ids_are_valid(
1136
_check_delta_new_path_entry_both_or_None(
1120
# before starting to mutate the inventory.
1121
unique_file_ids = set([f for _, _, f, _ in delta])
1122
if len(unique_file_ids) != len(delta):
1123
raise AssertionError("a file-id appears multiple times in %r"
1140
1128
# Remove all affected items which were in the original inventory,
1143
1131
# modified children remaining by the time we examine it.
1144
1132
for old_path, file_id in sorted(((op, f) for op, np, f, e in delta
1145
1133
if op is not None), reverse=True):
1134
if file_id not in self:
1146
1137
# Preserve unaltered children of file_id for later reinsertion.
1147
1138
file_id_children = getattr(self[file_id], 'children', {})
1148
1139
if len(file_id_children):
1149
1140
children[file_id] = file_id_children
1150
if self.id2path(file_id) != old_path:
1151
raise errors.InconsistentDelta(old_path, file_id,
1152
"Entry was at wrong other path %r." % self.id2path(file_id))
1153
1141
# Remove file_id and the unaltered children. If file_id is not
1154
1142
# being deleted it will be reinserted back later.
1155
1143
self.remove_recursive_id(file_id)
1158
1146
# longest, ensuring that items which were modified and whose parents in
1159
1147
# the resulting inventory were also modified, are inserted after their
1161
for new_path, f, new_entry in sorted((np, f, e) for op, np, f, e in
1149
for new_path, new_entry in sorted((np, e) for op, np, f, e in
1162
1150
delta if np is not None):
1163
1151
if new_entry.kind == 'directory':
1164
1152
# Pop the child which to allow detection of children whose
1169
1157
replacement.revision = new_entry.revision
1170
1158
replacement.children = children.pop(replacement.file_id, {})
1171
1159
new_entry = replacement
1174
except errors.DuplicateFileId:
1175
raise errors.InconsistentDelta(new_path, new_entry.file_id,
1176
"New id is already present in target.")
1177
except AttributeError:
1178
raise errors.InconsistentDelta(new_path, new_entry.file_id,
1179
"Parent is not a directory.")
1180
if self.id2path(new_entry.file_id) != new_path:
1181
raise errors.InconsistentDelta(new_path, new_entry.file_id,
1182
"New path is not consistent with parent path.")
1183
1161
if len(children):
1184
1162
# Get the parent id that was deleted
1185
1163
parent_id, children = children.popitem()
1265
1243
To add a file to a branch ready to be committed, use Branch.add,
1266
1244
which calls this.
1246
Returns the new entry object.
1270
1248
if entry.file_id in self._byid:
1271
1249
raise errors.DuplicateFileId(entry.file_id,
1272
1250
self._byid[entry.file_id])
1273
1252
if entry.parent_id is None:
1274
1253
self.root = entry
1277
1256
parent = self._byid[entry.parent_id]
1278
1257
except KeyError:
1279
raise errors.InconsistentDelta("<unknown>", entry.parent_id,
1280
"Parent not in inventory.")
1258
raise BzrError("parent_id {%s} not in inventory" %
1281
1261
if entry.name in parent.children:
1282
raise errors.InconsistentDelta(
1283
self.id2path(parent.children[entry.name].file_id),
1285
"Path already versioned")
1262
raise BzrError("%s is already versioned" %
1263
osutils.pathjoin(self.id2path(parent.file_id),
1264
entry.name).encode('utf-8'))
1286
1265
parent.children[entry.name] = entry
1287
1266
return self._add_child(entry)
1481
1460
self._fileid_to_entry_cache = {}
1482
1461
self._path_to_fileid_cache = {}
1483
1462
self._search_key_name = search_key_name
1486
def __eq__(self, other):
1487
"""Compare two sets by comparing their contents."""
1488
if not isinstance(other, CHKInventory):
1489
return NotImplemented
1491
this_key = self.id_to_entry.key()
1492
other_key = other.id_to_entry.key()
1493
this_pid_key = self.parent_id_basename_to_file_id.key()
1494
other_pid_key = other.parent_id_basename_to_file_id.key()
1495
if None in (this_key, this_pid_key, other_key, other_pid_key):
1497
return this_key == other_key and this_pid_key == other_pid_key
1499
1464
def _entry_to_bytes(self, entry):
1500
1465
"""Serialise entry as a single bytestring.
1582
1547
def _get_mutable_inventory(self):
1583
1548
"""See CommonInventory._get_mutable_inventory."""
1584
1549
entries = self.iter_entries()
1585
inv = Inventory(None, self.revision_id)
1550
if self.root_id is not None:
1552
inv = Inventory(self.root_id, self.revision_id)
1586
1553
for path, inv_entry in entries:
1587
inv.add(inv_entry.copy())
1590
1557
def create_by_apply_delta(self, inventory_delta, new_revision_id,
1591
1558
propagate_caches=False):
1592
1559
"""Create a new CHKInventory by applying inventory_delta to this one.
1594
See the inventory developers documentation for the theory behind
1597
1561
:param inventory_delta: The inventory delta to apply. See
1598
1562
Inventory.apply_delta for details.
1599
1563
:param new_revision_id: The revision id of the resulting CHKInventory.
1616
1579
search_key_func=search_key_func)
1617
1580
result.id_to_entry._ensure_root()
1618
1581
result.id_to_entry._root_node.set_maximum_size(maximum_size)
1619
# Change to apply to the parent_id_basename delta. The dict maps
1620
# (parent_id, basename) -> (old_key, new_value). We use a dict because
1621
# when a path has its id replaced (e.g. the root is changed, or someone
1622
# does bzr mv a b, bzr mv c a, we should output a single change to this
1623
# map rather than two.
1624
parent_id_basename_delta = {}
1582
parent_id_basename_delta = []
1625
1583
if self.parent_id_basename_to_file_id is not None:
1626
1584
result.parent_id_basename_to_file_id = chk_map.CHKMap(
1627
1585
self.parent_id_basename_to_file_id._store,
1637
1595
result.parent_id_basename_to_file_id = None
1638
1596
result.root_id = self.root_id
1639
1597
id_to_entry_delta = []
1640
# inventory_delta is only traversed once, so we just update the
1642
# Check for repeated file ids
1643
inventory_delta = _check_delta_unique_ids(inventory_delta)
1644
# Repeated old paths
1645
inventory_delta = _check_delta_unique_old_paths(inventory_delta)
1646
# Check for repeated new paths
1647
inventory_delta = _check_delta_unique_new_paths(inventory_delta)
1648
# Check for entries that don't match the fileid
1649
inventory_delta = _check_delta_ids_match_entry(inventory_delta)
1650
# Check for nonsense fileids
1651
inventory_delta = _check_delta_ids_are_valid(inventory_delta)
1652
# Check for new_path <-> entry consistency
1653
inventory_delta = _check_delta_new_path_entry_both_or_None(
1655
# All changed entries need to have their parents be directories and be
1656
# at the right path. This set contains (path, id) tuples.
1658
# When we delete an item, all the children of it must be either deleted
1659
# or altered in their own right. As we batch process the change via
1660
# CHKMap.apply_delta, we build a set of things to use to validate the
1664
1598
for old_path, new_path, file_id, entry in inventory_delta:
1665
1599
# file id changes
1666
1600
if new_path == '':
1675
1609
del result._path_to_fileid_cache[old_path]
1676
1610
except KeyError:
1678
deletes.add(file_id)
1680
1613
new_key = (file_id,)
1681
1614
new_value = result._entry_to_bytes(entry)
1682
1615
# Update caches. It's worth doing this whether
1683
1616
# we're propagating the old caches or not.
1684
1617
result._path_to_fileid_cache[new_path] = file_id
1685
parents.add((split(new_path)[0], entry.parent_id))
1686
1618
if old_path is None:
1689
1621
old_key = (file_id,)
1690
if self.id2path(file_id) != old_path:
1691
raise errors.InconsistentDelta(old_path, file_id,
1692
"Entry was at wrong other path %r." %
1693
self.id2path(file_id))
1694
altered.add(file_id)
1695
1622
id_to_entry_delta.append((old_key, new_key, new_value))
1696
1623
if result.parent_id_basename_to_file_id is not None:
1697
1624
# parent_id, basename changes
1707
1634
new_key = self._parent_id_basename_key(entry)
1708
1635
new_value = file_id
1709
# If the two keys are the same, the value will be unchanged
1710
# as its always the file id for this entry.
1711
1636
if old_key != new_key:
1712
# Transform a change into explicit delete/add preserving
1713
# a possible match on the key from a different file id.
1714
if old_key is not None:
1715
parent_id_basename_delta.setdefault(
1716
old_key, [None, None])[0] = old_key
1717
if new_key is not None:
1718
parent_id_basename_delta.setdefault(
1719
new_key, [None, None])[1] = new_value
1720
# validate that deletes are complete.
1721
for file_id in deletes:
1722
entry = self[file_id]
1723
if entry.kind != 'directory':
1725
# This loop could potentially be better by using the id_basename
1726
# map to just get the child file ids.
1727
for child in entry.children.values():
1728
if child.file_id not in altered:
1729
raise errors.InconsistentDelta(self.id2path(child.file_id),
1730
child.file_id, "Child not deleted or reparented when "
1637
# If the two keys are the same, the value will be unchanged
1638
# as its always the file id.
1639
parent_id_basename_delta.append((old_key, new_key, new_value))
1732
1640
result.id_to_entry.apply_delta(id_to_entry_delta)
1733
1641
if parent_id_basename_delta:
1734
# Transform the parent_id_basename delta data into a linear delta
1735
# with only one record for a given key. Optimally this would allow
1736
# re-keying, but its simpler to just output that as a delete+add
1737
# to spend less time calculating the delta.
1739
for key, (old_key, value) in parent_id_basename_delta.iteritems():
1740
if value is not None:
1741
delta_list.append((old_key, key, value))
1743
delta_list.append((old_key, None, None))
1744
result.parent_id_basename_to_file_id.apply_delta(delta_list)
1745
parents.discard(('', None))
1746
for parent_path, parent in parents:
1748
if result[parent].kind != 'directory':
1749
raise errors.InconsistentDelta(result.id2path(parent), parent,
1750
'Not a directory, but given children')
1751
except errors.NoSuchId:
1752
raise errors.InconsistentDelta("<unknown>", parent,
1753
"Parent is not present in resulting inventory.")
1754
if result.path2id(parent_path) != parent:
1755
raise errors.InconsistentDelta(parent_path, parent,
1756
"Parent has wrong path %r." % result.path2id(parent_path))
1642
result.parent_id_basename_to_file_id.apply_delta(parent_id_basename_delta)
1823
1709
:param maximum_size: The CHKMap node size limit.
1824
1710
:param search_key_name: The identifier for the search key function
1826
result = klass(search_key_name)
1712
result = CHKInventory(search_key_name)
1827
1713
result.revision_id = inventory.revision_id
1828
1714
result.root_id = inventory.root.file_id
1830
entry_to_bytes = result._entry_to_bytes
1831
parent_id_basename_key = result._parent_id_basename_key
1832
id_to_entry_dict = {}
1833
parent_id_basename_dict = {}
1715
search_key_func = chk_map.search_key_registry.get(search_key_name)
1716
result.id_to_entry = chk_map.CHKMap(chk_store, None, search_key_func)
1717
result.id_to_entry._root_node.set_maximum_size(maximum_size)
1719
result.parent_id_basename_to_file_id = chk_map.CHKMap(chk_store,
1720
None, search_key_func)
1721
result.parent_id_basename_to_file_id._root_node.set_maximum_size(
1723
result.parent_id_basename_to_file_id._root_node._key_width = 2
1724
parent_id_delta = []
1834
1725
for path, entry in inventory.iter_entries():
1835
id_to_entry_dict[(entry.file_id,)] = entry_to_bytes(entry)
1836
p_id_key = parent_id_basename_key(entry)
1837
parent_id_basename_dict[p_id_key] = entry.file_id
1839
result._populate_from_dicts(chk_store, id_to_entry_dict,
1840
parent_id_basename_dict, maximum_size=maximum_size)
1726
file_id_delta.append((None, (entry.file_id,),
1727
result._entry_to_bytes(entry)))
1728
parent_id_delta.append(
1729
(None, result._parent_id_basename_key(entry),
1731
result.id_to_entry.apply_delta(file_id_delta)
1732
result.parent_id_basename_to_file_id.apply_delta(parent_id_delta)
1843
def _populate_from_dicts(self, chk_store, id_to_entry_dict,
1844
parent_id_basename_dict, maximum_size):
1845
search_key_func = chk_map.search_key_registry.get(self._search_key_name)
1846
root_key = chk_map.CHKMap.from_dict(chk_store, id_to_entry_dict,
1847
maximum_size=maximum_size, key_width=1,
1848
search_key_func=search_key_func)
1849
self.id_to_entry = chk_map.CHKMap(chk_store, root_key,
1851
root_key = chk_map.CHKMap.from_dict(chk_store,
1852
parent_id_basename_dict,
1853
maximum_size=maximum_size, key_width=2,
1854
search_key_func=search_key_func)
1855
self.parent_id_basename_to_file_id = chk_map.CHKMap(chk_store,
1856
root_key, search_key_func)
1858
1735
def _parent_id_basename_key(self, entry):
1859
1736
"""Create a key for a entry in a parent_id_basename_to_file_id index."""
1860
1737
if entry.parent_id is not None:
2019
1896
def path2id(self, name):
2020
1897
"""See CommonInventory.path2id()."""
2021
# TODO: perhaps support negative hits?
2022
1898
result = self._path_to_fileid_cache.get(name, None)
2023
if result is not None:
2025
if isinstance(name, basestring):
2026
names = osutils.splitpath(name)
2029
current_id = self.root_id
2030
if current_id is None:
2032
parent_id_index = self.parent_id_basename_to_file_id
2033
for basename in names:
2034
# TODO: Cache each path we figure out in this function.
2035
basename_utf8 = basename.encode('utf8')
2036
key_filter = [(current_id, basename_utf8)]
2038
for (parent_id, name_utf8), file_id in parent_id_index.iteritems(
2039
key_filter=key_filter):
2040
if parent_id != current_id or name_utf8 != basename_utf8:
2041
raise errors.BzrError("corrupt inventory lookup! "
2042
"%r %r %r %r" % (parent_id, current_id, name_utf8,
2046
current_id = file_id
2047
self._path_to_fileid_cache[name] = current_id
1900
result = CommonInventory.path2id(self, name)
1901
self._path_to_fileid_cache[name] = result
2050
1904
def to_lines(self):
2051
1905
"""Serialise the inventory to lines."""
2185
2039
_NAME_RE = re.compile(r'^[^/\\]+$')
2187
2041
return bool(_NAME_RE.match(name))
2190
def _check_delta_unique_ids(delta):
2191
"""Decorate a delta and check that the file ids in it are unique.
2193
:return: A generator over delta.
2197
length = len(ids) + 1
2199
if len(ids) != length:
2200
raise errors.InconsistentDelta(item[0] or item[1], item[2],
2205
def _check_delta_unique_new_paths(delta):
2206
"""Decorate a delta and check that the new paths in it are unique.
2208
:return: A generator over delta.
2212
length = len(paths) + 1
2214
if path is not None:
2216
if len(paths) != length:
2217
raise errors.InconsistentDelta(path, item[2], "repeated path")
2221
def _check_delta_unique_old_paths(delta):
2222
"""Decorate a delta and check that the old paths in it are unique.
2224
:return: A generator over delta.
2228
length = len(paths) + 1
2230
if path is not None:
2232
if len(paths) != length:
2233
raise errors.InconsistentDelta(path, item[2], "repeated path")
2237
def _check_delta_ids_are_valid(delta):
2238
"""Decorate a delta and check that the ids in it are valid.
2240
:return: A generator over delta.
2245
raise errors.InconsistentDelta(item[0] or item[1], item[2],
2246
"entry with file_id None %r" % entry)
2247
if type(item[2]) != str:
2248
raise errors.InconsistentDelta(item[0] or item[1], item[2],
2249
"entry with non bytes file_id %r" % entry)
2253
def _check_delta_ids_match_entry(delta):
2254
"""Decorate a delta and check that the ids in it match the entry.file_id.
2256
:return: A generator over delta.
2260
if entry is not None:
2261
if entry.file_id != item[2]:
2262
raise errors.InconsistentDelta(item[0] or item[1], item[2],
2263
"mismatched id with %r" % entry)
2267
def _check_delta_new_path_entry_both_or_None(delta):
2268
"""Decorate a delta and check that the new_path and entry are paired.
2270
:return: A generator over delta.
2275
if new_path is None and entry is not None:
2276
raise errors.InconsistentDelta(item[0], item[1],
2277
"Entry with no new_path")
2278
if new_path is not None and entry is None:
2279
raise errors.InconsistentDelta(new_path, item[1],
2280
"new_path with no entry")