101
106
InventoryDirectory('2325', 'wibble', parent_id='123', revision=None)
102
107
>>> i.path2id('src/wibble')
106
109
>>> i.add(InventoryFile('2326', 'wibble.c', '2325'))
107
110
InventoryFile('2326', 'wibble.c', parent_id='2325', sha1=None, len=None, revision=None)
251
254
if self.parent_id is not None:
252
255
if not inv.has_id(self.parent_id):
253
raise BzrCheckError('missing parent {%s} in inventory for revision {%s}'
254
% (self.parent_id, rev_id))
256
raise errors.BzrCheckError(
257
'missing parent {%s} in inventory for revision {%s}' % (
258
self.parent_id, rev_id))
255
259
checker._add_entry_to_text_key_references(inv, self)
256
260
self._check(checker, rev_id)
629
633
inserted, other than through the Inventory API.
632
def __contains__(self, file_id):
633
"""True if this entry contains a file with given id.
635
>>> inv = Inventory()
636
>>> inv.add(InventoryFile('123', 'foo.c', ROOT_ID))
637
InventoryFile('123', 'foo.c', parent_id='TREE_ROOT', sha1=None, len=None, revision=None)
643
Note that this method along with __iter__ are not encouraged for use as
644
they are less clear than specific query methods - they may be rmeoved
647
return self.has_id(file_id)
649
636
def has_filename(self, filename):
650
637
return bool(self.path2id(filename))
718
705
# if we finished all children, pop it off the stack
708
def _preload_cache(self):
709
"""Populate any caches, we are about to access all items.
711
The default implementation does nothing, because CommonInventory doesn't
721
716
def iter_entries_by_dir(self, from_dir=None, specific_file_ids=None,
722
717
yield_parents=False):
723
718
"""Iterate over the entries in a directory first order.
736
731
specific_file_ids = set(specific_file_ids)
737
732
# TODO? Perhaps this should return the from_dir so that the root is
738
733
# yielded? or maybe an option?
734
if from_dir is None and specific_file_ids is None:
735
# They are iterating from the root, and have not specified any
736
# specific entries to look at. All current callers fully consume the
737
# iterator, so we can safely assume we are accessing all entries
738
self._preload_cache()
739
739
if from_dir is None:
740
740
if self.root is None:
809
809
file_id, self[file_id]))
812
def _get_mutable_inventory(self):
813
"""Returns a mutable copy of the object.
815
Some inventories are immutable, yet working trees, for example, needs
816
to mutate exisiting inventories instead of creating a new one.
818
raise NotImplementedError(self._get_mutable_inventory)
820
812
def make_entry(self, kind, name, parent_id, file_id=None):
821
813
"""Simple thunk to bzrlib.inventory.make_entry."""
822
814
return make_entry(kind, name, parent_id, file_id)
840
832
descend(self.root, u'')
843
def directories(self):
844
"""Return (path, entry) pairs for all directories, including the root.
847
def descend(parent_ie, parent_path):
848
accum.append((parent_path, parent_ie))
850
kids = [(ie.name, ie) for ie in parent_ie.children.itervalues() if ie.kind == 'directory']
853
for name, child_ie in kids:
854
child_path = osutils.pathjoin(parent_path, name)
855
descend(child_ie, child_path)
856
descend(self.root, u'')
859
835
def path2id(self, relpath):
860
836
"""Walk down through directories to return entry of last component.
1169
1141
def _add_child(self, entry):
1170
1142
"""Add an entry to the inventory, without adding it to its parent"""
1171
1143
if entry.file_id in self._byid:
1172
raise BzrError("inventory already contains entry with id {%s}" %
1144
raise errors.BzrError(
1145
"inventory already contains entry with id {%s}" %
1174
1147
self._byid[entry.file_id] = entry
1175
1148
for child in getattr(entry, 'children', {}).itervalues():
1176
1149
self._add_child(child)
1341
1314
new_name = ensure_normalized_name(new_name)
1342
1315
if not is_valid_name(new_name):
1343
raise BzrError("not an acceptable filename: %r" % new_name)
1316
raise errors.BzrError("not an acceptable filename: %r" % new_name)
1345
1318
new_parent = self._byid[new_parent_id]
1346
1319
if new_name in new_parent.children:
1347
raise BzrError("%r already exists in %r" % (new_name, self.id2path(new_parent_id)))
1320
raise errors.BzrError("%r already exists in %r" %
1321
(new_name, self.id2path(new_parent_id)))
1349
1323
new_parent_idpath = self.get_idpath(new_parent_id)
1350
1324
if file_id in new_parent_idpath:
1351
raise BzrError("cannot move directory %r into a subdirectory of itself, %r"
1325
raise errors.BzrError(
1326
"cannot move directory %r into a subdirectory of itself, %r"
1352
1327
% (self.id2path(file_id), self.id2path(new_parent_id)))
1354
1329
file_ie = self._byid[file_id]
1482
1458
if entry.kind == 'directory':
1483
1459
directories_to_expand.add(entry.file_id)
1484
1460
interesting.add(entry.parent_id)
1485
children_of_parent_id.setdefault(entry.parent_id, []
1486
).append(entry.file_id)
1461
children_of_parent_id.setdefault(entry.parent_id, set()
1462
).add(entry.file_id)
1488
1464
# Now, interesting has all of the direct parents, but not the
1489
1465
# parents of those parents. It also may have some duplicates with
1497
1473
next_parents = set()
1498
1474
for entry in self._getitems(remaining_parents):
1499
1475
next_parents.add(entry.parent_id)
1500
children_of_parent_id.setdefault(entry.parent_id, []
1501
).append(entry.file_id)
1476
children_of_parent_id.setdefault(entry.parent_id, set()
1477
).add(entry.file_id)
1502
1478
# Remove any search tips we've already processed
1503
1479
remaining_parents = next_parents.difference(interesting)
1504
1480
interesting.update(remaining_parents)
1517
1493
for entry in self._getitems(next_file_ids):
1518
1494
if entry.kind == 'directory':
1519
1495
directories_to_expand.add(entry.file_id)
1520
children_of_parent_id.setdefault(entry.parent_id, []
1521
).append(entry.file_id)
1496
children_of_parent_id.setdefault(entry.parent_id, set()
1497
).add(entry.file_id)
1522
1498
return interesting, children_of_parent_id
1524
1500
def filter(self, specific_fileids):
1602
1578
self._fileid_to_entry_cache[result.file_id] = result
1605
def _get_mutable_inventory(self):
1606
"""See CommonInventory._get_mutable_inventory."""
1607
entries = self.iter_entries()
1608
inv = Inventory(None, self.revision_id)
1609
for path, inv_entry in entries:
1610
inv.add(inv_entry.copy())
1613
1581
def create_by_apply_delta(self, inventory_delta, new_revision_id,
1614
1582
propagate_caches=False):
1615
1583
"""Create a new CHKInventory by applying inventory_delta to this one.
1970
1938
self._fileid_to_entry_cache[file_id] = ie
1941
def _preload_cache(self):
1942
"""Make sure all file-ids are in _fileid_to_entry_cache"""
1943
if self._fully_cached:
1944
return # No need to do it again
1945
# The optimal sort order is to use iteritems() directly
1946
cache = self._fileid_to_entry_cache
1947
for key, entry in self.id_to_entry.iteritems():
1949
if file_id not in cache:
1950
ie = self._bytes_to_entry(entry)
1954
last_parent_id = last_parent_ie = None
1955
pid_items = self.parent_id_basename_to_file_id.iteritems()
1956
for key, child_file_id in pid_items:
1957
if key == ('', ''): # This is the root
1958
if child_file_id != self.root_id:
1959
raise ValueError('Data inconsistency detected.'
1960
' We expected data with key ("","") to match'
1961
' the root id, but %s != %s'
1962
% (child_file_id, self.root_id))
1964
parent_id, basename = key
1965
ie = cache[child_file_id]
1966
if parent_id == last_parent_id:
1967
parent_ie = last_parent_ie
1969
parent_ie = cache[parent_id]
1970
if parent_ie.kind != 'directory':
1971
raise ValueError('Data inconsistency detected.'
1972
' An entry in the parent_id_basename_to_file_id map'
1973
' has parent_id {%s} but the kind of that object'
1974
' is %r not "directory"' % (parent_id, parent_ie.kind))
1975
if parent_ie._children is None:
1976
parent_ie._children = {}
1977
basename = basename.decode('utf-8')
1978
if basename in parent_ie._children:
1979
existing_ie = parent_ie._children[basename]
1980
if existing_ie != ie:
1981
raise ValueError('Data inconsistency detected.'
1982
' Two entries with basename %r were found'
1983
' in the parent entry {%s}'
1984
% (basename, parent_id))
1985
if basename != ie.name:
1986
raise ValueError('Data inconsistency detected.'
1987
' In the parent_id_basename_to_file_id map, file_id'
1988
' {%s} is listed as having basename %r, but in the'
1989
' id_to_entry map it is %r'
1990
% (child_file_id, basename, ie.name))
1991
parent_ie._children[basename] = ie
1992
self._fully_cached = True
1973
1994
def iter_changes(self, basis):
1974
1995
"""Generate a Tree.iter_changes change list between this and basis.
2072
2093
def path2id(self, relpath):
2073
2094
"""See CommonInventory.path2id()."""
2074
2095
# TODO: perhaps support negative hits?
2096
if isinstance(relpath, basestring):
2097
names = osutils.splitpath(relpath)
2102
relpath = osutils.pathjoin(*relpath)
2075
2103
result = self._path_to_fileid_cache.get(relpath, None)
2076
2104
if result is not None:
2078
if isinstance(relpath, basestring):
2079
names = osutils.splitpath(relpath)
2082
2106
current_id = self.root_id
2083
2107
if current_id is None:
2334
2354
raise errors.InconsistentDelta(new_path, item[1],
2335
2355
"new_path with no entry")
2359
def mutable_inventory_from_tree(tree):
2360
"""Create a new inventory that has the same contents as a specified tree.
2362
:param tree: Revision tree to create inventory from
2364
entries = tree.iter_entries_by_dir()
2365
inv = Inventory(None, tree.get_revision_id())
2366
for path, inv_entry in entries:
2367
inv.add(inv_entry.copy())