~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: Martin Pool
  • Date: 2006-08-10 01:16:16 UTC
  • mto: (1904.1.2 0.9)
  • mto: This revision was merged to the branch mainline in revision 1913.
  • Revision ID: mbp@sourcefrog.net-20060810011616-d74881eba696e746
compare_trees is deprecated in 0.9 not 0.10

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
 
import os
 
30
 
 
31
import collections
 
32
import os.path
31
33
import re
32
34
import sys
33
 
 
34
 
from bzrlib.lazy_import import lazy_import
35
 
lazy_import(globals(), """
36
 
import collections
37
35
import tarfile
 
36
import types
38
37
 
39
38
import bzrlib
40
 
from bzrlib import (
41
 
    errors,
42
 
    generate_ids,
43
 
    osutils,
44
 
    symbol_versioning,
45
 
    workingtree,
46
 
    )
47
 
""")
48
 
 
49
 
from bzrlib.errors import (
50
 
    BzrCheckError,
51
 
    BzrError,
52
 
    )
 
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)
53
44
from bzrlib.trace import mutter
54
45
 
55
46
 
90
81
    InventoryDirectory('123', 'src', parent_id='TREE_ROOT', revision=None)
91
82
    >>> i.add(InventoryFile('2323', 'hello.c', parent_id='123'))
92
83
    InventoryFile('2323', 'hello.c', parent_id='123', sha1=None, len=None)
93
 
    >>> shouldbe = {0: '', 1: 'src', 2: 'src/hello.c'}
 
84
    >>> shouldbe = {0: '', 1: 'src', 2: pathjoin('src','hello.c')}
94
85
    >>> for ix, j in enumerate(i.iter_entries()):
95
86
    ...   print (j[0] == shouldbe[ix], j[1])
96
87
    ... 
97
 
    (True, InventoryDirectory('TREE_ROOT', '', parent_id=None, revision=None))
 
88
    (True, RootEntry('TREE_ROOT', u'', parent_id=None, revision=None))
98
89
    (True, InventoryDirectory('123', 'src', parent_id='TREE_ROOT', revision=None))
99
90
    (True, InventoryFile('2323', 'hello.c', parent_id='123', sha1=None, len=None))
100
91
    >>> i.add(InventoryFile('2323', 'bye.c', '123'))
254
245
 
255
246
    def get_tar_item(self, root, dp, now, tree):
256
247
        """Get a tarfile item and a file stream for its content."""
257
 
        item = tarfile.TarInfo(osutils.pathjoin(root, dp).encode('utf8'))
 
248
        item = tarfile.TarInfo(pathjoin(root, dp))
258
249
        # TODO: would be cool to actually set it to the timestamp of the
259
250
        # revision it was last changed
260
251
        item.mtime = now
289
280
        """
290
281
        assert isinstance(name, basestring), name
291
282
        if '/' in name or '\\' in name:
292
 
            raise errors.InvalidEntryName(name=name)
 
283
            raise InvalidEntryName(name=name)
293
284
        self.executable = False
294
285
        self.revision = None
295
286
        self.text_sha1 = None
304
295
        """Return a short kind indicator useful for appending to names."""
305
296
        raise BzrError('unknown kind %r' % self.kind)
306
297
 
307
 
    known_kinds = ('file', 'directory', 'symlink')
 
298
    known_kinds = ('file', 'directory', 'symlink', 'root_directory')
308
299
 
309
300
    def _put_in_tar(self, item, tree):
310
301
        """populate item for stashing in a tar, and return the content stream.
319
310
        
320
311
        This is a template method - implement _put_on_disk in subclasses.
321
312
        """
322
 
        fullpath = osutils.pathjoin(dest, dp)
 
313
        fullpath = pathjoin(dest, dp)
323
314
        self._put_on_disk(fullpath, tree)
324
315
        # mutter("  export {%s} kind %s to %s", self.file_id,
325
316
        #         self.kind, fullpath)
333
324
 
334
325
    @staticmethod
335
326
    def versionable_kind(kind):
336
 
        return (kind in ('file', 'directory', 'symlink'))
 
327
        return kind in ('file', 'directory', 'symlink')
337
328
 
338
329
    def check(self, checker, rev_id, inv, tree):
339
330
        """Check this inventory entry is intact.
