~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: John Arbash Meinel
  • Date: 2006-10-11 00:23:23 UTC
  • mfrom: (2070 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2071.
  • Revision ID: john@arbash-meinel.com-20061011002323-82ba88c293d7caff
[merge] bzr.dev 2070

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
# created, but it's not for now.
28
28
ROOT_ID = "TREE_ROOT"
29
29
 
30
 
 
31
 
import collections
32
 
import os.path
 
30
import os
33
31
import re
34
32
import sys
 
33
 
 
34
from bzrlib.lazy_import import lazy_import
 
35
lazy_import(globals(), """
 
36
import collections
35
37
import tarfile
36
 
import types
37
38
 
38
39
import bzrlib
39
 
from bzrlib import errors, osutils
40
 
from bzrlib.osutils import (pumpfile, quotefn, splitpath, joinpath,
41
 
                            pathjoin, sha_strings)
42
 
from bzrlib.errors import (NotVersionedError, InvalidEntryName,
43
 
                           BzrError, BzrCheckError, BinaryFile)
 
40
from bzrlib import (
 
41
    errors,
 
42
    osutils,
 
43
    symbol_versioning,
 
44
    )
 
45
""")
 
46
 
 
47
from bzrlib.errors import (
 
48
    BzrCheckError,
 
49
    BzrError,
 
50
    )
44
51
from bzrlib.trace import mutter
45
52
 
46
53
 
81
88
    InventoryDirectory('123', 'src', parent_id='TREE_ROOT', revision=None)
82
89
    >>> i.add(InventoryFile('2323', 'hello.c', parent_id='123'))
83
90
    InventoryFile('2323', 'hello.c', parent_id='123', sha1=None, len=None)
84
 
    >>> shouldbe = {0: 'src', 1: pathjoin('src','hello.c')}
 
91
    >>> shouldbe = {0: '', 1: 'src', 2: 'src/hello.c'}
85
92
    >>> for ix, j in enumerate(i.iter_entries()):
86
93
    ...   print (j[0] == shouldbe[ix], j[1])
87
94
    ... 
 
95
    (True, InventoryDirectory('TREE_ROOT', '', parent_id=None, revision=None))
88
96
    (True, InventoryDirectory('123', 'src', parent_id='TREE_ROOT', revision=None))
89
97
    (True, InventoryFile('2323', 'hello.c', parent_id='123', sha1=None, len=None))
90
98
    >>> i.add(InventoryFile('2323', 'bye.c', '123'))
107
115
    ...     print path
108
116
    ...     assert i.path2id(path)
109
117
    ... 
 
118
    <BLANKLINE>
110
119
    src
111
120
    src/bye.c
112
121
    src/hello.c
243
252
 
244
253
    def get_tar_item(self, root, dp, now, tree):
245
254
        """Get a tarfile item and a file stream for its content."""
246
 
        item = tarfile.TarInfo(pathjoin(root, dp))
 
255
        item = tarfile.TarInfo(osutils.pathjoin(root, dp).encode('utf8'))
247
256
        # TODO: would be cool to actually set it to the timestamp of the
248
257
        # revision it was last changed
249
258
        item.mtime = now
278
287
        """
279
288
        assert isinstance(name, basestring), name
280
289
        if '/' in name or '\\' in name:
281
 
            raise InvalidEntryName(name=name)
 
290
            raise errors.InvalidEntryName(name=name)
282
291
        self.executable = False
283
292
        self.revision = None
284
293
        self.text_sha1 = None
293
302
        """Return a short kind indicator useful for appending to names."""
294
303
        raise BzrError('unknown kind %r' % self.kind)
295
304
 
296
 
    known_kinds = ('file', 'directory', 'symlink', 'root_directory')
 
305
    known_kinds = ('file', 'directory', 'symlink')
297
306
 
298
307
    def _put_in_tar(self, item, tree):
299
308
        """populate item for stashing in a tar, and return the content stream.
308
317
        
309
318
        This is a template method - implement _put_on_disk in subclasses.
310
319
        """
311
 
        fullpath = pathjoin(dest, dp)
 
320
        fullpath = osutils.pathjoin(dest, dp)
312
321
        self._put_on_disk(fullpath, tree)
313
 
        mutter("  export {%s} kind %s to %s", self.file_id,
314
 
                self.kind, fullpath)
 
322
        # mutter("  export {%s} kind %s to %s", self.file_id,
 
323
        #         self.kind, fullpath)
315
324
 
316
325
    def _put_on_disk(self, fullpath, tree):
317
326
        """Put this entry onto disk at fullpath, from tree tree."""
322
331
 
323
332
    @staticmethod
324
333
    def versionable_kind(kind):
325
 
        return kind in ('file', 'directory', 'symlink')
 
334
        return (kind in ('file', 'directory', 'symlink'))
326
335
 
327
336
    def check(self, checker, rev_id, inv, tree):
328
337
        """Check this inventory entry is intact.
408
417
        This means that all its fields are populated, that it has its
409
418
        text stored in the text store or weave.
410
419
        """
411
 
        mutter('new parents of %s are %r', path, previous_entries)
 
420
        # mutter('new parents of %s are %r', path, previous_entries)
412
421
        self._read_tree_state(path, work_tree)
413
422
        # TODO: Where should we determine whether to reuse a
414
423
        # previous revision id or create a new revision? 20060606
416
425
            # cannot be unchanged unless there is only one parent file rev.
417
426
            parent_ie = previous_entries.values()[0]
418
427
            if self._unchanged(parent_ie):
419
 
                mutter("found unchanged entry")
 
428
                # mutter("found unchanged entry")
420
429
                self.revision = parent_ie.revision
421
430
                return "unchanged"
422
431
        return self._snapshot_into_revision(revision, previous_entries, 
433
442
 
434
443
        :returns: String description of the commit (e.g. "merged", "modified"), etc.
435
444
        """
436
 
        mutter('new revision {%s} for {%s}', revision, self.file_id)
 
445
        # mutter('new revision {%s} for {%s}', revision, self.file_id)
437
446
        self.revision = revision
438
447
        self._snapshot_text(previous_entries, work_tree, commit_builder)
439
448
 
507
516
    def __init__(self, file_id):
508
517
        self.file_id = file_id
509
518
        self.children = {}
510
 
        self.kind = 'root_directory'
 
519
        self.kind = 'directory'
511
520
        self.parent_id = None
512
521
        self.name = u''
513
522
        self.revision = None
 
523
        symbol_versioning.warn('RootEntry is deprecated as of bzr 0.10.'
 
524
                               '  Please use InventoryDirectory instead.',
 
525
                               DeprecationWarning, stacklevel=2)
514
526
 
515
527
    def __eq__(self, other):
516
528
        if not isinstance(other, RootEntry):
639
651
            else:
640
652
                text_diff(to_label, to_text,
641
653
                          from_label, from_text, output_to)
642
 
        except BinaryFile:
 
654
        except errors.BinaryFile:
643
655
            if reverse:
644
656
                label_pair = (to_label, from_label)
645
657
            else:
671
683
 
672
684
    def _put_on_disk(self, fullpath, tree):
673
685
        """See InventoryEntry._put_on_disk."""
674
 
        pumpfile(tree.get_file(self.file_id), file(fullpath, 'wb'))
 
686
        osutils.pumpfile(tree.get_file(self.file_id), file(fullpath, 'wb'))
675
687
        if tree.is_executable(self.file_id):
676
688
            os.chmod(fullpath, 0755)
677
689
 
840
852
    May also look up by name:
841
853
 
842
854
    >>> [x[0] for x in inv.iter_entries()]
843
 
    [u'hello.c']
 
855
    ['', u'hello.c']
844
856
    >>> inv = Inventory('TREE_ROOT-12345678-12345678')
845
857
    >>> inv.add(InventoryFile('123-123', 'hello.c', ROOT_ID))
846
858
    InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT-12345678-12345678', sha1=None, len=None)
859
871
        # root id. Rather than generating a random one here.
860
872
        #if root_id is None:
861
873
        #    root_id = bzrlib.branch.gen_file_id('TREE_ROOT')
862
 
        self.root = RootEntry(root_id)
 
874
        if root_id is not None:
 
875
            self._set_root(InventoryDirectory(root_id, '', None))
 
876
        else:
 
877
            self.root = None
 
878
            self._byid = {}
863
879
        # FIXME: this isn't ever used, changing it to self.revision may break
864
880
        # things. TODO make everything use self.revision_id
865
881
        self.revision_id = revision_id
 
882
 
 
883
    def _set_root(self, ie):
 
884
        self.root = ie
866
885
        self._byid = {self.root.file_id: self.root}
867
886
 
868
887
    def copy(self):
869
888
        # TODO: jam 20051218 Should copy also copy the revision_id?
870
 
        other = Inventory(self.root.file_id)
 
889
        entries = self.iter_entries()
 
890
        other = Inventory(entries.next()[1].file_id)
871
891
        # copy recursively so we know directories will be added before
872
892
        # their children.  There are more efficient ways than this...
873
 
        for path, entry in self.iter_entries():
874
 
            if entry == self.root:
875
 
                continue
 
893
        for path, entry in entries():
876
894
            other.add(entry.copy())
877
895
        return other
878
896
 
888
906
        if from_dir is None:
889
907
            assert self.root
890
908
            from_dir = self.root
 
909
            yield '', self.root
891
910
        elif isinstance(from_dir, basestring):
892
911
            from_dir = self._byid[from_dir]
893
912
            
940
959
        if from_dir is None:
941
960
            assert self.root
942
961
            from_dir = self.root
 
962
            yield '', self.root
943
963
        elif isinstance(from_dir, basestring):
944
964
            from_dir = self._byid[from_dir]
945
965
            
968
988
            kids = dir_ie.children.items()
969
989
            kids.sort()
970
990
            for name, ie in kids:
971
 
                child_path = pathjoin(dir_path, name)
 
991
                child_path = osutils.pathjoin(dir_path, name)
972
992
                accum.append((child_path, ie))
973
993
                if ie.kind == 'directory':
974
994
                    descend(ie, child_path)
987
1007
            kids.sort()
988
1008
 
989
1009
            for name, child_ie in kids:
990
 
                child_path = pathjoin(parent_path, name)
 
1010
                child_path = osutils.pathjoin(parent_path, name)
991
1011
                descend(child_ie, child_path)
992
1012
        descend(self.root, u'')
993
1013
        return accum
1003
1023
        >>> '456' in inv
1004
1024
        False
1005
1025
        """
1006
 
        return file_id in self._byid
 
1026
        return (file_id in self._byid)
1007
1027
 
1008
1028
    def __getitem__(self, file_id):
1009
1029
        """Return the entry for given file_id.
1039
1059
        if entry.file_id in self._byid:
1040
1060
            raise BzrError("inventory already contains entry with id {%s}" % entry.file_id)
1041
1061
 
1042
 
        if entry.parent_id == ROOT_ID or entry.parent_id is None:
 
1062
        if entry.parent_id is None:
 
1063
            assert self.root is None and len(self._byid) == 0
 
1064
            self._set_root(entry)
 
1065
            return entry
 
1066
        if entry.parent_id == ROOT_ID:
 
1067
            assert self.root is not None, self
1043
1068
            entry.parent_id = self.root.file_id
1044
1069
 
1045
1070
        try:
1049
1074
 
1050
1075
        if entry.name in parent.children:
1051
1076
            raise BzrError("%s is already versioned" %
1052
 
                    pathjoin(self.id2path(parent.file_id), entry.name))
 
1077
                    osutils.pathjoin(self.id2path(parent.file_id), entry.name))
1053
1078
 
1054
1079
        self._byid[entry.file_id] = entry
1055
1080
        parent.children[entry.name] = entry
1067
1092
        if len(parts) == 0:
1068
1093
            if file_id is None:
1069
1094
                file_id = bzrlib.workingtree.gen_root_id()
1070
 
            self.root = RootEntry(file_id)
 
1095
            self.root = InventoryDirectory(file_id, '', None)
1071
1096
            self._byid = {self.root.file_id: self.root}
1072
1097
            return
1073
1098
        else:
1074
1099
            parent_path = parts[:-1]
1075
1100
            parent_id = self.path2id(parent_path)
1076
1101
            if parent_id is None:
1077
 
                raise NotVersionedError(path=parent_path)
 
1102
                raise errors.NotVersionedError(path=parent_path)
1078
1103
        ie = make_entry(kind, parts[-1], parent_id, file_id)
1079
1104
        return self.add(ie)
1080
1105
 
1174
1199
 
1175
1200
        Returns None IFF the path is not found.
1176
1201
        """
1177
 
        if isinstance(name, types.StringTypes):
1178
 
            name = splitpath(name)
 
1202
        if isinstance(name, basestring):
 
1203
            name = osutils.splitpath(name)
1179
1204
 
1180
1205
        # mutter("lookup path %r" % name)
1181
1206
 
1196
1221
        return bool(self.path2id(names))
1197
1222
 
1198
1223
    def has_id(self, file_id):
1199
 
        return self._byid.has_key(file_id)
 
1224
        return (file_id in self._byid)
 
1225
 
 
1226
    def remove_recursive_id(self, file_id):
 
1227
        """Remove file_id, and children, from the inventory.
 
1228
        
 
1229
        :param file_id: A file_id to remove.
 
1230
        """
 
1231
        to_find_delete = [self._byid[file_id]]
 
1232
        to_delete = []
 
1233
        while to_find_delete:
 
1234
            ie = to_find_delete.pop()
 
1235
            to_delete.append(ie.file_id)
 
1236
            if ie.kind == 'directory':
 
1237
                to_find_delete.extend(ie.children.values())
 
1238
        for file_id in reversed(to_delete):
 
1239
            ie = self[file_id]
 
1240
            del self._byid[file_id]
 
1241
            if ie.parent_id is not None:
 
1242
                del self[ie.parent_id].children[ie.name]
1200
1243
 
1201
1244
    def rename(self, file_id, new_parent_id, new_name):
1202
1245
        """Move a file within the inventory.