~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: wang
  • Date: 2006-10-29 13:41:32 UTC
  • mto: (2104.4.1 wang_65714)
  • mto: This revision was merged to the branch mainline in revision 2109.
  • Revision ID: wang@ubuntu-20061029134132-3d7f4216f20c4aef
Replace python's difflib by patiencediff because the worst case 
performance is cubic for difflib and people commiting large data 
files are often hurt by this. The worst case performance of patience is 
quadratic. Fix bug 65714.

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
 
from warnings import warn
38
38
 
39
39
import bzrlib
40
 
from bzrlib import errors, osutils
41
 
from bzrlib.osutils import (pumpfile, quotefn, splitpath, joinpath,
42
 
                            pathjoin, sha_strings)
43
 
from bzrlib.errors import (NotVersionedError, InvalidEntryName,
44
 
                           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
    )
45
51
from bzrlib.trace import mutter
46
52
 
47
53
 
82
88
    InventoryDirectory('123', 'src', parent_id='TREE_ROOT', revision=None)
83
89
    >>> i.add(InventoryFile('2323', 'hello.c', parent_id='123'))
84
90
    InventoryFile('2323', 'hello.c', parent_id='123', sha1=None, len=None)
85
 
    >>> shouldbe = {0: '', 1: 'src', 2: pathjoin('src','hello.c')}
 
91
    >>> shouldbe = {0: '', 1: 'src', 2: 'src/hello.c'}
86
92
    >>> for ix, j in enumerate(i.iter_entries()):
87
93
    ...   print (j[0] == shouldbe[ix], j[1])
88
94
    ... 
246
252
 
247
253
    def get_tar_item(self, root, dp, now, tree):
248
254
        """Get a tarfile item and a file stream for its content."""
249
 
        item = tarfile.TarInfo(pathjoin(root, dp))
 
255
        item = tarfile.TarInfo(osutils.pathjoin(root, dp).encode('utf8'))
250
256
        # TODO: would be cool to actually set it to the timestamp of the
251
257
        # revision it was last changed
252
258
        item.mtime = now
281
287
        """
282
288
        assert isinstance(name, basestring), name
283
289
        if '/' in name or '\\' in name:
284
 
            raise InvalidEntryName(name=name)
 
290
            raise errors.InvalidEntryName(name=name)
285
291
        self.executable = False
286
292
        self.revision = None
287
293
        self.text_sha1 = None
311
317
        
312
318
        This is a template method - implement _put_on_disk in subclasses.
313
319
        """
314
 
        fullpath = pathjoin(dest, dp)
 
320
        fullpath = osutils.pathjoin(dest, dp)
315
321
        self._put_on_disk(fullpath, tree)
316
322
        # mutter("  export {%s} kind %s to %s", self.file_id,
317
323
        #         self.kind, fullpath)
325
331
 
326
332
    @staticmethod
327
333
    def versionable_kind(kind):
328
 
        return kind in ('file', 'directory', 'symlink')
 
334
        return (kind in ('file', 'directory', 'symlink'))
329
335
 
330
336
    def check(self, checker, rev_id, inv, tree):
331
337
        """Check this inventory entry is intact.
514
520
        self.parent_id = None
515
521
        self.name = u''
516
522
        self.revision = None
517
 
        warn('RootEntry is deprecated as of bzr 0.10.  Please use '
518
 
             'InventoryDirectory instead.',
519
 
            DeprecationWarning, stacklevel=2)
 
523
        symbol_versioning.warn('RootEntry is deprecated as of bzr 0.10.'
 
524
                               '  Please use InventoryDirectory instead.',
 
525
                               DeprecationWarning, stacklevel=2)
520
526
 
521
527
    def __eq__(self, other):
522
528
        if not isinstance(other, RootEntry):
645
651
            else:
646
652
                text_diff(to_label, to_text,
647
653
                          from_label, from_text, output_to)
648
 
        except BinaryFile:
 
654
        except errors.BinaryFile:
649
655
            if reverse:
650
656
                label_pair = (to_label, from_label)
651
657
            else:
677
683
 
678
684
    def _put_on_disk(self, fullpath, tree):
679
685
        """See InventoryEntry._put_on_disk."""
680
 
        pumpfile(tree.get_file(self.file_id), file(fullpath, 'wb'))
 
686
        osutils.pumpfile(tree.get_file(self.file_id), file(fullpath, 'wb'))
681
687
        if tree.is_executable(self.file_id):
682
688
            os.chmod(fullpath, 0755)
683
689
 
849
855
    ['', u'hello.c']
850
856
    >>> inv = Inventory('TREE_ROOT-12345678-12345678')
851
857
    >>> inv.add(InventoryFile('123-123', 'hello.c', ROOT_ID))
 
858
    Traceback (most recent call last):
 
859
    BzrError: parent_id {TREE_ROOT} not in inventory
 
860
    >>> inv.add(InventoryFile('123-123', 'hello.c', 'TREE_ROOT-12345678-12345678'))
852
861
    InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT-12345678-12345678', sha1=None, len=None)
853
862
    """
854
863
    def __init__(self, root_id=ROOT_ID, revision_id=None):
861
870
        The inventory is created with a default root directory, with
862
871
        an id of None.