518
509
    def __init__(self, file_id):
519
510
        self.file_id = file_id
520
511
        self.children = {}
521
 
        self.kind = 'directory'
 
512
        self.kind = 'root_directory'
522
513
        self.parent_id = None
523
514
        self.name = u''
524
515
        self.revision = None
525
 
        symbol_versioning.warn('RootEntry is deprecated as of bzr 0.10.'
526
 
                               '  Please use InventoryDirectory instead.',
527
 
                               DeprecationWarning, stacklevel=2)
528
516
 
529
517
    def __eq__(self, other):
530
518
        if not isinstance(other, RootEntry):
653
641
            else:
654
642
                text_diff(to_label, to_text,
655
643
                          from_label, from_text, output_to)
656
 
        except errors.BinaryFile:
 
644
        except BinaryFile:
657
645
            if reverse:
658
646
                label_pair = (to_label, from_label)
659
647
            else:
685
673
 
686
674
    def _put_on_disk(self, fullpath, tree):
687
675
        """See InventoryEntry._put_on_disk."""
688
 
        osutils.pumpfile(tree.get_file(self.file_id), file(fullpath, 'wb'))
 
676
        pumpfile(tree.get_file(self.file_id), file(fullpath, 'wb'))
689
677
        if tree.is_executable(self.file_id):
690
678
            os.chmod(fullpath, 0755)
691
679
 
857
845
    ['', u'hello.c']
858
846
    >>> inv = Inventory('TREE_ROOT-12345678-12345678')
859
847
    >>> inv.add(InventoryFile('123-123', 'hello.c', ROOT_ID))
860
 
    Traceback (most recent call last):
861
 
    BzrError: parent_id {TREE_ROOT} not in inventory
862
 
    >>> inv.add(InventoryFile('123-123', 'hello.c', 'TREE_ROOT-12345678-12345678'))
863
848
    InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT-12345678-12345678', sha1=None, len=None)
864
849
    """
865
850
    def __init__(self, root_id=ROOT_ID, revision_id=None):
872
857
        The inventory is created with a default root directory, with
873
858
        an id of None.
874
859
        """
875
 
        if root_id is not None:
876
 
            self._set_root(InventoryDirectory(root_id, '', None))
877
 
        else:
878
 
            self.root = None
879
 
            self._byid = {}
 
860
        # We are letting Branch.create() create a unique inventory
 
861
        # root id. Rather than generating a random one here.
 
862
        #if root_id is None:
 
863
        #    root_id = bzrlib.branch.gen_file_id('TREE_ROOT')
 
864
        self.root = RootEntry(root_id)
 
865
        # FIXME: this isn't ever used, changing it to self.revision may break
 
866
        # things. TODO make everything use self.revision_id
880
867
        self.revision_id = revision_id
881
 
 
882
 
    def _set_root(self, ie):
883
 
        self.root = ie
884
868
        self._byid = {self.root.file_id: self.root}
885
869
 
886
870
    def copy(self):
903
887
    def iter_entries(self, from_dir=None):
904
888
        """Return (path, entry) pairs, in order by name."""
905
889
        if from_dir is None:
906
 
            if self.root is None:
907
 
                return
 
890
            assert self.root
908
891
            from_dir = self.root
909
892
            yield '', self.root
910
893
        elif isinstance(from_dir, basestring):
944
927
                # if we finished all children, pop it off the stack
945
928
                stack.pop()
946
929
 
947
 
    def iter_entries_by_dir(self, from_dir=None, specific_file_ids=None):
 
930
    def iter_entries_by_dir(self, from_dir=None):
