131
128
RENAMED = 'renamed'
132
129
MODIFIED_AND_RENAMED = 'modified and renamed'
131
__slots__ = ['file_id', 'revision', 'parent_id', 'name']
133
# Attributes that all InventoryEntry instances are expected to have, but
134
# that don't vary for all kinds of entry. (e.g. symlink_target is only
135
# relevant to InventoryLink, so there's no reason to make every
136
# InventoryFile instance allocate space to hold a value for it.)
137
# Attributes that only vary for files: executable, text_sha1, text_size,
143
# Attributes that only vary for symlinks: symlink_target
144
symlink_target = None
145
# Attributes that only vary for tree-references: reference_revision
146
reference_revision = None
136
149
def detect_changes(self, old_entry):
137
150
"""Return a (text_modified, meta_modified) from this to old_entry.
176
189
candidates[ie.revision] = ie
177
190
return candidates
179
@deprecated_method(deprecated_in((1, 6, 0)))
180
def get_tar_item(self, root, dp, now, tree):
181
"""Get a tarfile item and a file stream for its content."""
182
item = tarfile.TarInfo(osutils.pathjoin(root, dp).encode('utf8'))
183
# TODO: would be cool to actually set it to the timestamp of the
184
# revision it was last changed
186
fileobj = self._put_in_tar(item, tree)
189
192
def has_text(self):
190
193
"""Return true if the object this entry represents has textual data.
200
def __init__(self, file_id, name, parent_id, text_id=None):
203
def __init__(self, file_id, name, parent_id):
201
204
"""Create an InventoryEntry
203
206
The filename must be a single component, relative to the
215
218
if '/' in name or '\\' in name:
216
219
raise errors.InvalidEntryName(name=name)
217
self.executable = False
220
self.file_id = file_id
218
221
self.revision = None
219
self.text_sha1 = None
220
self.text_size = None
221
self.file_id = file_id
223
self.text_id = text_id
224
223
self.parent_id = parent_id
225
self.symlink_target = None
226
self.reference_revision = None
228
225
def kind_character(self):
229
226
"""Return a short kind indicator useful for appending to names."""
232
229
known_kinds = ('file', 'directory', 'symlink')
234
def _put_in_tar(self, item, tree):
235
"""populate item for stashing in a tar, and return the content stream.
237
If no content is available, return None.
239
raise BzrError("don't know how to export {%s} of kind %r" %
240
(self.file_id, self.kind))
242
@deprecated_method(deprecated_in((1, 6, 0)))
243
def put_on_disk(self, dest, dp, tree):
244
"""Create a representation of self on disk in the prefix dest.
246
This is a template method - implement _put_on_disk in subclasses.
248
fullpath = osutils.pathjoin(dest, dp)
249
self._put_on_disk(fullpath, tree)
250
# mutter(" export {%s} kind %s to %s", self.file_id,
251
# self.kind, fullpath)
253
def _put_on_disk(self, fullpath, tree):
254
"""Put this entry onto disk at fullpath, from tree tree."""
255
raise BzrError("don't know how to export {%s} of kind %r" % (self.file_id, self.kind))
257
231
def sorted_children(self):
258
232
return sorted(self.children.items())
400
class RootEntry(InventoryEntry):
402
__slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
403
'text_id', 'parent_id', 'children', 'executable',
404
'revision', 'symlink_target', 'reference_revision']
406
def _check(self, checker, rev_id):
407
"""See InventoryEntry._check"""
409
def __init__(self, file_id):
410
self.file_id = file_id
412
self.kind = 'directory'
413
self.parent_id = None
416
symbol_versioning.warn('RootEntry is deprecated as of bzr 0.10.'
417
' Please use InventoryDirectory instead.',
418
DeprecationWarning, stacklevel=2)
420
def __eq__(self, other):
421
if not isinstance(other, RootEntry):
422
return NotImplemented
424
return (self.file_id == other.file_id) \
425
and (self.children == other.children)
428
374
class InventoryDirectory(InventoryEntry):
429
375
"""A directory in an inventory."""
431
__slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
432
'text_id', 'parent_id', 'children', 'executable',
433
'revision', 'symlink_target', 'reference_revision']
377
__slots__ = ['children']
435
381
def _check(self, checker, rev_id):
436
382
"""See InventoryEntry._check"""
437
if (self.text_sha1 is not None or self.text_size is not None or
438
self.text_id is not None):
439
checker._report_items.append('directory {%s} has text in revision {%s}'
440
% (self.file_id, rev_id))
441
383
# In non rich root repositories we do not expect a file graph for the
443
385
if self.name == '' and not checker.rich_roots:
459
401
def __init__(self, file_id, name, parent_id):
460
402
super(InventoryDirectory, self).__init__(file_id, name, parent_id)
461
403
self.children = {}
462
self.kind = 'directory'
464
405
def kind_character(self):
465
406
"""See InventoryEntry.kind_character."""
468
def _put_in_tar(self, item, tree):
469
"""See InventoryEntry._put_in_tar."""
470
item.type = tarfile.DIRTYPE
477
def _put_on_disk(self, fullpath, tree):
478
"""See InventoryEntry._put_on_disk."""
482
410
class InventoryFile(InventoryEntry):
483
411
"""A file in an inventory."""
485
__slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
486
'text_id', 'parent_id', 'children', 'executable',
487
'revision', 'symlink_target', 'reference_revision']
413
__slots__ = ['text_sha1', 'text_size', 'text_id', 'executable']
417
def __init__(self, file_id, name, parent_id):
418
super(InventoryFile, self).__init__(file_id, name, parent_id)
419
self.text_sha1 = None
420
self.text_size = None
422
self.executable = False
489
424
def _check(self, checker, tree_revision_id):
490
425
"""See InventoryEntry._check"""
533
468
"""See InventoryEntry.has_text."""
536
def __init__(self, file_id, name, parent_id):
537
super(InventoryFile, self).__init__(file_id, name, parent_id)
540
471
def kind_character(self):
541
472
"""See InventoryEntry.kind_character."""
544
def _put_in_tar(self, item, tree):
545
"""See InventoryEntry._put_in_tar."""
546
item.type = tarfile.REGTYPE
547
fileobj = tree.get_file(self.file_id)
548
item.size = self.text_size
549
if tree.is_executable(self.file_id):
555
def _put_on_disk(self, fullpath, tree):
556
"""See InventoryEntry._put_on_disk."""
557
osutils.pumpfile(tree.get_file(self.file_id), file(fullpath, 'wb'))
558
if tree.is_executable(self.file_id):
559
os.chmod(fullpath, 0755)
561
475
def _read_tree_state(self, path, work_tree):
562
476
"""See InventoryEntry._read_tree_state."""
563
477
self.text_sha1 = work_tree.get_file_sha1(self.file_id, path=path)
595
509
class InventoryLink(InventoryEntry):
596
510
"""A file in an inventory."""
598
__slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
599
'text_id', 'parent_id', 'children', 'executable',
600
'revision', 'symlink_target', 'reference_revision']
512
__slots__ = ['symlink_target']
516
def __init__(self, file_id, name, parent_id):
517
super(InventoryLink, self).__init__(file_id, name, parent_id)
518
self.symlink_target = None
602
520
def _check(self, checker, tree_revision_id):
603
521
"""See InventoryEntry._check"""
604
if self.text_sha1 is not None or self.text_size is not None or self.text_id is not None:
605
checker._report_items.append(
606
'symlink {%s} has text in revision {%s}'
607
% (self.file_id, tree_revision_id))
608
522
if self.symlink_target is None:
609
523
checker._report_items.append(
610
524
'symlink {%s} has no target in revision {%s}'
648
562
differ = DiffSymlink(old_tree, new_tree, output_to)
649
563
return differ.diff_symlink(old_target, new_target)
651
def __init__(self, file_id, name, parent_id):
652
super(InventoryLink, self).__init__(file_id, name, parent_id)
653
self.kind = 'symlink'
655
565
def kind_character(self):
656
566
"""See InventoryEntry.kind_character."""
659
def _put_in_tar(self, item, tree):
660
"""See InventoryEntry._put_in_tar."""
661
item.type = tarfile.SYMTYPE
665
item.linkname = self.symlink_target
668
def _put_on_disk(self, fullpath, tree):
669
"""See InventoryEntry._put_on_disk."""
671
os.symlink(self.symlink_target, fullpath)
673
raise BzrError("Failed to create symlink %r -> %r, error: %s" % (fullpath, self.symlink_target, e))
675
569
def _read_tree_state(self, path, work_tree):
676
570
"""See InventoryEntry._read_tree_state."""
677
571
self.symlink_target = work_tree.get_symlink_target(self.file_id)
822
718
# if we finished all children, pop it off the stack
721
def _preload_cache(self):
722
"""Populate any caches, we are about to access all items.
724
The default implementation does nothing, because CommonInventory doesn't
825
729
def iter_entries_by_dir(self, from_dir=None, specific_file_ids=None,
826
730
yield_parents=False):
827
731
"""Iterate over the entries in a directory first order.
840
744
specific_file_ids = set(specific_file_ids)
841
745
# TODO? Perhaps this should return the from_dir so that the root is
842
746
# yielded? or maybe an option?
747
if from_dir is None and specific_file_ids is None:
748
# They are iterating from the root, and have not specified any
749
# specific entries to look at. All current callers fully consume the
750
# iterator, so we can safely assume we are accessing all entries
751
self._preload_cache()
843
752
if from_dir is None:
844
753
if self.root is None:
1652
1560
# parent_to_children with at least the tree root.)
1654
1562
cache = self._fileid_to_entry_cache
1656
remaining_children = collections.deque(parent_to_children[self.root_id])
1658
import pdb; pdb.set_trace()
1563
remaining_children = collections.deque(parent_to_children[self.root_id])
1660
1564
while remaining_children:
1661
1565
file_id = remaining_children.popleft()
1662
1566
ie = cache[file_id]
2080
1984
self._fileid_to_entry_cache[file_id] = ie
1987
def _preload_cache(self):
1988
"""Make sure all file-ids are in _fileid_to_entry_cache"""
1989
if self._fully_cached:
1990
return # No need to do it again
1991
# The optimal sort order is to use iteritems() directly
1992
cache = self._fileid_to_entry_cache
1993
for key, entry in self.id_to_entry.iteritems():
1995
if file_id not in cache:
1996
ie = self._bytes_to_entry(entry)
2000
last_parent_id = last_parent_ie = None
2001
pid_items = self.parent_id_basename_to_file_id.iteritems()
2002
for key, child_file_id in pid_items:
2003
if key == ('', ''): # This is the root
2004
if child_file_id != self.root_id:
2005
raise ValueError('Data inconsistency detected.'
2006
' We expected data with key ("","") to match'
2007
' the root id, but %s != %s'
2008
% (child_file_id, self.root_id))
2010
parent_id, basename = key
2011
ie = cache[child_file_id]
2012
if parent_id == last_parent_id:
2013
parent_ie = last_parent_ie
2015
parent_ie = cache[parent_id]
2016
if parent_ie.kind != 'directory':
2017
raise ValueError('Data inconsistency detected.'
2018
' An entry in the parent_id_basename_to_file_id map'
2019
' has parent_id {%s} but the kind of that object'
2020
' is %r not "directory"' % (parent_id, parent_ie.kind))
2021
if parent_ie._children is None:
2022
parent_ie._children = {}
2023
basename = basename.decode('utf-8')
2024
if basename in parent_ie._children:
2025
existing_ie = parent_ie._children[basename]
2026
if existing_ie != ie:
2027
raise ValueError('Data inconsistency detected.'
2028
' Two entries with basename %r were found'
2029
' in the parent entry {%s}'
2030
% (basename, parent_id))
2031
if basename != ie.name:
2032
raise ValueError('Data inconsistency detected.'
2033
' In the parent_id_basename_to_file_id map, file_id'
2034
' {%s} is listed as having basename %r, but in the'
2035
' id_to_entry map it is %r'
2036
% (child_file_id, basename, ie.name))
2037
parent_ie._children[basename] = ie
2038
self._fully_cached = True
2083
2040
def iter_changes(self, basis):
2084
2041
"""Generate a Tree.iter_changes change list between this and basis.
2245
2202
class CHKInventoryDirectory(InventoryDirectory):
2246
2203
"""A directory in an inventory."""
2248
__slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
2249
'text_id', 'parent_id', '_children', 'executable',
2250
'revision', 'symlink_target', 'reference_revision',
2205
__slots__ = ['_children', '_chk_inventory']
2253
2207
def __init__(self, file_id, name, parent_id, chk_inventory):
2254
2208
# Don't call InventoryDirectory.__init__ - it isn't right for this
2256
2210
InventoryEntry.__init__(self, file_id, name, parent_id)
2257
2211
self._children = None
2258
self.kind = 'directory'
2259
2212
self._chk_inventory = chk_inventory