~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: Alexander Belchenko
  • Date: 2006-07-30 16:43:12 UTC
  • mto: (1711.2.111 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1906.
  • Revision ID: bialix@ukr.net-20060730164312-b025fd3ff0cee59e
rename  gpl.txt => COPYING.txt

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
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', u'', 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))
 
91
    >>> i.add(InventoryFile('2323', 'bye.c', '123'))
 
92
    Traceback (most recent call last):
 
93
    ...
 
94
    BzrError: inventory already contains entry with id {2323}
100
95
    >>> i.add(InventoryFile('2324', 'bye.c', '123'))
101
96
    InventoryFile('2324', 'bye.c', parent_id='123', sha1=None, len=None)
102
97
    >>> i.add(InventoryDirectory('2325', 'wibble', '123'))
181
176
        :param entry_vf: The entry versioned file, if its already available.
182
177
        """
183
178
        def get_ancestors(weave, entry):
184
 
            return set(weave.get_ancestry(entry.revision, topo_sorted=False))
 
179
            return set(weave.get_ancestry(entry.revision))
185
180
        # revision:ie mapping for each ie found in previous_inventories.
186
181
        candidates = {}
187
182
        # revision:ie mapping with one revision for each head.
193
188
            if self.file_id in inv:
194
189
                ie = inv[self.file_id]
195
190
                assert ie.file_id == self.file_id
196
 
                if ie.kind != self.kind:
197
 
                    # Can't be a candidate if the kind has changed.
198
 
                    continue
199
191
                if ie.revision in candidates:
200
192
                    # same revision value in two different inventories:
201
193
                    # correct possible inconsistencies:
253
245
 
254
246
    def get_tar_item(self, root, dp, now, tree):
255
247
        """Get a tarfile item and a file stream for its content."""
256
 
        item = tarfile.TarInfo(osutils.pathjoin(root, dp).encode('utf8'))
 
248
        item = tarfile.TarInfo(pathjoin(root, dp))
257
249
        # TODO: would be cool to actually set it to the timestamp of the
258
250
        # revision it was last changed
259
251
        item.mtime = now
288
280
        """
289
281
        assert isinstance(name, basestring), name
290
282
        if '/' in name or '\\' in name:
291
 
            raise errors.InvalidEntryName(name=name)
 
283
            raise InvalidEntryName(name=name)
292
284
        self.executable = False
293
285
        self.revision = None
294
286
        self.text_sha1 = None
295
287
        self.text_size = None
296
288
        self.file_id = file_id
297
 
        assert isinstance(file_id, (str, None.__class__)), \
298
 
            'bad type %r for %r' % (type(file_id), file_id)
299
289
        self.name = name
300
290
        self.text_id = text_id
301
291
        self.parent_id = parent_id
302
292
        self.symlink_target = None
303
 
        self.reference_revision = None
304
293
 
305
294
    def kind_character(self):
306
295
        """Return a short kind indicator useful for appending to names."""
307
296
        raise BzrError('unknown kind %r' % self.kind)
308
297
 
309
 
    known_kinds = ('file', 'directory', 'symlink')
 
298
    known_kinds = ('file', 'directory', 'symlink', 'root_directory')
310
299
 
311
300
    def _put_in_tar(self, item, tree):
312
301
        """populate item for stashing in a tar, and return the content stream.
321
310
        
322
311
        This is a template method - implement _put_on_disk in subclasses.
323
312
        """
324
 
        fullpath = osutils.pathjoin(dest, dp)
 
313
        fullpath = pathjoin(dest, dp)
325
314
        self._put_on_disk(fullpath, tree)
326
315
        # mutter("  export {%s} kind %s to %s", self.file_id,
327
316
        #         self.kind, fullpath)
335
324
 
336
325
    @staticmethod
337
326
    def versionable_kind(kind):
338
 
        return (kind in ('file', 'directory', 'symlink', 'tree-reference'))
 
327
        return kind in ('file', 'directory', 'symlink')
339
328
 
340
329
    def check(self, checker, rev_id, inv, tree):
341
330
        """Check this inventory entry is intact.
386
375
            return 'added'
387
376
        elif new_entry is None:
388
377
            return 'removed'
389
 
        if old_entry.kind != new_entry.kind:
390
 
            return 'modified'
391
378
        text_modified, meta_modified = new_entry.detect_changes(old_entry)
392
379
        if text_modified or meta_modified:
393
380
            modified = True
473
460
                and (self.kind == other.kind)
474
461
                and (self.revision == other.revision)
475
462
                and (self.executable == other.executable)
476
 
                and (self.reference_revision == other.reference_revision)
477
463
                )
478
464
 
479
465
    def __ne__(self, other):
494
480
        # renamed
495
481
        elif previous_ie.name != self.name:
496
482
            compatible = False
497
 
        elif previous_ie.kind != self.kind:
498
 
            compatible = False
499
483
        return compatible
500
484
 
501
485
    def _read_tree_state(self, path, work_tree):
516
500
class RootEntry(InventoryEntry):
517
501
 
518
502
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
519
 
                 'text_id', 'parent_id', 'children', 'executable',
520
 
                 'revision', 'symlink_target', 'reference_revision']
 
503
                 'text_id', 'parent_id', 'children', 'executable', 
 
504
                 'revision', 'symlink_target']
521
505
 
522
506
    def _check(self, checker, rev_id, tree):
523
507
        """See InventoryEntry._check"""
525
509
    def __init__(self, file_id):
526
510
        self.file_id = file_id
527
511
        self.children = {}
528
 
        self.kind = 'directory'
 
512
        self.kind = 'root_directory'
529
513
        self.parent_id = None
530
514
        self.name = u''
531
515
        self.revision = None
532
 
        symbol_versioning.warn('RootEntry is deprecated as of bzr 0.10.'
533
 
                               '  Please use InventoryDirectory instead.',
534
 
                               DeprecationWarning, stacklevel=2)
535
516
 
536
517
    def __eq__(self, other):
537
518
        if not isinstance(other, RootEntry):
545
526
    """A directory in an inventory."""
546
527
 
547
528
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
548
 
                 'text_id', 'parent_id', 'children', 'executable',
549
 
                 'revision', 'symlink_target', 'reference_revision']
 
529
                 'text_id', 'parent_id', 'children', 'executable', 
 
530
                 'revision', 'symlink_target']
550
531
 
551
532
    def _check(self, checker, rev_id, tree):
552
533
        """See InventoryEntry._check"""
592
573
    """A file in an inventory."""
593
574
 
594
575
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
595
 
                 'text_id', 'parent_id', 'children', 'executable',
596
 
                 'revision', 'symlink_target', 'reference_revision']
 
576
                 'text_id', 'parent_id', 'children', 'executable', 
 
577
                 'revision', 'symlink_target']
597
578
 
598
579
    def _check(self, checker, tree_revision_id, tree):
599
580
        """See InventoryEntry._check"""
609
590
 
610
591
        if self.file_id not in checker.checked_weaves:
611
592
            mutter('check weave {%s}', self.file_id)
612
 
            w = tree._get_weave(self.file_id)
 
593
            w = tree.get_weave(self.file_id)
613
594
            # Not passing a progress bar, because it creates a new
614
595
            # progress, which overwrites the current progress,
615
596
            # and doesn't look nice
616
597
            w.check()
617
598
            checker.checked_weaves[self.file_id] = True
618
599
        else:
619
 
            w = tree._get_weave(self.file_id)
 
600
            w = tree.get_weave(self.file_id)
620
601
 
621
602
        mutter('check version {%s} of {%s}', tree_revision_id, self.file_id)
622
603
        checker.checked_text_cnt += 1
660
641
            else:
661
642
                text_diff(to_label, to_text,
662
643
                          from_label, from_text, output_to)
663
 
        except errors.BinaryFile:
 
644
        except BinaryFile:
664
645
            if reverse:
665
646
                label_pair = (to_label, from_label)
666
647
            else:
692
673
 
693
674
    def _put_on_disk(self, fullpath, tree):
694
675
        """See InventoryEntry._put_on_disk."""
695
 
        osutils.pumpfile(tree.get_file(self.file_id), file(fullpath, 'wb'))
 
676
        pumpfile(tree.get_file(self.file_id), file(fullpath, 'wb'))
696
677
        if tree.is_executable(self.file_id):
697
678
            os.chmod(fullpath, 0755)
698
679
 
740
721
    """A file in an inventory."""
741
722
 
742
723
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
743
 
                 'text_id', 'parent_id', 'children', 'executable',
744
 
                 'revision', 'symlink_target', 'reference_revision']
 
724
                 'text_id', 'parent_id', 'children', 'executable', 
 
725
                 'revision', 'symlink_target']
745
726
 
746
727
    def _check(self, checker, rev_id, tree):
747
728
        """See InventoryEntry._check"""
828
809
            self.file_id, file_parents, self.symlink_target)
829
810
 
830
811
 
831
 
class TreeReference(InventoryEntry):
832
 
    
833
 
    kind = 'tree-reference'
834
 
    
835
 
    def __init__(self, file_id, name, parent_id, revision=None,
836
 
                 reference_revision=None):
837
 
        InventoryEntry.__init__(self, file_id, name, parent_id)
838
 
        self.revision = revision
839
 
        self.reference_revision = reference_revision
840
 
 
841
 
    def copy(self):
842
 
        return TreeReference(self.file_id, self.name, self.parent_id,
843
 
                             self.revision, self.reference_revision)
844
 
 
845
 
    def _snapshot_text(self, file_parents, work_tree, commit_builder):
846
 
        commit_builder.modified_reference(self.file_id, file_parents)
847
 
 
848
 
    def _read_tree_state(self, path, work_tree):
849
 
        """Populate fields in the inventory entry from the given tree.
850
 
        """
851
 
        self.reference_revision = work_tree.get_reference_revision(
852
 
            self.file_id, path)
853
 
 
854
 
    def _forget_tree_state(self):
855
 
        self.reference_revision = None 
856
 
 
857
 
 
858
812
class Inventory(object):
859
813
    """Inventory of versioned files in a tree.
860
814
 
891
845
    ['', u'hello.c']
892
846
    >>> inv = Inventory('TREE_ROOT-12345678-12345678')
893
847
    >>> inv.add(InventoryFile('123-123', 'hello.c', ROOT_ID))
894
 
    Traceback (most recent call last):
895
 
    BzrError: parent_id {TREE_ROOT} not in inventory
896
 
    >>> inv.add(InventoryFile('123-123', 'hello.c', 'TREE_ROOT-12345678-12345678'))
897
848
    InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT-12345678-12345678', sha1=None, len=None)
898
849
    """
899
850
    def __init__(self, root_id=ROOT_ID, revision_id=None):
906
857
        The inventory is created with a default root directory, with
907
858
        an id of None.
908
859
        """
909
 
        if root_id is not None:
910
 
            assert root_id.__class__ == str
911
 
            self._set_root(InventoryDirectory(root_id, u'', None))
912
 
        else:
913
 
            self.root = None
914
 
            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
915
867
        self.revision_id = revision_id
916
 
 
917
 
    def _set_root(self, ie):
918
 
        self.root = ie
919
868
        self._byid = {self.root.file_id: self.root}
920
869
 
921
870
    def copy(self):
938
887
    def iter_entries(self, from_dir=None):
939
888
        """Return (path, entry) pairs, in order by name."""
940
889
        if from_dir is None:
941
 
            if self.root is None:
942
 
                return
 
890
            assert self.root
943
891
            from_dir = self.root
944
892
            yield '', self.root
945
893
        elif isinstance(from_dir, basestring):
979
927
                # if we finished all children, pop it off the stack
980
928
                stack.pop()
981
929
 
982
 
    def iter_entries_by_dir(self, from_dir=None, specific_file_ids=None):
 
930
    def iter_entries_by_dir(self, from_dir=None):
983
931
        """Iterate over the entries in a directory first order.
984
932
 
985
933
        This returns all entries for a directory before returning
989
937
 
990
938
        :return: This yields (path, entry) pairs
991
939
        """
992
 
        if specific_file_ids:
993
 
            safe = osutils.safe_file_id
994
 
            specific_file_ids = set(safe(fid) for fid in specific_file_ids)
995
940
        # TODO? Perhaps this should return the from_dir so that the root is
996
941
        # yielded? or maybe an option?
997
942
        if from_dir is None:
998
 
            if self.root is None:
999
 
                return
1000
 
            # Optimize a common case
1001
 
            if specific_file_ids is not None and len(specific_file_ids) == 1:
1002
 
                file_id = list(specific_file_ids)[0]
1003
 
                if file_id in self:
1004
 
                    yield self.id2path(file_id), self[file_id]
1005
 
                return 
 
943
            assert self.root
1006
944
            from_dir = self.root
1007
 
            if (specific_file_ids is None or 
1008
 
                self.root.file_id in specific_file_ids):
1009
 
                yield u'', self.root
 
945
            yield '', self.root
1010
946
        elif isinstance(from_dir, basestring):
1011
947
            from_dir = self._byid[from_dir]
1012
 
 
1013
 
        if specific_file_ids is not None:
1014
 
            # TODO: jam 20070302 This could really be done as a loop rather
1015
 
            #       than a bunch of recursive calls.
1016
 
            parents = set()
1017
 
            byid = self._byid
1018
 
            def add_ancestors(file_id):
1019
 
                if file_id not in byid:
1020
 
                    return
1021
 
                parent_id = byid[file_id].parent_id
1022
 
                if parent_id is None:
1023
 
                    return
1024
 
                if parent_id not in parents:
1025
 
                    parents.add(parent_id)
1026
 
                    add_ancestors(parent_id)
1027
 
            for file_id in specific_file_ids:
1028
 
                add_ancestors(file_id)
1029
 
        else:
1030
 
            parents = None
1031
948
            
1032
949
        stack = [(u'', from_dir)]
1033
950
        while stack:
1038
955
 
1039
956
                child_relpath = cur_relpath + child_name
1040
957
 
1041
 
                if (specific_file_ids is None or 
1042
 
                    child_ie.file_id in specific_file_ids):
1043
 
                    yield child_relpath, child_ie
 
958
                yield child_relpath, child_ie
1044
959
 
1045
960
                if child_ie.kind == 'directory':
1046
 
                    if parents is None or child_ie.file_id in parents:
1047
 
                        child_dirs.append((child_relpath+'/', child_ie))
 
961
                    child_dirs.append((child_relpath+'/', child_ie))
1048
962
            stack.extend(reversed(child_dirs))
1049
963
 
1050
 
    def make_entry(self, kind, name, parent_id, file_id=None):
1051
 
        """Simple thunk to bzrlib.inventory.make_entry."""
1052
 
        return make_entry(kind, name, parent_id, file_id)
1053
 
 
1054
964
    def entries(self):
1055
965
        """Return list of (path, ie) for all entries except the root.
1056
966
 
1061
971
            kids = dir_ie.children.items()
1062
972
            kids.sort()
1063
973
            for name, ie in kids:
1064
 
                child_path = osutils.pathjoin(dir_path, name)
 
974
                child_path = pathjoin(dir_path, name)
1065
975
                accum.append((child_path, ie))
1066
976
                if ie.kind == 'directory':
1067
977
                    descend(ie, child_path)
1080
990
            kids.sort()
1081
991
 
1082
992
            for name, child_ie in kids:
1083
 
                child_path = osutils.pathjoin(parent_path, name)
 
993
                child_path = pathjoin(parent_path, name)
1084
994
                descend(child_ie, child_path)
1085
995
        descend(self.root, u'')
1086
996
        return accum
1096
1006
        >>> '456' in inv
1097
1007
        False
1098
1008
        """
1099
 
        file_id = osutils.safe_file_id(file_id)
1100
 
        return (file_id in self._byid)
 
1009
        return file_id in self._byid
1101
1010
 
1102
1011
    def __getitem__(self, file_id):
1103
1012
        """Return the entry for given file_id.
1108
1017
        >>> inv['123123'].name
1109
1018
        'hello.c'
1110
1019
        """
1111
 
        file_id = osutils.safe_file_id(file_id)
1112
1020
        try:
1113
1021
            return self._byid[file_id]
1114
1022
        except KeyError:
1115
 
            # really we're passing an inventory, not a tree...
1116
 
            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)
