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)
 
 
690
584
class TreeReference(InventoryEntry):
 
 
586
    __slots__ = ['reference_revision']
 
692
588
    kind = 'tree-reference'
 
694
590
    def __init__(self, file_id, name, parent_id, revision=None,
 
 
1652
1546
            # parent_to_children with at least the tree root.)
 
1654
1548
        cache = self._fileid_to_entry_cache
 
1656
 
            remaining_children = collections.deque(parent_to_children[self.root_id])
 
1658
 
            import pdb; pdb.set_trace()
 
 
1549
        remaining_children = collections.deque(parent_to_children[self.root_id])
 
1660
1550
        while remaining_children:
 
1661
1551
            file_id = remaining_children.popleft()
 
1662
1552
            ie = cache[file_id]
 
 
2245
2135
class CHKInventoryDirectory(InventoryDirectory):
 
2246
2136
    """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',
 
 
2138
    __slots__ = ['_children', '_chk_inventory']
 
2253
2140
    def __init__(self, file_id, name, parent_id, chk_inventory):
 
2254
2141
        # Don't call InventoryDirectory.__init__ - it isn't right for this
 
2256
2143
        InventoryEntry.__init__(self, file_id, name, parent_id)
 
2257
2144
        self._children = None
 
2258
 
        self.kind = 'directory'
 
2259
2145
        self._chk_inventory = chk_inventory