274
272
:param rev_id: Revision id from which this InventoryEntry was loaded.
275
273
Not necessarily the last-changed revision for this file.
276
274
:param inv: Inventory from which the entry was loaded.
277
:param tree: RevisionTree for this entry.
279
276
if self.parent_id is not None:
280
277
if not inv.has_id(self.parent_id):
281
278
raise BzrCheckError('missing parent {%s} in inventory for revision {%s}'
282
279
% (self.parent_id, rev_id))
283
self._check(checker, rev_id, tree)
280
checker._add_entry_to_text_key_references(inv, self)
281
self._check(checker, rev_id)
285
def _check(self, checker, rev_id, tree):
283
def _check(self, checker, rev_id):
286
284
"""Check this inventory entry for kind specific errors."""
287
raise BzrCheckError('unknown entry kind %r in revision {%s}' %
285
checker._report_items.append(
286
'unknown entry kind %r in revision {%s}' % (self.kind, rev_id))
291
289
"""Clone this inventory entry."""
433
431
'text_id', 'parent_id', 'children', 'executable',
434
432
'revision', 'symlink_target', 'reference_revision']
436
def _check(self, checker, rev_id, tree):
434
def _check(self, checker, rev_id):
437
435
"""See InventoryEntry._check"""
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}'
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}'
440
439
% (self.file_id, rev_id))
440
# In non rich root repositories we do not expect a file graph for the
442
if self.name == '' and not checker.rich_roots:
444
# Directories are stored as an empty file, but the file should exist
445
# to provide a per-fileid log. The hash of every directory content is
446
# "da..." below (the sha1sum of '').
447
checker.add_pending_item(rev_id,
448
('texts', self.file_id, self.revision), 'text',
449
'da39a3ee5e6b4b0d3255bfef95601890afd80709')
443
452
other = InventoryDirectory(self.file_id, self.name, self.parent_id)
476
485
'text_id', 'parent_id', 'children', 'executable',
477
486
'revision', 'symlink_target', 'reference_revision']
479
def _check(self, checker, tree_revision_id, tree):
488
def _check(self, checker, tree_revision_id):
480
489
"""See InventoryEntry._check"""
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
490
# TODO: check size too.
491
checker.add_pending_item(tree_revision_id,
492
('texts', self.file_id, self.revision), 'text',
494
if self.text_size is None:
495
checker._report_items.append(
496
'fileid {%s} in {%s} has None for text_size' % (self.file_id,
502
500
other = InventoryFile(self.file_id, self.name, self.parent_id)
600
598
'text_id', 'parent_id', 'children', 'executable',
601
599
'revision', 'symlink_target', 'reference_revision']
603
def _check(self, checker, rev_id, tree):
601
def _check(self, checker, tree_revision_id):
604
602
"""See InventoryEntry._check"""
605
603
if self.text_sha1 is not None or self.text_size is not None or self.text_id is not None:
606
raise BzrCheckError('symlink {%s} has text in revision {%s}'
607
% (self.file_id, rev_id))
604
checker._report_items.append(
605
'symlink {%s} has text in revision {%s}'
606
% (self.file_id, tree_revision_id))
608
607
if self.symlink_target is None:
609
raise BzrCheckError('symlink {%s} has no target in revision {%s}'
610
% (self.file_id, rev_id))
608
checker._report_items.append(
609
'symlink {%s} has no target in revision {%s}'
610
% (self.file_id, tree_revision_id))
611
# Symlinks are stored as ''
612
checker.add_pending_item(tree_revision_id,
613
('texts', self.file_id, self.revision), 'text',
614
'da39a3ee5e6b4b0d3255bfef95601890afd80709')
613
617
other = InventoryLink(self.file_id, self.name, self.parent_id)
716
720
class CommonInventory(object):
717
"""Basic inventory logic, defined in terms of primitives like has_id."""
721
"""Basic inventory logic, defined in terms of primitives like has_id.
723
An inventory is the metadata about the contents of a tree.
725
This is broadly a map from file_id to entries such as directories, files,
726
symlinks and tree references. Each entry maintains its own metadata like
727
SHA1 and length for files, or children for a directory.
729
Entries can be looked up either by path or by file_id.
731
InventoryEntry objects must not be modified after they are
732
inserted, other than through the Inventory API.
719
735
def __contains__(self, file_id):
720
736
"""True if this entry contains a file with given id.
741
760
>>> e = i.add(InventoryFile('foo-id', 'foo.c', parent_id='src-id'))
742
761
>>> print i.id2path('foo-id')
764
:raises NoSuchId: If file_id is not present in the inventory.
745
766
# get all names, skipping root
746
767
return '/'.join(reversed(
747
768
[parent.name for parent in
748
769
self._iter_file_id_parents(file_id)][:-1]))
750
def iter_entries(self, from_dir=None):
751
"""Return (path, entry) pairs, in order by name."""
771
def iter_entries(self, from_dir=None, recursive=True):
772
"""Return (path, entry) pairs, in order by name.
774
:param from_dir: if None, start from the root,
775
otherwise start from this directory (either file-id or entry)
776
:param recursive: recurse into directories or not
752
778
if from_dir is None:
753
779
if self.root is None:
1014
1044
class Inventory(CommonInventory):
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
1045
"""Mutable dict based in-memory inventory.
1047
We never store the full path to a file, because renaming a directory
1048
implicitly moves all of its contents. This class internally maintains a
1025
1049
lookup tree that allows the children under a directory to be
1026
1050
returned quickly.
1028
InventoryEntry objects must not be modified after they are
1029
inserted, other than through the Inventory API.
1031
1052
>>> inv = Inventory()
1032
1053
>>> inv.add(InventoryFile('123-123', 'hello.c', ROOT_ID))
1033
1054
InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT', sha1=None, len=None, revision=None)
1034
1055
>>> inv['123-123'].name
1037
May be treated as an iterator or set to look up file ids:
1058
Id's may be looked up from paths:
1039
>>> bool(inv.path2id('hello.c'))
1060
>>> inv.path2id('hello.c')
1041
1062
>>> '123-123' in inv
1044
May also look up by name:
1065
There are iterators over the contents:
1046
>>> [x[0] for x in inv.iter_entries()]
1067
>>> [entry[0] for entry in inv.iter_entries()]
1047
1068
['', 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)
1055
1071
def __init__(self, root_id=ROOT_ID, revision_id=None):
1056
1072
"""Create or read an inventory.
1131
1154
# modified children remaining by the time we examine it.
1132
1155
for old_path, file_id in sorted(((op, f) for op, np, f, e in delta
1133
1156
if op is not None), reverse=True):
1134
if file_id not in self:
1137
1157
# Preserve unaltered children of file_id for later reinsertion.
1138
1158
file_id_children = getattr(self[file_id], 'children', {})
1139
1159
if len(file_id_children):
1140
1160
children[file_id] = file_id_children
1161
if self.id2path(file_id) != old_path:
1162
raise errors.InconsistentDelta(old_path, file_id,
1163
"Entry was at wrong other path %r." % self.id2path(file_id))
1141
1164
# Remove file_id and the unaltered children. If file_id is not
1142
1165
# being deleted it will be reinserted back later.
1143
1166
self.remove_recursive_id(file_id)
1157
1180
replacement.revision = new_entry.revision
1158
1181
replacement.children = children.pop(replacement.file_id, {})
1159
1182
new_entry = replacement
1185
except errors.DuplicateFileId:
1186
raise errors.InconsistentDelta(new_path, new_entry.file_id,
1187
"New id is already present in target.")
1188
except AttributeError:
1189
raise errors.InconsistentDelta(new_path, new_entry.file_id,
1190
"Parent is not a directory.")
1191
if self.id2path(new_entry.file_id) != new_path:
1192
raise errors.InconsistentDelta(new_path, new_entry.file_id,
1193
"New path is not consistent with parent path.")
1161
1194
if len(children):
1162
1195
# Get the parent id that was deleted
1163
1196
parent_id, children = children.popitem()
1164
1197
raise errors.InconsistentDelta("<deleted>", parent_id,
1165
1198
"The file id was deleted but its children were not deleted.")
1200
def create_by_apply_delta(self, inventory_delta, new_revision_id,
1201
propagate_caches=False):
1202
"""See CHKInventory.create_by_apply_delta()"""
1203
new_inv = self.copy()
1204
new_inv.apply_delta(inventory_delta)
1205
new_inv.revision_id = new_revision_id
1167
1208
def _set_root(self, ie):
1169
1210
self._byid = {self.root.file_id: self.root}
1243
1284
To add a file to a branch ready to be committed, use Branch.add,
1244
1285
which calls this.
1246
Returns the new entry object.
1248
1289
if entry.file_id in self._byid:
1249
1290
raise errors.DuplicateFileId(entry.file_id,
1250
1291
self._byid[entry.file_id])
1252
1292
if entry.parent_id is None:
1253
1293
self.root = entry
1256
1296
parent = self._byid[entry.parent_id]
1257
1297
except KeyError:
1258
raise BzrError("parent_id {%s} not in inventory" %
1298
raise errors.InconsistentDelta("<unknown>", entry.parent_id,
1299
"Parent not in inventory.")
1261
1300
if entry.name in parent.children:
1262
raise BzrError("%s is already versioned" %
1263
osutils.pathjoin(self.id2path(parent.file_id),
1264
entry.name).encode('utf-8'))
1301
raise errors.InconsistentDelta(
1302
self.id2path(parent.children[entry.name].file_id),
1304
"Path already versioned")
1265
1305
parent.children[entry.name] = entry
1266
1306
return self._add_child(entry)
1504
1555
raise ValueError("unknown kind %r" % entry.kind)
1557
def _expand_fileids_to_parents_and_children(self, file_ids):
1558
"""Give a more wholistic view starting with the given file_ids.
1560
For any file_id which maps to a directory, we will include all children
1561
of that directory. We will also include all directories which are
1562
parents of the given file_ids, but we will not include their children.
1569
fringle # fringle-id
1573
if given [foo-id] we will include
1574
TREE_ROOT as interesting parents
1576
foo-id, baz-id, frob-id, fringle-id
1580
# TODO: Pre-pass over the list of fileids to see if anything is already
1581
# deserialized in self._fileid_to_entry_cache
1583
directories_to_expand = set()
1584
children_of_parent_id = {}
1585
# It is okay if some of the fileids are missing
1586
for entry in self._getitems(file_ids):
1587
if entry.kind == 'directory':
1588
directories_to_expand.add(entry.file_id)
1589
interesting.add(entry.parent_id)
1590
children_of_parent_id.setdefault(entry.parent_id, []
1591
).append(entry.file_id)
1593
# Now, interesting has all of the direct parents, but not the
1594
# parents of those parents. It also may have some duplicates with
1596
remaining_parents = interesting.difference(file_ids)
1597
# When we hit the TREE_ROOT, we'll get an interesting parent of None,
1598
# but we don't actually want to recurse into that
1599
interesting.add(None) # this will auto-filter it in the loop
1600
remaining_parents.discard(None)
1601
while remaining_parents:
1602
if None in remaining_parents:
1603
import pdb; pdb.set_trace()
1604
next_parents = set()
1605
for entry in self._getitems(remaining_parents):
1606
next_parents.add(entry.parent_id)
1607
children_of_parent_id.setdefault(entry.parent_id, []
1608
).append(entry.file_id)
1609
# Remove any search tips we've already processed
1610
remaining_parents = next_parents.difference(interesting)
1611
interesting.update(remaining_parents)
1612
# We should probably also .difference(directories_to_expand)
1613
interesting.update(file_ids)
1614
interesting.discard(None)
1615
while directories_to_expand:
1616
# Expand directories by looking in the
1617
# parent_id_basename_to_file_id map
1618
keys = [(f,) for f in directories_to_expand]
1619
directories_to_expand = set()
1620
items = self.parent_id_basename_to_file_id.iteritems(keys)
1621
next_file_ids = set([item[1] for item in items])
1622
next_file_ids = next_file_ids.difference(interesting)
1623
interesting.update(next_file_ids)
1624
for entry in self._getitems(next_file_ids):
1625
if entry.kind == 'directory':
1626
directories_to_expand.add(entry.file_id)
1627
children_of_parent_id.setdefault(entry.parent_id, []
1628
).append(entry.file_id)
1629
return interesting, children_of_parent_id
1631
def filter(self, specific_fileids):
1632
"""Get an inventory view filtered against a set of file-ids.
1634
Children of directories and parents are included.
1636
The result may or may not reference the underlying inventory
1637
so it should be treated as immutable.
1640
parent_to_children) = self._expand_fileids_to_parents_and_children(
1642
# There is some overlap here, but we assume that all interesting items
1643
# are in the _fileid_to_entry_cache because we had to read them to
1644
# determine if they were a dir we wanted to recurse, or just a file
1645
# This should give us all the entries we'll want to add, so start
1647
other = Inventory(self.root_id)
1648
other.root.revision = self.root.revision
1649
other.revision_id = self.revision_id
1650
if not interesting or not parent_to_children:
1651
# empty filter, or filtering entrys that don't exist
1652
# (if even 1 existed, then we would have populated
1653
# parent_to_children with at least the tree root.)
1655
cache = self._fileid_to_entry_cache
1657
remaining_children = collections.deque(parent_to_children[self.root_id])
1659
import pdb; pdb.set_trace()
1661
while remaining_children:
1662
file_id = remaining_children.popleft()
1664
if ie.kind == 'directory':
1665
ie = ie.copy() # We create a copy to depopulate the .children attribute
1666
# TODO: depending on the uses of 'other' we should probably alwyas
1667
# '.copy()' to prevent someone from mutating other and
1668
# invaliding our internal cache
1670
if file_id in parent_to_children:
1671
remaining_children.extend(parent_to_children[file_id])
1507
1675
def _bytes_to_utf8name_key(bytes):
1508
1676
"""Get the file_id, revision_id key out of bytes."""
1547
1715
def _get_mutable_inventory(self):
1548
1716
"""See CommonInventory._get_mutable_inventory."""
1549
1717
entries = self.iter_entries()
1550
if self.root_id is not None:
1552
inv = Inventory(self.root_id, self.revision_id)
1718
inv = Inventory(None, self.revision_id)
1553
1719
for path, inv_entry in entries:
1720
inv.add(inv_entry.copy())
1557
1723
def create_by_apply_delta(self, inventory_delta, new_revision_id,
1558
1724
propagate_caches=False):
1559
1725
"""Create a new CHKInventory by applying inventory_delta to this one.
1727
See the inventory developers documentation for the theory behind
1561
1730
:param inventory_delta: The inventory delta to apply. See
1562
1731
Inventory.apply_delta for details.
1563
1732
:param new_revision_id: The revision id of the resulting CHKInventory.
1595
1770
result.parent_id_basename_to_file_id = None
1596
1771
result.root_id = self.root_id
1597
1772
id_to_entry_delta = []
1773
# inventory_delta is only traversed once, so we just update the
1775
# Check for repeated file ids
1776
inventory_delta = _check_delta_unique_ids(inventory_delta)
1777
# Repeated old paths
1778
inventory_delta = _check_delta_unique_old_paths(inventory_delta)
1779
# Check for repeated new paths
1780
inventory_delta = _check_delta_unique_new_paths(inventory_delta)
1781
# Check for entries that don't match the fileid
1782
inventory_delta = _check_delta_ids_match_entry(inventory_delta)
1783
# Check for nonsense fileids
1784
inventory_delta = _check_delta_ids_are_valid(inventory_delta)
1785
# Check for new_path <-> entry consistency
1786
inventory_delta = _check_delta_new_path_entry_both_or_None(
1788
# All changed entries need to have their parents be directories and be
1789
# at the right path. This set contains (path, id) tuples.
1791
# When we delete an item, all the children of it must be either deleted
1792
# or altered in their own right. As we batch process the change via
1793
# CHKMap.apply_delta, we build a set of things to use to validate the
1598
1797
for old_path, new_path, file_id, entry in inventory_delta:
1599
1798
# file id changes
1600
1799
if new_path == '':
1634
1840
new_key = self._parent_id_basename_key(entry)
1635
1841
new_value = file_id
1842
# If the two keys are the same, the value will be unchanged
1843
# as its always the file id for this entry.
1636
1844
if old_key != new_key:
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))
1845
# Transform a change into explicit delete/add preserving
1846
# a possible match on the key from a different file id.
1847
if old_key is not None:
1848
parent_id_basename_delta.setdefault(
1849
old_key, [None, None])[0] = old_key
1850
if new_key is not None:
1851
parent_id_basename_delta.setdefault(
1852
new_key, [None, None])[1] = new_value
1853
# validate that deletes are complete.
1854
for file_id in deletes:
1855
entry = self[file_id]
1856
if entry.kind != 'directory':
1858
# This loop could potentially be better by using the id_basename
1859
# map to just get the child file ids.
1860
for child in entry.children.values():
1861
if child.file_id not in altered:
1862
raise errors.InconsistentDelta(self.id2path(child.file_id),
1863
child.file_id, "Child not deleted or reparented when "
1640
1865
result.id_to_entry.apply_delta(id_to_entry_delta)
1641
1866
if parent_id_basename_delta:
1642
result.parent_id_basename_to_file_id.apply_delta(parent_id_basename_delta)
1867
# Transform the parent_id_basename delta data into a linear delta
1868
# with only one record for a given key. Optimally this would allow
1869
# re-keying, but its simpler to just output that as a delete+add
1870
# to spend less time calculating the delta.
1872
for key, (old_key, value) in parent_id_basename_delta.iteritems():
1873
if value is not None:
1874
delta_list.append((old_key, key, value))
1876
delta_list.append((old_key, None, None))
1877
result.parent_id_basename_to_file_id.apply_delta(delta_list)
1878
parents.discard(('', None))
1879
for parent_path, parent in parents:
1881
if result[parent].kind != 'directory':
1882
raise errors.InconsistentDelta(result.id2path(parent), parent,
1883
'Not a directory, but given children')
1884
except errors.NoSuchId:
1885
raise errors.InconsistentDelta("<unknown>", parent,
1886
"Parent is not present in resulting inventory.")
1887
if result.path2id(parent_path) != parent:
1888
raise errors.InconsistentDelta(parent_path, parent,
1889
"Parent has wrong path %r." % result.path2id(parent_path))
1709
1956
:param maximum_size: The CHKMap node size limit.
1710
1957
:param search_key_name: The identifier for the search key function
1712
result = CHKInventory(search_key_name)
1959
result = klass(search_key_name)
1713
1960
result.revision_id = inventory.revision_id
1714
1961
result.root_id = inventory.root.file_id
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 = []
1963
entry_to_bytes = result._entry_to_bytes
1964
parent_id_basename_key = result._parent_id_basename_key
1965
id_to_entry_dict = {}
1966
parent_id_basename_dict = {}
1725
1967
for path, entry in inventory.iter_entries():
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)
1968
id_to_entry_dict[(entry.file_id,)] = entry_to_bytes(entry)
1969
p_id_key = parent_id_basename_key(entry)
1970
parent_id_basename_dict[p_id_key] = entry.file_id
1972
result._populate_from_dicts(chk_store, id_to_entry_dict,
1973
parent_id_basename_dict, maximum_size=maximum_size)
1976
def _populate_from_dicts(self, chk_store, id_to_entry_dict,
1977
parent_id_basename_dict, maximum_size):
1978
search_key_func = chk_map.search_key_registry.get(self._search_key_name)
1979
root_key = chk_map.CHKMap.from_dict(chk_store, id_to_entry_dict,
1980
maximum_size=maximum_size, key_width=1,
1981
search_key_func=search_key_func)
1982
self.id_to_entry = chk_map.CHKMap(chk_store, root_key,
1984
root_key = chk_map.CHKMap.from_dict(chk_store,
1985
parent_id_basename_dict,
1986
maximum_size=maximum_size, key_width=2,
1987
search_key_func=search_key_func)
1988
self.parent_id_basename_to_file_id = chk_map.CHKMap(chk_store,
1989
root_key, search_key_func)
1735
1991
def _parent_id_basename_key(self, entry):
1736
1992
"""Create a key for a entry in a parent_id_basename_to_file_id index."""
1737
1993
if entry.parent_id is not None:
1754
2010
# really we're passing an inventory, not a tree...
1755
2011
raise errors.NoSuchId(self, file_id)
2013
def _getitems(self, file_ids):
2014
"""Similar to __getitem__, but lets you query for multiple.
2016
The returned order is undefined. And currently if an item doesn't
2017
exist, it isn't included in the output.
2021
for file_id in file_ids:
2022
entry = self._fileid_to_entry_cache.get(file_id, None)
2024
remaining.append(file_id)
2026
result.append(entry)
2027
file_keys = [(f,) for f in remaining]
2028
for file_key, value in self.id_to_entry.iteritems(file_keys):
2029
entry = self._bytes_to_entry(value)
2030
result.append(entry)
2031
self._fileid_to_entry_cache[entry.file_id] = entry
1757
2034
def has_id(self, file_id):
1758
2035
# Perhaps have an explicit 'contains' method on CHKMap ?
1759
2036
if self._fileid_to_entry_cache.get(file_id, None) is not None:
1896
2173
def path2id(self, name):
1897
2174
"""See CommonInventory.path2id()."""
2175
# TODO: perhaps support negative hits?
1898
2176
result = self._path_to_fileid_cache.get(name, None)
1900
result = CommonInventory.path2id(self, name)
1901
self._path_to_fileid_cache[name] = result
2177
if result is not None:
2179
if isinstance(name, basestring):
2180
names = osutils.splitpath(name)
2183
current_id = self.root_id
2184
if current_id is None:
2186
parent_id_index = self.parent_id_basename_to_file_id
2187
for basename in names:
2188
# TODO: Cache each path we figure out in this function.
2189
basename_utf8 = basename.encode('utf8')
2190
key_filter = [(current_id, basename_utf8)]
2192
for (parent_id, name_utf8), file_id in parent_id_index.iteritems(
2193
key_filter=key_filter):
2194
if parent_id != current_id or name_utf8 != basename_utf8:
2195
raise errors.BzrError("corrupt inventory lookup! "
2196
"%r %r %r %r" % (parent_id, current_id, name_utf8,
2200
current_id = file_id
2201
self._path_to_fileid_cache[name] = current_id
1904
2204
def to_lines(self):
1905
2205
"""Serialise the inventory to lines."""
2039
2339
_NAME_RE = re.compile(r'^[^/\\]+$')
2041
2341
return bool(_NAME_RE.match(name))
2344
def _check_delta_unique_ids(delta):
2345
"""Decorate a delta and check that the file ids in it are unique.
2347
:return: A generator over delta.
2351
length = len(ids) + 1
2353
if len(ids) != length:
2354
raise errors.InconsistentDelta(item[0] or item[1], item[2],
2359
def _check_delta_unique_new_paths(delta):
2360
"""Decorate a delta and check that the new paths in it are unique.
2362
:return: A generator over delta.
2366
length = len(paths) + 1
2368
if path is not None:
2370
if len(paths) != length:
2371
raise errors.InconsistentDelta(path, item[2], "repeated path")
2375
def _check_delta_unique_old_paths(delta):
2376
"""Decorate a delta and check that the old paths in it are unique.
2378
:return: A generator over delta.
2382
length = len(paths) + 1
2384
if path is not None:
2386
if len(paths) != length:
2387
raise errors.InconsistentDelta(path, item[2], "repeated path")
2391
def _check_delta_ids_are_valid(delta):
2392
"""Decorate a delta and check that the ids in it are valid.
2394
:return: A generator over delta.
2399
raise errors.InconsistentDelta(item[0] or item[1], item[2],
2400
"entry with file_id None %r" % entry)
2401
if type(item[2]) != str:
2402
raise errors.InconsistentDelta(item[0] or item[1], item[2],
2403
"entry with non bytes file_id %r" % entry)
2407
def _check_delta_ids_match_entry(delta):
2408
"""Decorate a delta and check that the ids in it match the entry.file_id.
2410
:return: A generator over delta.
2414
if entry is not None:
2415
if entry.file_id != item[2]:
2416
raise errors.InconsistentDelta(item[0] or item[1], item[2],
2417
"mismatched id with %r" % entry)
2421
def _check_delta_new_path_entry_both_or_None(delta):
2422
"""Decorate a delta and check that the new_path and entry are paired.
2424
:return: A generator over delta.
2429
if new_path is None and entry is not None:
2430
raise errors.InconsistentDelta(item[0], item[1],
2431
"Entry with no new_path")
2432
if new_path is not None and entry is None:
2433
raise errors.InconsistentDelta(new_path, item[1],
2434
"new_path with no entry")