948
931
        """Iterate over the entries in a directory first order.
949
932
 
950
933
        This returns all entries for a directory before returning
957
940
        # TODO? Perhaps this should return the from_dir so that the root is
958
941
        # yielded? or maybe an option?
959
942
        if from_dir is None:
960
 
            if self.root is None:
961
 
                return
962
 
            # Optimize a common case
963
 
            if specific_file_ids is not None and len(specific_file_ids) == 1:
964
 
                file_id = list(specific_file_ids)[0]
965
 
                if file_id in self:
966
 
                    yield self.id2path(file_id), self[file_id]
967
 
                return 
 
943
            assert self.root
968
944
            from_dir = self.root
969
 
            if (specific_file_ids is None or 
970
 
                self.root.file_id in specific_file_ids):
971
 
                yield '', self.root
 
945
            yield '', self.root
972
946
        elif isinstance(from_dir, basestring):
973
947
            from_dir = self._byid[from_dir]
974
 
 
975
 
        if specific_file_ids is not None:
976
 
            parents = set()
977
 
            def add_ancestors(file_id):
978
 
                if file_id not in self:
979
 
                    return
980
 
                parent_id = self[file_id].parent_id
981
 
                if parent_id is None:
982
 
                    return
983
 
                if parent_id not in parents:
984
 
                    parents.add(parent_id)
985
 
                    add_ancestors(parent_id)
986
 
            for file_id in specific_file_ids:
987
 
                add_ancestors(file_id)
988
 
        else:
989
 
            parents = None
990
948
            
991
949
        stack = [(u'', from_dir)]
992
950
        while stack:
997
955
 
998
956
                child_relpath = cur_relpath + child_name
999
957
 
1000
 
                if (specific_file_ids is None or 
1001
 
                    child_ie.file_id in specific_file_ids):
1002
 
                    yield child_relpath, child_ie
 
958
                yield child_relpath, child_ie
1003
959
 
1004
960
                if child_ie.kind == 'directory':
1005
 
                    if parents is None or child_ie.file_id in parents:
1006
 
                        child_dirs.append((child_relpath+'/', child_ie))
 
961
                    child_dirs.append((child_relpath+'/', child_ie))
1007
962
            stack.extend(reversed(child_dirs))
1008
963
 
1009
964
    def entries(self):
1016
971
            kids = dir_ie.children.items()
1017
972
            kids.sort()
1018
973
            for name, ie in kids:
1019
 
                child_path = osutils.pathjoin(dir_path, name)
 
974
                child_path = pathjoin(dir_path, name)
1020
975
                accum.append((child_path, ie))
1021
976
                if ie.kind == 'directory':
1022
977
                    descend(ie, child_path)
1035
990
            kids.sort()
1036
991
 
1037
992
            for name, child_ie in kids:
1038
 
                child_path = osutils.pathjoin(parent_path, name)
 
993
                child_path = pathjoin(parent_path, name)
1039
994
                descend(child_ie, child_path)
1040
995
        descend(self.root, u'')
1041
996
        return accum
1051
1006
        >>> '456' in inv
1052
1007
        False
1053
1008
        """
1054
 
        return (file_id in self._byid)
 
1009
        return file_id in self._byid
1055
1010
 
1056
1011
    def __getitem__(self, file_id):