1117
1027
 
1118
1028
    def get_file_kind(self, file_id):
1119
 
        file_id = osutils.safe_file_id(file_id)
1120
1029
        return self._byid[file_id].kind
1121
1030
 
1122
1031
    def get_child(self, parent_id, filename):
1123
 
        parent_id = osutils.safe_file_id(parent_id)
1124
1032
        return self[parent_id].children.get(filename)
1125
1033
 
1126
 
    def _add_child(self, entry):
1127
 
        """Add an entry to the inventory, without adding it to its parent"""
1128
 
        if entry.file_id in self._byid:
1129
 
            raise BzrError("inventory already contains entry with id {%s}" %
1130
 
                           entry.file_id)
1131
 
        self._byid[entry.file_id] = entry
1132
 
        for child in getattr(entry, 'children', {}).itervalues():
1133
 
            self._add_child(child)
1134
 
        return entry
1135
 
 
1136
1034
    def add(self, entry):
1137
1035
        """Add entry to inventory.
1138
1036
 
1142
1040
        Returns the new entry object.
1143
1041
        """
1144
1042
        if entry.file_id in self._byid:
1145
 
            raise errors.DuplicateFileId(entry.file_id,
1146
 
                                         self._byid[entry.file_id])
1147
 
 
1148
 
        if entry.parent_id is None:
1149
 
            assert self.root is None and len(self._byid) == 0
1150
 
            self.root = entry
1151
 
        else:
1152
 
            try:
1153
 
                parent = self._byid[entry.parent_id]
1154
 
            except KeyError:
1155
 
                raise BzrError("parent_id {%s} not in inventory" %
1156
 
                               entry.parent_id)
1157
 
 
1158
 
            if entry.name in parent.children:
1159
 
                raise BzrError("%s is already versioned" %
1160
 
                        osutils.pathjoin(self.id2path(parent.file_id),
1161
 
                        entry.name).encode('utf-8'))
1162
 
            parent.children[entry.name] = entry
1163
 
        return self._add_child(entry)
 
1043
            raise BzrError("inventory already contains entry with id {%s}" % entry.file_id)
 
1044
 
 
1045
        if entry.parent_id == ROOT_ID or entry.parent_id is None:
 
1046
            entry.parent_id = self.root.file_id
 
1047
 
 
1048
        try:
 
1049
            parent = self._byid[entry.parent_id]
 
1050
        except KeyError:
 
1051
            raise BzrError("parent_id {%s} not in inventory" % entry.parent_id)
 
1052
 
 
1053
        if entry.name in parent.children:
 
1054
            raise BzrError("%s is already versioned" %
 
1055
                    pathjoin(self.id2path(parent.file_id), entry.name))
 
1056
 
 
1057
        self._byid[entry.file_id] = entry
 
1058
        parent.children[entry.name] = entry
 
1059
        return entry
1164
1060
 
1165
1061
    def add_path(self, relpath, kind, file_id=None, parent_id=None):
1166
1062
        """Add entry from a path.
1173
1069
 
1174
1070
        if len(parts) == 0:
1175
1071
            if file_id is None:
1176
 
                file_id = generate_ids.gen_root_id()
1177
 
            else:
1178
 
                file_id = osutils.safe_file_id(file_id)
1179
 
            self.root = InventoryDirectory(file_id, '', None)
 
1072
                file_id = bzrlib.workingtree.gen_root_id()
 
1073
            self.root = RootEntry(file_id)
1180
1074
            self._byid = {self.root.file_id: self.root}
1181
 
            return self.root
 
1075
            return
1182
1076
        else:
1183
1077
            parent_path = parts[:-1]
1184
1078
            parent_id = self.path2id(parent_path)
1185
1079
            if parent_id is None:
1186
 
                raise errors.NotVersionedError(path=parent_path)
 
1080
                raise NotVersionedError(path=parent_path)
1187
1081
        ie = make_entry(kind, parts[-1], parent_id, file_id)
1188
1082
        return self.add(ie)
1189
1083
 
1199
1093
        >>> '123' in inv
1200
1094
        False
1201
1095
        """
1202
 
        file_id = osutils.safe_file_id(file_id)
1203
1096
        ie = self[file_id]
1204
1097
 
1205
1098
        assert ie.parent_id is None or \
1238
1131
 
1239
1132
    def _iter_file_id_parents(self, file_id):
1240
1133
        """Yield the parents of file_id up to the root."""
1241
 
        file_id = osutils.safe_file_id(file_id)
1242
1134
        while file_id is not None:
1243
1135
            try:
1244
1136
                ie = self._byid[file_id]
1245
1137
            except KeyError:
1246
 
                raise errors.NoSuchId(tree=None, file_id=file_id)
 
1138
                raise BzrError("file_id {%s} not found in inventory" % file_id)
1247
1139
            yield ie
1248
1140
            file_id = ie.parent_id
1249
1141
 
1255
1147
        is equal to the depth of the file in the tree, counting the
1256
1148
        root directory as depth 1.
1257
1149
        """
1258
 
        file_id = osutils.safe_file_id(file_id)
1259
1150
        p = []
1260
1151
        for parent in self._iter_file_id_parents(file_id):
1261
1152
            p.insert(0, parent.file_id)
1270
1161
        >>> print i.id2path('foo-id')
1271
1162
        src/foo.c
1272
1163
        """
1273
 
        file_id = osutils.safe_file_id(file_id)
1274
1164
        # get all names, skipping root
1275
1165
        return '/'.join(reversed(
1276
1166
            [parent.name for parent in 
1287
1177
 
1288
1178
        Returns None IFF the path is not found.
1289
1179
        """
1290
 
        if isinstance(name, basestring):
1291
 
            name = osutils.splitpath(name)
 
1180
        if isinstance(name, types.StringTypes):
 
1181
            name = splitpath(name)
1292
1182
 
1293
1183
        # mutter("lookup path %r" % name)
1294
1184
 
1295
1185
        parent = self.root
1296
 
        if parent is None:
1297
 
            return None
1298
1186
        for f in name:
1299
1187
            try:
1300
 
                children = getattr(parent, 'children', None)
1301
 
                if children is None:
1302
 
                    return None
1303
 
                cie = children[f]
 
1188
                cie = parent.children[f]
1304
1189
                assert cie.name == f
1305
1190
                assert cie.parent_id == parent.file_id
1306
1191
                parent = cie
1314
1199
        return bool(self.path2id(names))
1315
1200
 
1316
1201
    def has_id(self, file_id):
1317
 
        file_id = osutils.safe_file_id(file_id)
1318
 
        return (file_id in self._byid)
1319
 
 
1320
 
    def remove_recursive_id(self, file_id):
1321
 
        """Remove file_id, and children, from the inventory.
1322
 
        
1323
 
        :param file_id: A file_id to remove.
1324
 
        """
1325
 
        file_id = osutils.safe_file_id(file_id)
1326
 
        to_find_delete = [self._byid[file_id]]
1327
 
        to_delete = []
1328
 
        while to_find_delete:
1329
 
            ie = to_find_delete.pop()
1330
 
            to_delete.append(ie.file_id)
1331
 
            if ie.kind == 'directory':
1332
 
                to_find_delete.extend(ie.children.values())
1333
 
        for file_id in reversed(to_delete):
1334
 
            ie = self[file_id]
1335
 
            del self._byid[file_id]
1336
 
        if ie.parent_id is not None:
1337
 
            del self[ie.parent_id].children[ie.name]
1338
 
        else:
1339
 
            self.root = None
 
1202
        return self._byid.has_key(file_id)
1340
1203
 
1341
1204
    def rename(self, file_id, new_parent_id, new_name):
1342
1205
        """Move a file within the inventory.
1343
1206
 
1344
1207
        This can change either the name, or the parent, or both.
1345
1208
 
1346
 
        This does not move the working file.
1347
 
        """
1348
 
        file_id = osutils.safe_file_id(file_id)
 
1209
        This does not move the working file."""
1349
1210
        if not is_valid_name(new_name):
1350
1211
            raise BzrError("not an acceptable filename: %r" % new_name)
1351
1212
 
1369
1230
        file_ie.name = new_name
1370
1231
        file_ie.parent_id = new_parent_id
1371
1232
 
1372
 
    def is_root(self, file_id):
1373
 
        file_id = osutils.safe_file_id(file_id)
1374
 
        return self.root is not None and file_id == self.root.file_id
1375
 
 
1376
 
 
1377
 
entry_factory = {
1378
 
    'directory': InventoryDirectory,
1379
 
    'file': InventoryFile,
1380
 
    'symlink': InventoryLink,
1381
 
    'tree-reference': TreeReference
1382
 
}
1383
1233
 
1384
1234
def make_entry(kind, name, parent_id, file_id=None):
1385
1235
    """Create an inventory entry.
1390
1240
    :param file_id: the file_id to use. if None, one will be created.
1391
1241
    """
1392
1242
    if file_id is None:
1393
 
        file_id = generate_ids.gen_file_id(name)
1394
 
    else:
1395
 
        file_id = osutils.safe_file_id(file_id)
 
1243
        file_id = bzrlib.workingtree.gen_file_id(name)
1396
1244
 
1397
 
    #------- This has been copied to bzrlib.dirstate.DirState.add, please
1398
 
    # keep them synchronised.
1399
 
    # we dont import normalized_filename directly because we want to be
1400
 
    # able to change the implementation at runtime for tests.
1401
1245
    norm_name, can_access = osutils.normalized_filename(name)
1402
1246
    if norm_name != name:
1403
1247
        if can_access:
1407
1251
            #       if the error was raised with the full path
1408
1252
            raise errors.InvalidNormalization(name)
1409
1253
 
1410
 
    try:
1411
 
        factory = entry_factory[kind]
1412
 
    except KeyError:
 
1254
    if kind == 'directory':
 
1255
        return InventoryDirectory(file_id, name, parent_id)
 
1256
    elif kind == 'file':
 
1257
        return InventoryFile(file_id, name, parent_id)
 
1258
    elif kind == 'symlink':
 
1259
        return InventoryLink(file_id, name, parent_id)
 
1260
    else:
1413
1261
        raise BzrError("unknown kind %r" % kind)
1414
 
    return factory(file_id, name, parent_id)
1415
1262
 
1416
1263
 
1417
1264
_NAME_RE = None