863
872
        """
864
 
        # We are letting Branch.create() create a unique inventory
865
 
        # root id. Rather than generating a random one here.
866
 
        #if root_id is None:
867
 
        #    root_id = bzrlib.branch.gen_file_id('TREE_ROOT')
868
 
        self.root = InventoryDirectory(root_id, '', None)
869
 
        # FIXME: this isn't ever used, changing it to self.revision may break
870
 
        # things. TODO make everything use self.revision_id
 
873
        if root_id is not None:
 
874
            self._set_root(InventoryDirectory(root_id, '', None))
 
875
        else:
 
876
            self.root = None
 
877
            self._byid = {}
871
878
        self.revision_id = revision_id
 
879
 
 
880
    def _set_root(self, ie):
 
881
        self.root = ie
872
882
        self._byid = {self.root.file_id: self.root}
873
883
 
874
884
    def copy(self):
891
901
    def iter_entries(self, from_dir=None):
892
902
        """Return (path, entry) pairs, in order by name."""
893
903
        if from_dir is None:
894
 
            assert self.root
 
904
            if self.root is None:
 
905
                return
895
906
            from_dir = self.root
896
907
            yield '', self.root
897
908
        elif isinstance(from_dir, basestring):
944
955
        # TODO? Perhaps this should return the from_dir so that the root is
945
956
        # yielded? or maybe an option?
946
957
        if from_dir is None:
947
 
            assert self.root
 
958
            if self.root is None:
 
959
                return
948
960
            from_dir = self.root
949
961
            yield '', self.root
950
962
        elif isinstance(from_dir, basestring):
975
987
            kids = dir_ie.children.items()
976
988
            kids.sort()
977
989
            for name, ie in kids:
978
 
                child_path = pathjoin(dir_path, name)
 
990
                child_path = osutils.pathjoin(dir_path, name)
979
991
                accum.append((child_path, ie))
980
992
                if ie.kind == 'directory':
981
993
                    descend(ie, child_path)
994
1006
            kids.sort()
995
1007
 
996
1008
            for name, child_ie in kids:
997
 
                child_path = pathjoin(parent_path, name)
 
1009
                child_path = osutils.pathjoin(parent_path, name)
998
1010
                descend(child_ie, child_path)
999
1011
        descend(self.root, u'')
1000
1012
        return accum
1010
1022
        >>> '456' in inv
1011
1023
        False
1012
1024
        """
1013
 
        return file_id in self._byid
 
1025
        return (file_id in self._byid)
1014
1026
 
1015
1027
    def __getitem__(self, file_id):
1016
1028
        """Return the entry for given file_id.
1046
1058
        if entry.file_id in self._byid:
1047
1059
            raise BzrError("inventory already contains entry with id {%s}" % entry.file_id)
1048
1060
 
1049
 
        if entry.parent_id == ROOT_ID or entry.parent_id is None:
1050
 
            entry.parent_id = self.root.file_id
1051
 
 
 
1061
        if entry.parent_id is None:
 
1062
            assert self.root is None and len(self._byid) == 0
 
1063
            self._set_root(entry)
 
1064
            return entry
1052
1065
        try:
1053
1066
            parent = self._byid[entry.parent_id]
1054
1067
        except KeyError:
1056
1069
 
1057
1070
        if entry.name in parent.children:
1058
1071
            raise BzrError("%s is already versioned" %
1059
 
                    pathjoin(self.id2path(parent.file_id), entry.name))
 
1072
                    osutils.pathjoin(self.id2path(parent.file_id), entry.name))
1060
1073
 
1061
1074
        self._byid[entry.file_id] = entry
1062
1075
        parent.children[entry.name] = entry
1081
1094
            parent_path = parts[:-1]
1082
1095
            parent_id = self.path2id(parent_path)
1083
1096
            if parent_id is None:
1084
 
                raise NotVersionedError(path=parent_path)
 
1097
                raise errors.NotVersionedError(path=parent_path)
1085
1098
        ie = make_entry(kind, parts[-1], parent_id, file_id)
1086
1099
        return self.add(ie)
1087
1100
 
1181
1194
 
1182
1195
        Returns None IFF the path is not found.
1183
1196
        """
1184
 
        if isinstance(name, types.StringTypes):
1185
 
            name = splitpath(name)
 
1197
        if isinstance(name, basestring):
 
1198
            name = osutils.splitpath(name)
1186
1199
 
1187
1200
        # mutter("lookup path %r" % name)
1188
1201
 
1189
1202
        parent = self.root
 
1203
        if parent is None:
 
1204
            return None
1190
1205
        for f in name:
1191
1206
            try:
1192
 
                cie = parent.children[f]
 
1207
                children = getattr(parent, 'children', None)
 
1208
                if children is None:
 
1209
                    return None
 
1210
                cie = children[f]
1193
1211
                assert cie.name == f
1194
1212
                assert cie.parent_id == parent.file_id
1195
1213
                parent = cie
1203
1221
        return bool(self.path2id(names))
1204
1222
 
1205
1223
    def has_id(self, file_id):
1206
 
        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]
1207
1243
 
1208
1244
    def rename(self, file_id, new_parent_id, new_name):
1209
1245
        """Move a file within the inventory.
1234
1270
        file_ie.name = new_name
1235
1271
        file_ie.parent_id = new_parent_id
1236
1272
 
 
1273
    def is_root(self, file_id):
 
1274
        return self.root is not None and file_id == self.root.file_id
 
1275
 
1237
1276
 
1238
1277
def make_entry(kind, name, parent_id, file_id=None):
1239
1278
    """Create an inventory entry.