1057
1012
        """Return the entry for given file_id.
1065
1020
        try:
1066
1021
            return self._byid[file_id]
1067
1022
        except KeyError:
1068
 
            # really we're passing an inventory, not a tree...
1069
 
            raise errors.NoSuchId(self, file_id)
 
1023
            if file_id is None:
 
1024
                raise BzrError("can't look up file_id None")
 
1025
            else:
 
1026
                raise BzrError("file_id {%s} not in inventory" % file_id)
1070
1027
 
1071
1028
    def get_file_kind(self, file_id):
1072
1029
        return self._byid[file_id].kind
1085
1042
        if entry.file_id in self._byid:
1086
1043
            raise BzrError("inventory already contains entry with id {%s}" % entry.file_id)
1087
1044
 
1088
 
        if entry.parent_id is None:
1089
 
            assert self.root is None and len(self._byid) == 0
1090
 
            self._set_root(entry)
1091
 
            return entry
 
1045
        if entry.parent_id == ROOT_ID or entry.parent_id is None:
 
1046
            entry.parent_id = self.root.file_id
 
1047
 
1092
1048
        try:
1093
1049
            parent = self._byid[entry.parent_id]
1094
1050
        except KeyError:
1096
1052
 
1097
1053
        if entry.name in parent.children:
1098
1054
            raise BzrError("%s is already versioned" %
1099
 
                    osutils.pathjoin(self.id2path(parent.file_id), entry.name))
 
1055
                    pathjoin(self.id2path(parent.file_id), entry.name))
1100
1056
 
1101
1057
        self._byid[entry.file_id] = entry
1102
1058
        parent.children[entry.name] = entry
1113
1069
 
1114
1070
        if len(parts) == 0:
1115
1071
            if file_id is None:
1116
 
                file_id = generate_ids.gen_root_id()
1117
 
            self.root = InventoryDirectory(file_id, '', None)
 
1072
                file_id = bzrlib.workingtree.gen_root_id()
 
1073
            self.root = RootEntry(file_id)
1118
1074
            self._byid = {self.root.file_id: self.root}
1119
 
            return self.root
 
1075
            return
1120
1076
        else:
1121
1077
            parent_path = parts[:-1]
1122
1078
            parent_id = self.path2id(parent_path)
1123
1079
            if parent_id is None:
1124
 
                raise errors.NotVersionedError(path=parent_path)
 
1080
                raise NotVersionedError(path=parent_path)
1125
1081
        ie = make_entry(kind, parts[-1], parent_id, file_id)
1126
1082
        return self.add(ie)
1127
1083
 
1221
1177
 
1222
1178
        Returns None IFF the path is not found.
1223
1179
        """
1224
 
        if isinstance(name, basestring):
1225
 
            name = osutils.splitpath(name)
 
1180
        if isinstance(name, types.StringTypes):
 
1181
            name = splitpath(name)
1226
1182
 
1227
1183
        # mutter("lookup path %r" % name)
1228
1184
 
1229
1185
        parent = self.root
1230
 
        if parent is None:
1231
 
            return None
1232
1186
        for f in name:
1233
1187
            try:
1234
 
                children = getattr(parent, 'children', None)
1235
 
                if children is None:
1236
 
                    return None
1237
 
                cie = children[f]
 
1188
                cie = parent.children[f]
1238
1189
                assert cie.name == f
1239
1190
                assert cie.parent_id == parent.file_id
1240
1191
                parent = cie
1248
1199
        return bool(self.path2id(names))
1249
1200
 
1250
1201
    def has_id(self, file_id):
1251
 
        return (file_id in self._byid)
1252
 
 
1253
 
    def remove_recursive_id(self, file_id):
1254
 
        """Remove file_id, and children, from the inventory.
1255
 
        
1256
 
        :param file_id: A file_id to remove.
1257
 
        """
1258
 
        to_find_delete = [self._byid[file_id]]
1259
 
        to_delete = []
1260
 
        while to_find_delete:
1261
 
            ie = to_find_delete.pop()
1262
 
            to_delete.append(ie.file_id)
1263
 
            if ie.kind == 'directory':
1264
 
                to_find_delete.extend(ie.children.values())
1265
 
        for file_id in reversed(to_delete):
1266
 
            ie = self[file_id]
1267
 
            del self._byid[file_id]
1268
 
            if ie.parent_id is not None:
1269
 
                del self[ie.parent_id].children[ie.name]
 
1202
        return self._byid.has_key(file_id)
1270
1203
 
1271
1204
    def rename(self, file_id, new_parent_id, new_name):
1272
1205
        """Move a file within the inventory.
1297
1230
        file_ie.name = new_name
1298
1231
        file_ie.parent_id = new_parent_id
1299
1232
 
1300
 
    def is_root(self, file_id):
1301
 
        return self.root is not None and file_id == self.root.file_id
1302
 
 
1303
1233
 
1304
1234
def make_entry(kind, name, parent_id, file_id=None):
1305
1235
    """Create an inventory entry.
1310
1240
    :param file_id: the file_id to use. if None, one will be created.
1311
1241
    """
1312
1242
    if file_id is None:
1313
 
        file_id = generate_ids.gen_file_id(name)
 
1243
        file_id = bzrlib.workingtree.gen_file_id(name)
1314
1244
 
1315
1245
    norm_name, can_access = osutils.normalized_filename(name)
1316
1246
    if norm_name != name: