~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: Martin Pool
  • Date: 2006-01-13 08:12:22 UTC
  • mfrom: (1185.63.5 bzr.patches)
  • Revision ID: mbp@sourcefrog.net-20060113081222-6b572004a2ade0cc
[merge] test_hashcache_raise from Denys

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
 
17
# FIXME: This refactoring of the workingtree code doesn't seem to keep 
 
18
# the WorkingTree's copy of the inventory in sync with the branch.  The
 
19
# branch modifies its working inventory when it does a commit to make
 
20
# missing files permanently removed.
17
21
 
18
22
# TODO: Maybe also keep the full path of the entry, and the children?
19
23
# But those depend on its position within a particular inventory, and
31
35
import types
32
36
 
33
37
import bzrlib
34
 
from bzrlib.errors import BzrError, BzrCheckError
35
 
 
36
38
from bzrlib.osutils import (pumpfile, quotefn, splitpath, joinpath,
37
 
                            appendpath, sha_strings)
 
39
                            pathjoin, sha_strings)
38
40
from bzrlib.trace import mutter
39
 
from bzrlib.errors import NotVersionedError
 
41
from bzrlib.errors import (NotVersionedError, InvalidEntryName,
 
42
                           BzrError, BzrCheckError)
40
43
 
41
44
 
42
45
class InventoryEntry(object):
76
79
    InventoryDirectory('123', 'src', parent_id='TREE_ROOT')
77
80
    >>> i.add(InventoryFile('2323', 'hello.c', parent_id='123'))
78
81
    InventoryFile('2323', 'hello.c', parent_id='123')
79
 
    >>> for j in i.iter_entries():
80
 
    ...   print j
 
82
    >>> shouldbe = {0: 'src', 1: pathjoin('src','hello.c')}
 
83
    >>> for ix, j in enumerate(i.iter_entries()):
 
84
    ...   print (j[0] == shouldbe[ix], j[1])
81
85
    ... 
82
 
    ('src', InventoryDirectory('123', 'src', parent_id='TREE_ROOT'))
83
 
    ('src/hello.c', InventoryFile('2323', 'hello.c', parent_id='123'))
 
86
    (True, InventoryDirectory('123', 'src', parent_id='TREE_ROOT'))
 
87
    (True, InventoryFile('2323', 'hello.c', parent_id='123'))
84
88
    >>> i.add(InventoryFile('2323', 'bye.c', '123'))
85
89
    Traceback (most recent call last):
86
90
    ...
98
102
    >>> i['2326']
99
103
    InventoryFile('2326', 'wibble.c', parent_id='2325')
100
104
    >>> for path, entry in i.iter_entries():
101
 
    ...     print path.replace('\\\\', '/')     # for win32 os.sep
 
105
    ...     print path
102
106
    ...     assert i.path2id(path)
103
107
    ... 
104
108
    src
106
110
    src/hello.c
107
111
    src/wibble
108
112
    src/wibble/wibble.c
109
 
    >>> i.id2path('2326').replace('\\\\', '/')
 
113
    >>> i.id2path('2326')
110
114
    'src/wibble/wibble.c'
111
115
    """
112
116
    
133
137
        text_diff will be used for textual difference calculation.
134
138
        This is a template method, override _diff in child classes.
135
139
        """
136
 
        self._read_tree_state(tree)
 
140
        self._read_tree_state(tree.id2path(self.file_id), tree)
137
141
        if to_entry:
138
142
            # cannot diff from one kind to another - you must do a removal
139
143
            # and an addif they do not match.
140
144
            assert self.kind == to_entry.kind
141
 
            to_entry._read_tree_state(to_tree)
 
145
            to_entry._read_tree_state(to_tree.id2path(to_entry.file_id),
 
146
                                      to_tree)
142
147
        self._diff(text_diff, from_label, tree, to_label, to_entry, to_tree,
143
148
                   output_to, reverse)
144
149
 
197
202
 
198
203
    def get_tar_item(self, root, dp, now, tree):
199
204
        """Get a tarfile item and a file stream for its content."""
200
 
        item = tarfile.TarInfo(os.path.join(root, dp))
 
205
        item = tarfile.TarInfo(pathjoin(root, dp))
201
206
        # TODO: would be cool to actually set it to the timestamp of the
202
207
        # revision it was last changed
203
208
        item.mtime = now
228
233
        '123'
229
234
        >>> e = InventoryFile('123', 'src/hello.c', ROOT_ID)
230
235
        Traceback (most recent call last):
231
 
        BzrCheckError: InventoryEntry name 'src/hello.c' is invalid
 
236
        InvalidEntryName: Invalid entry name: src/hello.c
232
237
        """
233
238
        assert isinstance(name, basestring), name
234
239
        if '/' in name or '\\' in name:
235
 
            raise BzrCheckError('InventoryEntry name %r is invalid' % name)
236
 
        
 
240
            raise InvalidEntryName(name=name)
237
241
        self.executable = False
238
242
        self.revision = None
239
243
        self.text_sha1 = None
263
267
        
264
268
        This is a template method - implement _put_on_disk in subclasses.
265
269
        """
266
 
        fullpath = appendpath(dest, dp)
 
270
        fullpath = pathjoin(dest, dp)
267
271
        self._put_on_disk(fullpath, tree)
268
 
        mutter("  export {%s} kind %s to %s" % (self.file_id, self.kind, fullpath))
 
272
        mutter("  export {%s} kind %s to %s", self.file_id,
 
273
                self.kind, fullpath)
269
274
 
270
275
    def _put_on_disk(self, fullpath, tree):
271
276
        """Put this entry onto disk at fullpath, from tree tree."""
325
330
        text stored in the text store or weave.
326
331
        """
327
332
        mutter('new parents of %s are %r', path, previous_entries)
328
 
        self._read_tree_state(work_tree)
 
333
        self._read_tree_state(path, work_tree)
329
334
        if len(previous_entries) == 1:
330
335
            # cannot be unchanged unless there is only one parent file rev.
331
336
            parent_ie = previous_entries.values()[0]
391
396
            compatible = False
392
397
        return compatible
393
398
 
394
 
    def _read_tree_state(self, work_tree):
 
399
    def _read_tree_state(self, path, work_tree):
395
400
        """Populate fields in the inventory entry from the given tree.
396
401
        
397
402
        Note that this should be modified to be a noop on virtual trees
413
418
        self.children = {}
414
419
        self.kind = 'root_directory'
415
420
        self.parent_id = None
416
 
        self.name = ''
 
421
        self.name = u''
417
422
 
418
423
    def __eq__(self, other):
419
424
        if not isinstance(other, RootEntry):
477
482
            else:
478
483
                checker.repeated_text_cnt += 1
479
484
                return
 
485
 
 
486
        if self.file_id not in checker.checked_weaves:
 
487
            mutter('check weave {%s}', self.file_id)
 
488
            w = tree.get_weave(self.file_id)
 
489
            # Not passing a progress bar, because it creates a new
 
490
            # progress, which overwrites the current progress,
 
491
            # and doesn't look nice
 
492
            w.check()
 
493
            checker.checked_weaves[self.file_id] = True
 
494
        else:
 
495
            w = tree.get_weave_prelude(self.file_id)
 
496
 
480
497
        mutter('check version {%s} of {%s}', rev_id, self.file_id)
481
 
        file_lines = tree.get_file_lines(self.file_id)
482
498
        checker.checked_text_cnt += 1 
483
 
        if self.text_size != sum(map(len, file_lines)):
484
 
            raise BzrCheckError('text {%s} wrong size' % self.text_id)
485
 
        if self.text_sha1 != sha_strings(file_lines):
486
 
            raise BzrCheckError('text {%s} wrong sha1' % self.text_id)
 
499
        # We can't check the length, because Weave doesn't store that
 
500
        # information, and the whole point of looking at the weave's
 
501
        # sha1sum is that we don't have to extract the text.
 
502
        if self.text_sha1 != w.get_sha1(self.revision):
 
503
            raise BzrCheckError('text {%s} version {%s} wrong sha1' 
 
504
                                % (self.file_id, self.revision))
487
505
        checker.checked_texts[t] = self.text_sha1
488
506
 
489
507
    def copy(self):
547
565
        if tree.is_executable(self.file_id):
548
566
            os.chmod(fullpath, 0755)
549
567
 
550
 
    def _read_tree_state(self, work_tree):
 
568
    def _read_tree_state(self, path, work_tree):
551
569
        """See InventoryEntry._read_tree_state."""
552
570
        self.text_sha1 = work_tree.get_file_sha1(self.file_id)
553
571
        self.executable = work_tree.is_executable(self.file_id)
643
661
 
644
662
    def _put_in_tar(self, item, tree):
645
663
        """See InventoryEntry._put_in_tar."""
646
 
        iterm.type = tarfile.SYMTYPE
 
664
        item.type = tarfile.SYMTYPE
647
665
        fileobj = None
648
666
        item.size = 0
649
667
        item.mode = 0755
657
675
        except OSError,e:
658
676
            raise BzrError("Failed to create symlink %r -> %r, error: %s" % (fullpath, self.symlink_target, e))
659
677
 
660
 
    def _read_tree_state(self, work_tree):
 
678
    def _read_tree_state(self, path, work_tree):
661
679
        """See InventoryEntry._read_tree_state."""
662
680
        self.symlink_target = work_tree.get_symlink_target(self.file_id)
663
681
 
759
777
            yield name, ie
760
778
            if ie.kind == 'directory':
761
779
                for cn, cie in self.iter_entries(from_dir=ie.file_id):
762
 
                    yield os.path.join(name, cn), cie
 
780
                    yield pathjoin(name, cn), cie
763
781
 
764
782
 
765
783
    def entries(self):
772
790
            kids = dir_ie.children.items()
773
791
            kids.sort()
774
792
            for name, ie in kids:
775
 
                child_path = os.path.join(dir_path, name)
 
793
                child_path = pathjoin(dir_path, name)
776
794
                accum.append((child_path, ie))
777
795
                if ie.kind == 'directory':
778
796
                    descend(ie, child_path)
779
797
 
780
 
        descend(self.root, '')
 
798
        descend(self.root, u'')
781
799
        return accum
782
800
 
783
801
 
792
810
            kids.sort()
793
811
 
794
812
            for name, child_ie in kids:
795
 
                child_path = os.path.join(parent_path, name)
 
813
                child_path = pathjoin(parent_path, name)
796
814
                descend(child_ie, child_path)
797
 
        descend(self.root, '')
 
815
        descend(self.root, u'')
798
816
        return accum
799
817
        
800
818
 
859
877
 
860
878
        if parent.children.has_key(entry.name):
861
879
            raise BzrError("%s is already versioned" %
862
 
                    appendpath(self.id2path(parent.file_id), entry.name))
 
880
                    pathjoin(self.id2path(parent.file_id), entry.name))
863
881
 
864
882
        self._byid[entry.file_id] = entry
865
883
        parent.children[entry.name] = entry
872
890
        The immediate parent must already be versioned.
873
891
 
874
892
        Returns the new entry object."""
875
 
        from bzrlib.branch import gen_file_id
 
893
        from bzrlib.workingtree import gen_file_id
876
894
        
877
895
        parts = bzrlib.osutils.splitpath(relpath)
878
896
        if len(parts) == 0:
884
902
        parent_path = parts[:-1]
885
903
        parent_id = self.path2id(parent_path)
886
904
        if parent_id == None:
887
 
            raise NotVersionedError(parent_path)
888
 
 
 
905
            raise NotVersionedError(path=parent_path)
889
906
        if kind == 'directory':
890
907
            ie = InventoryDirectory(file_id, parts[-1], parent_id)
891
908
        elif kind == 'file':
983
1000
        >>> i = Inventory()
984
1001
        >>> e = i.add(InventoryDirectory('src-id', 'src', ROOT_ID))
985
1002
        >>> e = i.add(InventoryFile('foo-id', 'foo.c', parent_id='src-id'))
986
 
        >>> print i.id2path('foo-id').replace(os.sep, '/')
 
1003
        >>> print i.id2path('foo-id')
987
1004
        src/foo.c
988
1005
        """
989
1006
        # get all names, skipping root
990
1007
        p = [self._byid[fid].name for fid in self.get_idpath(file_id)[1:]]
991
 
        return os.sep.join(p)
 
1008
        if p:
 
1009
            return pathjoin(*p)
 
1010
        else:
 
1011
            return ''
992
1012
            
993
1013
 
994
1014