~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-04-08 07:32:48 UTC
  • mfrom: (5138.1.1 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20100408073248-aj7k8qkvbv4nzlxd
(igc for parthm) update -r dotted-revno support

Show diffs side-by-side

added added

removed removed

Lines of Context:
35
35
import re
36
36
import tarfile
37
37
 
 
38
import bzrlib
38
39
from bzrlib import (
39
40
    chk_map,
40
41
    errors,
41
42
    generate_ids,
42
43
    osutils,
 
44
    symbol_versioning,
43
45
    )
44
46
""")
45
47
 
47
49
    BzrCheckError,
48
50
    BzrError,
49
51
    )
 
52
from bzrlib.symbol_versioning import deprecated_in, deprecated_method
50
53
from bzrlib.trace import mutter
51
54
from bzrlib.static_tuple import StaticTuple
52
55
 
128
131
    RENAMED = 'renamed'
129
132
    MODIFIED_AND_RENAMED = 'modified and renamed'
130
133
 
131
 
    __slots__ = ['file_id', 'revision', 'parent_id', 'name']
132
 
 
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,
138
 
    # text_id
139
 
    executable = False
140
 
    text_sha1 = None
141
 
    text_size = None
142
 
    text_id = None
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
147
 
 
 
134
    __slots__ = []
148
135
 
149
136
    def detect_changes(self, old_entry):
150
137
        """Return a (text_modified, meta_modified) from this to old_entry.
189
176
                    candidates[ie.revision] = ie
190
177
        return candidates
191
178
 
 
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
 
185
        item.mtime = now
 
186
        fileobj = self._put_in_tar(item, tree)
 
187
        return item, fileobj
 
188
 
192
189
    def has_text(self):
193
190
        """Return true if the object this entry represents has textual data.
194
191
 
200
197
        """
201
198
        return False
202
199
 
203
 
    def __init__(self, file_id, name, parent_id):
 
200
    def __init__(self, file_id, name, parent_id, text_id=None):
204
201
        """Create an InventoryEntry
205
202
 
206
203
        The filename must be a single component, relative to the
217
214
        """
218
215
        if '/' in name or '\\' in name:
219
216
            raise errors.InvalidEntryName(name=name)
 
217
        self.executable = False
 
218
        self.revision = None
 
219
        self.text_sha1 = None
 
220
        self.text_size = None
220
221
        self.file_id = file_id
221
 
        self.revision = None
222
222
        self.name = name
 
223
        self.text_id = text_id
223
224
        self.parent_id = parent_id
 
225
        self.symlink_target = None
 
226
        self.reference_revision = None
224
227
 
225
228
    def kind_character(self):
226
229
        """Return a short kind indicator useful for appending to names."""
228
231
 
229
232
    known_kinds = ('file', 'directory', 'symlink')
230
233
 
 
234
    def _put_in_tar(self, item, tree):
 
235
        """populate item for stashing in a tar, and return the content stream.
 
236
 
 
237
        If no content is available, return None.
 
238
        """
 
239
        raise BzrError("don't know how to export {%s} of kind %r" %
 
240
                       (self.file_id, self.kind))
 
241
 
 
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.
 
245
 
 
246
        This is a template method - implement _put_on_disk in subclasses.
 
247
        """
 
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)
 
252
 
 
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))
 
256
 
231
257
    def sorted_children(self):
232
258
        return sorted(self.children.items())
233
259
 
371
397
        pass
372
398
 
373
399
 
 
400
class RootEntry(InventoryEntry):
 
401
 
 
402
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
 
403
                 'text_id', 'parent_id', 'children', 'executable',
 
404
                 'revision', 'symlink_target', 'reference_revision']
 
405
 
 
406
    def _check(self, checker, rev_id):
 
407
        """See InventoryEntry._check"""
 
408
 
 
409
    def __init__(self, file_id):
 
410
        self.file_id = file_id
 
411
        self.children = {}
 
412
        self.kind = 'directory'
 
413
        self.parent_id = None
 
414
        self.name = u''
 
415
        self.revision = None
 
416
        symbol_versioning.warn('RootEntry is deprecated as of bzr 0.10.'
 
417
                               '  Please use InventoryDirectory instead.',
 
418
                               DeprecationWarning, stacklevel=2)
 
419
 
 
420
    def __eq__(self, other):
 
421
        if not isinstance(other, RootEntry):
 
422
            return NotImplemented
 
423
 
 
424
        return (self.file_id == other.file_id) \
 
425
               and (self.children == other.children)
 
426
 
 
427
 
374
428
class InventoryDirectory(InventoryEntry):
375
429
    """A directory in an inventory."""
376
430
 
377
 
    __slots__ = ['children']
378
 
 
379
 
    kind = 'directory'
 
431
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
 
432
                 'text_id', 'parent_id', 'children', 'executable',
 
433
                 'revision', 'symlink_target', 'reference_revision']
380
434
 
381
435
    def _check(self, checker, rev_id):
382
436
        """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))
383
441
        # In non rich root repositories we do not expect a file graph for the
384
442
        # root.
385
443
        if self.name == '' and not checker.rich_roots:
401
459
    def __init__(self, file_id, name, parent_id):
402
460
        super(InventoryDirectory, self).__init__(file_id, name, parent_id)
403
461
        self.children = {}
 
462
        self.kind = 'directory'
404
463
 
405
464
    def kind_character(self):
406
465
        """See InventoryEntry.kind_character."""
407
466
        return '/'
408
467
 
 
468
    def _put_in_tar(self, item, tree):
 
469
        """See InventoryEntry._put_in_tar."""
 
470
        item.type = tarfile.DIRTYPE
 
471
        fileobj = None
 
472
        item.name += '/'
 
473
        item.size = 0
 
474
        item.mode = 0755
 
475
        return fileobj
 
476
 
 
477
    def _put_on_disk(self, fullpath, tree):
 
478
        """See InventoryEntry._put_on_disk."""
 
479
        os.mkdir(fullpath)
 
480
 
409
481
 
410
482
class InventoryFile(InventoryEntry):
411
483
    """A file in an inventory."""
412
484
 
413
 
    __slots__ = ['text_sha1', 'text_size', 'text_id', 'executable']
414
 
 
415
 
    kind = 'file'
416
 
 
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
421
 
        self.text_id = None
422
 
        self.executable = False
 
485
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
 
486
                 'text_id', 'parent_id', 'children', 'executable',
 
487
                 'revision', 'symlink_target', 'reference_revision']
423
488
 
424
489
    def _check(self, checker, tree_revision_id):
425
490
        """See InventoryEntry._check"""
468
533
        """See InventoryEntry.has_text."""
469
534
        return True
470
535
 
 
536
    def __init__(self, file_id, name, parent_id):
 
537
        super(InventoryFile, self).__init__(file_id, name, parent_id)
 
538
        self.kind = 'file'
 
539
 
471
540
    def kind_character(self):
472
541
        """See InventoryEntry.kind_character."""
473
542
        return ''
474
543
 
 
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):
 
550
            item.mode = 0755
 
551
        else:
 
552
            item.mode = 0644
 
553
        return fileobj
 
554
 
 
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)
 
560
 
475
561
    def _read_tree_state(self, path, work_tree):
476
562
        """See InventoryEntry._read_tree_state."""
477
563
        self.text_sha1 = work_tree.get_file_sha1(self.file_id, path=path)
509
595
class InventoryLink(InventoryEntry):
510
596
    """A file in an inventory."""
511
597
 
512
 
    __slots__ = ['symlink_target']
513
 
 
514
 
    kind = 'symlink'
515
 
 
516
 
    def __init__(self, file_id, name, parent_id):
517
 
        super(InventoryLink, self).__init__(file_id, name, parent_id)
518
 
        self.symlink_target = None
 
598
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
 
599
                 'text_id', 'parent_id', 'children', 'executable',
 
600
                 'revision', 'symlink_target', 'reference_revision']
519
601
 
520
602
    def _check(self, checker, tree_revision_id):
521
603
        """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))
522
608
        if self.symlink_target is None:
523
609
            checker._report_items.append(
524
610
                'symlink {%s} has no target in revision {%s}'
562
648
        differ = DiffSymlink(old_tree, new_tree, output_to)
563
649
        return differ.diff_symlink(old_target, new_target)
564
650
 
 
651
    def __init__(self, file_id, name, parent_id):
 
652
        super(InventoryLink, self).__init__(file_id, name, parent_id)
 
653
        self.kind = 'symlink'
 
654
 
565
655
    def kind_character(self):
566
656
        """See InventoryEntry.kind_character."""
567
657
        return ''
568
658
 
 
659
    def _put_in_tar(self, item, tree):
 
660
        """See InventoryEntry._put_in_tar."""
 
661
        item.type = tarfile.SYMTYPE
 
662
        fileobj = None
 
663
        item.size = 0
 
664
        item.mode = 0755
 
665
        item.linkname = self.symlink_target
 
666
        return fileobj
 
667
 
 
668
    def _put_on_disk(self, fullpath, tree):
 
669
        """See InventoryEntry._put_on_disk."""
 
670
        try:
 
671
            os.symlink(self.symlink_target, fullpath)
 
672
        except OSError,e:
 
673
            raise BzrError("Failed to create symlink %r -> %r, error: %s" % (fullpath, self.symlink_target, e))
 
674
 
569
675
    def _read_tree_state(self, path, work_tree):
570
676
        """See InventoryEntry._read_tree_state."""
571
677
        self.symlink_target = work_tree.get_symlink_target(self.file_id)
583
689
 
584
690
class TreeReference(InventoryEntry):
585
691
 
586
 
    __slots__ = ['reference_revision']
587
 
 
588
692
    kind = 'tree-reference'
589
693
 
590
694
    def __init__(self, file_id, name, parent_id, revision=None,
836
940
                if ie.kind == 'directory':
837
941
                    descend(ie, child_path)
838
942
 
839
 
        if self.root is not None:
840
 
            descend(self.root, u'')
 
943
        descend(self.root, u'')
841
944
        return accum
842
945
 
843
946
    def directories(self):
1179
1282
    def add(self, entry):
1180
1283
        """Add entry to inventory.
1181
1284
 
 
1285
        To add  a file to a branch ready to be committed, use Branch.add,
 
1286
        which calls this.
 
1287
 
1182
1288
        :return: entry
1183
1289
        """
1184
1290
        if entry.file_id in self._byid:
1546
1652
            # parent_to_children with at least the tree root.)
1547
1653
            return other
1548
1654
        cache = self._fileid_to_entry_cache
1549
 
        remaining_children = collections.deque(parent_to_children[self.root_id])
 
1655
        try:
 
1656
            remaining_children = collections.deque(parent_to_children[self.root_id])
 
1657
        except:
 
1658
            import pdb; pdb.set_trace()
 
1659
            raise
1550
1660
        while remaining_children:
1551
1661
            file_id = remaining_children.popleft()
1552
1662
            ie = cache[file_id]
2135
2245
class CHKInventoryDirectory(InventoryDirectory):
2136
2246
    """A directory in an inventory."""
2137
2247
 
2138
 
    __slots__ = ['_children', '_chk_inventory']
 
2248
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
 
2249
                 'text_id', 'parent_id', '_children', 'executable',
 
2250
                 'revision', 'symlink_target', 'reference_revision',
 
2251
                 '_chk_inventory']
2139
2252
 
2140
2253
    def __init__(self, file_id, name, parent_id, chk_inventory):
2141
2254
        # Don't call InventoryDirectory.__init__ - it isn't right for this
2142
2255
        # class.
2143
2256
        InventoryEntry.__init__(self, file_id, name, parent_id)
2144
2257
        self._children = None
 
2258
        self.kind = 'directory'
2145
2259
        self._chk_inventory = chk_inventory
2146
2260
 
2147
2261
    @property