/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: Richard Wilbur
  • Date: 2016-02-04 19:07:28 UTC
  • mto: This revision was merged to the branch mainline in revision 6618.
  • Revision ID: richard.wilbur@gmail.com-20160204190728-p0zvfii6zase0fw7
Update COPYING.txt from the original http://www.gnu.org/licenses/gpl-2.0.txt  (Only differences were in whitespace.)  Thanks to Petr Stodulka for pointing out the discrepancy.

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
 
28
28
# This should really be an id randomly assigned when the tree is
29
29
# created, but it's not for now.
30
 
ROOT_ID = b"TREE_ROOT"
 
30
ROOT_ID = "TREE_ROOT"
31
31
 
32
 
from ..lazy_import import lazy_import
 
32
from bzrlib.lazy_import import lazy_import
33
33
lazy_import(globals(), """
34
34
import collections
35
35
import copy
36
36
import re
37
37
import tarfile
38
38
 
39
 
from breezy import (
 
39
from bzrlib import (
 
40
    chk_map,
40
41
    errors,
41
42
    generate_ids,
42
43
    osutils,
43
44
    )
44
 
from breezy.bzr import (
45
 
    chk_map,
46
 
    )
47
45
""")
48
46
 
49
 
from .. import (
 
47
from bzrlib import (
50
48
    lazy_regex,
51
49
    trace,
52
50
    )
53
 
from ..sixish import (
54
 
    bytesintern,
55
 
    PY3,
56
 
    text_type,
57
 
    viewitems,
58
 
    viewvalues,
 
51
 
 
52
from bzrlib.static_tuple import StaticTuple
 
53
from bzrlib.symbol_versioning import (
 
54
    deprecated_in,
 
55
    deprecated_method,
59
56
    )
60
 
from ..static_tuple import StaticTuple
61
57
 
62
58
 
63
59
class InventoryEntry(object):
112
108
    '2325'
113
109
    >>> i.add(InventoryFile('2326', 'wibble.c', '2325'))
114
110
    InventoryFile('2326', 'wibble.c', parent_id='2325', sha1=None, len=None, revision=None)
115
 
    >>> i.get_entry('2326')
 
111
    >>> i['2326']
116
112
    InventoryFile('2326', 'wibble.c', parent_id='2325', sha1=None, len=None, revision=None)
117
113
    >>> for path, entry in i.iter_entries():
118
114
    ...     print path
123
119
    src/hello.c
124
120
    src/wibble
125
121
    src/wibble/wibble.c
126
 
    >>> i.id2path(b'2326')
 
122
    >>> i.id2path('2326')
127
123
    'src/wibble/wibble.c'
128
124
    """
129
125
 
178
174
        candidates = {}
179
175
        # identify candidate head revision ids.
180
176
        for inv in previous_inventories:
181
 
            try:
182
 
                ie = inv.get_entry(self.file_id)
183
 
            except errors.NoSuchId:
184
 
                pass
185
 
            else:
 
177
            if inv.has_id(self.file_id):
 
178
                ie = inv[self.file_id]
186
179
                if ie.revision in candidates:
187
180
                    # same revision value in two different inventories:
188
181
                    # correct possible inconsistencies:
225
218
        Traceback (most recent call last):
226
219
        InvalidEntryName: Invalid entry name: src/hello.c
227
220
        """
228
 
        if u'/' in name:
 
221
        if '/' in name or '\\' in name:
229
222
            raise errors.InvalidEntryName(name=name)
230
223
        self.file_id = file_id
231
224
        self.revision = None
238
231
 
239
232
    known_kinds = ('file', 'directory', 'symlink')
240
233
 
 
234
    def sorted_children(self):
 
235
        return sorted(self.children.items())
 
236
 
241
237
    @staticmethod
242
238
    def versionable_kind(kind):
243
239
        return (kind in ('file', 'directory', 'symlink', 'tree-reference'))
396
392
        # to provide a per-fileid log. The hash of every directory content is
397
393
        # "da..." below (the sha1sum of '').
398
394
        checker.add_pending_item(rev_id,
399
 
            (b'texts', self.file_id, self.revision), b'text',
400
 
             b'da39a3ee5e6b4b0d3255bfef95601890afd80709')
 
395
            ('texts', self.file_id, self.revision), 'text',
 
396
             'da39a3ee5e6b4b0d3255bfef95601890afd80709')
401
397
 
402
398
    def copy(self):
403
399
        other = InventoryDirectory(self.file_id, self.name, self.parent_id)
410
406
        super(InventoryDirectory, self).__init__(file_id, name, parent_id)
411
407
        self.children = {}
412
408
 
413
 
    def sorted_children(self):
414
 
        return sorted(viewitems(self.children))
415
 
 
416
409
    def kind_character(self):
417
410
        """See InventoryEntry.kind_character."""
418
411
        return '/'
436
429
        """See InventoryEntry._check"""
437
430
        # TODO: check size too.
438
431
        checker.add_pending_item(tree_revision_id,
439
 
            (b'texts', self.file_id, self.revision), b'text',
 
432
            ('texts', self.file_id, self.revision), 'text',
440
433
             self.text_sha1)
441
434
        if self.text_size is None:
442
435
            checker._report_items.append(
461
454
    def _diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
462
455
             output_to, reverse=False):
463
456
        """See InventoryEntry._diff."""
464
 
        from breezy.diff import DiffText
 
457
        from bzrlib.diff import DiffText
465
458
        from_file_id = self.file_id
466
459
        if to_entry:
467
460
            to_file_id = to_entry.file_id
468
 
            to_path = to_tree.id2path(to_file_id)
469
461
        else:
470
462
            to_file_id = None
471
 
            to_path = None
472
 
        if from_file_id is not None:
473
 
            from_path = tree.id2path(from_file_id)
474
 
        else:
475
 
            from_path = None
476
463
        if reverse:
477
464
            to_file_id, from_file_id = from_file_id, to_file_id
478
465
            tree, to_tree = to_tree, tree
479
466
            from_label, to_label = to_label, from_label
480
467
        differ = DiffText(tree, to_tree, output_to, 'utf-8', '', '',
481
468
                          text_diff)
482
 
        return differ.diff_text(from_path, to_path, from_label, to_label,
483
 
                                from_file_id, to_file_id)
 
469
        return differ.diff_text(from_file_id, to_file_id, from_label, to_label)
484
470
 
485
471
    def has_text(self):
486
472
        """See InventoryEntry.has_text."""
492
478
 
493
479
    def _read_tree_state(self, path, work_tree):
494
480
        """See InventoryEntry._read_tree_state."""
495
 
        self.text_sha1 = work_tree.get_file_sha1(path, self.file_id)
 
481
        self.text_sha1 = work_tree.get_file_sha1(self.file_id, path=path)
496
482
        # FIXME: 20050930 probe for the text size when getting sha1
497
483
        # in _read_tree_state
498
 
        self.executable = work_tree.is_executable(path, self.file_id)
 
484
        self.executable = work_tree.is_executable(self.file_id, path=path)
499
485
 
500
486
    def __repr__(self):
501
487
        return ("%s(%r, %r, parent_id=%r, sha1=%r, len=%s, revision=%s)"
543
529
                    % (self.file_id, tree_revision_id))
544
530
        # Symlinks are stored as ''
545
531
        checker.add_pending_item(tree_revision_id,
546
 
            (b'texts', self.file_id, self.revision), b'text',
547
 
             b'da39a3ee5e6b4b0d3255bfef95601890afd80709')
 
532
            ('texts', self.file_id, self.revision), 'text',
 
533
             'da39a3ee5e6b4b0d3255bfef95601890afd80709')
548
534
 
549
535
    def copy(self):
550
536
        other = InventoryLink(self.file_id, self.name, self.parent_id)
564
550
    def _diff(self, text_diff, from_label, tree, to_label, to_entry, to_tree,
565
551
             output_to, reverse=False):
566
552
        """See InventoryEntry._diff."""
567
 
        from breezy.diff import DiffSymlink
 
553
        from bzrlib.diff import DiffSymlink
568
554
        old_target = self.symlink_target
569
555
        if to_entry is not None:
570
556
            new_target = to_entry.symlink_target
586
572
 
587
573
    def _read_tree_state(self, path, work_tree):
588
574
        """See InventoryEntry._read_tree_state."""
589
 
        self.symlink_target = work_tree.get_symlink_target(
590
 
                work_tree.id2path(self.file_id), self.file_id)
 
575
        self.symlink_target = work_tree.get_symlink_target(self.file_id)
591
576
 
592
577
    def _forget_tree_state(self):
593
578
        self.symlink_target = None
620
605
        """Populate fields in the inventory entry from the given tree.
621
606
        """
622
607
        self.reference_revision = work_tree.get_reference_revision(
623
 
            path, self.file_id)
 
608
            self.file_id, path)
624
609
 
625
610
    def _forget_tree_state(self):
626
611
        self.reference_revision = None
655
640
        """Return as a string the path to file_id.
656
641
 
657
642
        >>> i = Inventory()
658
 
        >>> e = i.add(InventoryDirectory(b'src-id', 'src', ROOT_ID))
659
 
        >>> e = i.add(InventoryFile(b'foo-id', 'foo.c', parent_id='src-id'))
660
 
        >>> print i.id2path(b'foo-id')
 
643
        >>> e = i.add(InventoryDirectory('src-id', 'src', ROOT_ID))
 
644
        >>> e = i.add(InventoryFile('foo-id', 'foo.c', parent_id='src-id'))
 
645
        >>> print i.id2path('foo-id')
661
646
        src/foo.c
662
647
 
663
648
        :raises NoSuchId: If file_id is not present in the inventory.
679
664
                return
680
665
            from_dir = self.root
681
666
            yield '', self.root
682
 
        elif isinstance(from_dir, bytes):
683
 
            from_dir = self.get_entry(from_dir)
 
667
        elif isinstance(from_dir, basestring):
 
668
            from_dir = self[from_dir]
684
669
 
685
670
        # unrolling the recursive called changed the time from
686
671
        # 440ms/663ms (inline/total) to 116ms/116ms
687
 
        children = sorted(viewitems(from_dir.children))
 
672
        children = from_dir.children.items()
 
673
        children.sort()
688
674
        if not recursive:
689
675
            for name, ie in children:
690
676
                yield name, ie
709
695
                    continue
710
696
 
711
697
                # But do this child first
712
 
                new_children = sorted(viewitems(ie.children))
 
698
                new_children = ie.children.items()
 
699
                new_children.sort()
713
700
                new_children = collections.deque(new_children)
714
701
                stack.append((path, new_children))
715
702
                # Break out of inner loop, so that we start outer loop with child
720
707
 
721
708
    def _preload_cache(self):
722
709
        """Populate any caches, we are about to access all items.
723
 
 
 
710
        
724
711
        The default implementation does nothing, because CommonInventory doesn't
725
712
        have a cache.
726
713
        """
727
714
        pass
728
 
 
729
 
    def iter_entries_by_dir(self, from_dir=None, specific_file_ids=None):
 
715
    
 
716
    def iter_entries_by_dir(self, from_dir=None, specific_file_ids=None,
 
717
        yield_parents=False):
730
718
        """Iterate over the entries in a directory first order.
731
719
 
732
720
        This returns all entries for a directory before returning
734
722
        lexicographically sorted order, and is a hybrid between
735
723
        depth-first and breadth-first.
736
724
 
 
725
        :param yield_parents: If True, yield the parents from the root leading
 
726
            down to specific_file_ids that have been requested. This has no
 
727
            impact if specific_file_ids is None.
737
728
        :return: This yields (path, entry) pairs
738
729
        """
739
730
        if specific_file_ids and not isinstance(specific_file_ids, set):
749
740
            if self.root is None:
750
741
                return
751
742
            # Optimize a common case
752
 
            if (specific_file_ids is not None and
 
743
            if (not yield_parents and specific_file_ids is not None and
753
744
                len(specific_file_ids) == 1):
754
745
                file_id = list(specific_file_ids)[0]
755
 
                if file_id is not None:
756
 
                    try:
757
 
                        path = self.id2path(file_id)
758
 
                    except errors.NoSuchId:
759
 
                        pass
760
 
                    else:
761
 
                        yield path, self.get_entry(file_id)
 
746
                if self.has_id(file_id):
 
747
                    yield self.id2path(file_id), self[file_id]
762
748
                return
763
749
            from_dir = self.root
764
 
            if (specific_file_ids is None or
 
750
            if (specific_file_ids is None or yield_parents or
765
751
                self.root.file_id in specific_file_ids):
766
752
                yield u'', self.root
767
 
        elif isinstance(from_dir, (str, text_type)):
768
 
            from_dir = self.get_entry(from_dir)
 
753
        elif isinstance(from_dir, basestring):
 
754
            from_dir = self[from_dir]
769
755
 
770
756
        if specific_file_ids is not None:
771
757
            # TODO: jam 20070302 This could really be done as a loop rather
775
761
            def add_ancestors(file_id):
776
762
                if not byid.has_id(file_id):
777
763
                    return
778
 
                parent_id = byid.get_entry(file_id).parent_id
 
764
                parent_id = byid[file_id].parent_id
779
765
                if parent_id is None:
780
766
                    return
781
767
                if parent_id not in parents:
791
777
            cur_relpath, cur_dir = stack.pop()
792
778
 
793
779
            child_dirs = []
794
 
            for child_name, child_ie in sorted(viewitems(cur_dir.children)):
 
780
            for child_name, child_ie in sorted(cur_dir.children.iteritems()):
795
781
 
796
782
                child_relpath = cur_relpath + child_name
797
783
 
798
784
                if (specific_file_ids is None or
799
 
                    child_ie.file_id in specific_file_ids):
 
785
                    child_ie.file_id in specific_file_ids or
 
786
                    (yield_parents and child_ie.file_id in parents)):
800
787
                    yield child_relpath, child_ie
801
788
 
802
789
                if child_ie.kind == 'directory':
806
793
 
807
794
    def _make_delta(self, old):
808
795
        """Make an inventory delta from two inventories."""
809
 
        old_ids = set(old.iter_all_ids())
810
 
        new_ids = set(self.iter_all_ids())
 
796
        old_ids = set(old)
 
797
        new_ids = set(self)
811
798
        adds = new_ids - old_ids
812
799
        deletes = old_ids - new_ids
813
800
        common = old_ids.intersection(new_ids)
815
802
        for file_id in deletes:
816
803
            delta.append((old.id2path(file_id), None, file_id, None))
817
804
        for file_id in adds:
818
 
            delta.append((None, self.id2path(file_id), file_id, self.get_entry(file_id)))
 
805
            delta.append((None, self.id2path(file_id), file_id, self[file_id]))
819
806
        for file_id in common:
820
 
            if old.get_entry(file_id) != self.get_entry(file_id):
 
807
            if old[file_id] != self[file_id]:
821
808
                delta.append((old.id2path(file_id), self.id2path(file_id),
822
 
                    file_id, self.get_entry(file_id)))
 
809
                    file_id, self[file_id]))
823
810
        return delta
824
811
 
825
812
    def make_entry(self, kind, name, parent_id, file_id=None):
826
 
        """Simple thunk to breezy.bzr.inventory.make_entry."""
 
813
        """Simple thunk to bzrlib.inventory.make_entry."""
827
814
        return make_entry(kind, name, parent_id, file_id)
828
815
 
829
816
    def entries(self):
833
820
        """
834
821
        accum = []
835
822
        def descend(dir_ie, dir_path):
836
 
            kids = sorted(viewitems(dir_ie.children))
 
823
            kids = dir_ie.children.items()
 
824
            kids.sort()
837
825
            for name, ie in kids:
838
826
                child_path = osutils.pathjoin(dir_path, name)
839
827
                accum.append((child_path, ie))
844
832
            descend(self.root, u'')
845
833
        return accum
846
834
 
847
 
    def get_entry_by_path(self, relpath):
848
 
        """Return an inventory entry by path.
 
835
    def path2id(self, relpath):
 
836
        """Walk down through directories to return entry of last component.
849
837
 
850
838
        :param relpath: may be either a list of path components, or a single
851
839
            string, in which case it is automatically split.
855
843
 
856
844
        Returns None IFF the path is not found.
857
845
        """
858
 
        if isinstance(relpath, (str, text_type)):
 
846
        if isinstance(relpath, basestring):
859
847
            names = osutils.splitpath(relpath)
860
848
        else:
861
849
            names = relpath
877
865
            except KeyError:
878
866
                # or raise an error?
879
867
                return None
880
 
        return parent
881
 
 
882
 
    def path2id(self, relpath):
883
 
        """Walk down through directories to return entry of last component.
884
 
 
885
 
        :param relpath: may be either a list of path components, or a single
886
 
            string, in which case it is automatically split.
887
 
 
888
 
        This returns the entry of the last component in the path,
889
 
        which may be either a file or a directory.
890
 
 
891
 
        Returns None IFF the path is not found.
892
 
        """
893
 
        ie = self.get_entry_by_path(relpath)
894
 
        if ie is None:
895
 
            return None
896
 
        return ie.file_id
 
868
 
 
869
        return parent.file_id
897
870
 
898
871
    def filter(self, specific_fileids):
899
872
        """Get an inventory view filtered against a set of file-ids.
913
886
        entries = self.iter_entries()
914
887
        if self.root is None:
915
888
            return Inventory(root_id=None)
916
 
        other = Inventory(next(entries)[1].file_id)
 
889
        other = Inventory(entries.next()[1].file_id)
917
890
        other.root.revision = self.root.revision
918
891
        other.revision_id = self.revision_id
919
892
        directories_to_expand = set()
953
926
    >>> inv = Inventory()
954
927
    >>> inv.add(InventoryFile('123-123', 'hello.c', ROOT_ID))
955
928
    InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT', sha1=None, len=None, revision=None)
956
 
    >>> inv.get_entry('123-123').name
 
929
    >>> inv['123-123'].name
957
930
    'hello.c'
958
931
 
959
932
    Id's may be looked up from paths:
1056
1029
        for old_path, file_id in sorted(((op, f) for op, np, f, e in delta
1057
1030
                                        if op is not None), reverse=True):
1058
1031
            # Preserve unaltered children of file_id for later reinsertion.
1059
 
            file_id_children = getattr(self.get_entry(file_id), 'children', {})
 
1032
            file_id_children = getattr(self[file_id], 'children', {})
1060
1033
            if len(file_id_children):
1061
1034
                children[file_id] = file_id_children
1062
1035
            if self.id2path(file_id) != old_path:
1115
1088
        entries = self.iter_entries()
1116
1089
        if self.root is None:
1117
1090
            return Inventory(root_id=None)
1118
 
        other = Inventory(next(entries)[1].file_id)
 
1091
        other = Inventory(entries.next()[1].file_id)
1119
1092
        other.root.revision = self.root.revision
1120
1093
        # copy recursively so we know directories will be added before
1121
1094
        # their children.  There are more efficient ways than this...
1123
1096
            other.add(entry.copy())
1124
1097
        return other
1125
1098
 
1126
 
    def iter_all_ids(self):
 
1099
    def __iter__(self):
1127
1100
        """Iterate over all file-ids."""
1128
1101
        return iter(self._byid)
1129
1102
 
1136
1109
        XXX: We may not want to merge this into bzr.dev.
1137
1110
        """
1138
1111
        if self.root is None:
1139
 
            return ()
1140
 
        return iter(viewvalues(self._byid))
 
1112
            return
 
1113
        for _, ie in self._byid.iteritems():
 
1114
            yield ie
1141
1115
 
1142
1116
    def __len__(self):
1143
1117
        """Returns number of entries."""
1144
1118
        return len(self._byid)
1145
1119
 
1146
 
    def get_entry(self, file_id):
 
1120
    def __getitem__(self, file_id):
1147
1121
        """Return the entry for given file_id.
1148
1122
 
1149
1123
        >>> inv = Inventory()
1150
1124
        >>> inv.add(InventoryFile('123123', 'hello.c', ROOT_ID))
1151
1125
        InventoryFile('123123', 'hello.c', parent_id='TREE_ROOT', sha1=None, len=None, revision=None)
1152
 
        >>> inv.get_entry('123123').name
 
1126
        >>> inv['123123'].name
1153
1127
        'hello.c'
1154
1128
        """
1155
1129
        try:
1162
1136
        return self._byid[file_id].kind
1163
1137
 
1164
1138
    def get_child(self, parent_id, filename):
1165
 
        return self.get_entry(parent_id).children.get(filename)
 
1139
        return self[parent_id].children.get(filename)
1166
1140
 
1167
1141
    def _add_child(self, entry):
1168
1142
        """Add an entry to the inventory, without adding it to its parent"""
1171
1145
                "inventory already contains entry with id {%s}" %
1172
1146
                entry.file_id)
1173
1147
        self._byid[entry.file_id] = entry
1174
 
        children = getattr(entry, 'children', {})
1175
 
        if children is not None:
1176
 
            for child in viewvalues(children):
1177
 
                self._add_child(child)
 
1148
        for child in getattr(entry, 'children', {}).itervalues():
 
1149
            self._add_child(child)
1178
1150
        return entry
1179
1151
 
1180
1152
    def add(self, entry):
1224
1196
        ie = make_entry(kind, parts[-1], parent_id, file_id)
1225
1197
        return self.add(ie)
1226
1198
 
1227
 
    def delete(self, file_id):
 
1199
    def __delitem__(self, file_id):
1228
1200
        """Remove entry by id.
1229
1201
 
1230
1202
        >>> inv = Inventory()
1232
1204
        InventoryFile('123', 'foo.c', parent_id='TREE_ROOT', sha1=None, len=None, revision=None)
1233
1205
        >>> inv.has_id('123')
1234
1206
        True
1235
 
        >>> inv.delete('123')
 
1207
        >>> del inv['123']
1236
1208
        >>> inv.has_id('123')
1237
1209
        False
1238
1210
        """
1239
 
        ie = self.get_entry(file_id)
 
1211
        ie = self[file_id]
1240
1212
        del self._byid[file_id]
1241
1213
        if ie.parent_id is not None:
1242
 
            del self.get_entry(ie.parent_id).children[ie.name]
 
1214
            del self[ie.parent_id].children[ie.name]
1243
1215
 
1244
1216
    def __eq__(self, other):
1245
1217
        """Compare two sets by comparing their contents.
1283
1255
 
1284
1256
    def _make_delta(self, old):
1285
1257
        """Make an inventory delta from two inventories."""
1286
 
        old_getter = old.get_entry
1287
 
        new_getter = self.get_entry
1288
 
        old_ids = set(old.iter_all_ids())
1289
 
        new_ids = set(self.iter_all_ids())
 
1258
        old_getter = getattr(old, '_byid', old)
 
1259
        new_getter = self._byid
 
1260
        old_ids = set(old_getter)
 
1261
        new_ids = set(new_getter)
1290
1262
        adds = new_ids - old_ids
1291
1263
        deletes = old_ids - new_ids
1292
1264
        if not adds and not deletes:
1297
1269
        for file_id in deletes:
1298
1270
            delta.append((old.id2path(file_id), None, file_id, None))
1299
1271
        for file_id in adds:
1300
 
            delta.append((None, self.id2path(file_id), file_id, self.get_entry(file_id)))
 
1272
            delta.append((None, self.id2path(file_id), file_id, self[file_id]))
1301
1273
        for file_id in common:
1302
 
            new_ie = new_getter(file_id)
1303
 
            old_ie = old_getter(file_id)
 
1274
            new_ie = new_getter[file_id]
 
1275
            old_ie = old_getter[file_id]
1304
1276
            # If xml_serializer returns the cached InventoryEntries (rather
1305
1277
            # than always doing .copy()), inlining the 'is' check saves 2.7M
1306
1278
            # calls to __eq__.  Under lsprof this saves 20s => 6s.
1323
1295
            ie = to_find_delete.pop()
1324
1296
            to_delete.append(ie.file_id)
1325
1297
            if ie.kind == 'directory':
1326
 
                to_find_delete.extend(viewvalues(ie.children))
 
1298
                to_find_delete.extend(ie.children.values())
1327
1299
        for file_id in reversed(to_delete):
1328
 
            ie = self.get_entry(file_id)
 
1300
            ie = self[file_id]
1329
1301
            del self._byid[file_id]
1330
1302
        if ie.parent_id is not None:
1331
 
            del self.get_entry(ie.parent_id).children[ie.name]
 
1303
            del self[ie.parent_id].children[ie.name]
1332
1304
        else:
1333
1305
            self.root = None
1334
1306
 
1429
1401
        if entry.parent_id is not None:
1430
1402
            parent_str = entry.parent_id
1431
1403
        else:
1432
 
            parent_str = b''
 
1404
            parent_str = ''
1433
1405
        name_str = entry.name.encode("utf8")
1434
1406
        if entry.kind == 'file':
1435
1407
            if entry.executable:
1436
 
                exec_str = b"Y"
 
1408
                exec_str = "Y"
1437
1409
            else:
1438
 
                exec_str = b"N"
1439
 
            return b"file: %s\n%s\n%s\n%s\n%s\n%d\n%s" % (
 
1410
                exec_str = "N"
 
1411
            return "file: %s\n%s\n%s\n%s\n%s\n%d\n%s" % (
1440
1412
                entry.file_id, parent_str, name_str, entry.revision,
1441
1413
                entry.text_sha1, entry.text_size, exec_str)
1442
1414
        elif entry.kind == 'directory':
1443
 
            return b"dir: %s\n%s\n%s\n%s" % (
 
1415
            return "dir: %s\n%s\n%s\n%s" % (
1444
1416
                entry.file_id, parent_str, name_str, entry.revision)
1445
1417
        elif entry.kind == 'symlink':
1446
 
            return b"symlink: %s\n%s\n%s\n%s\n%s" % (
 
1418
            return "symlink: %s\n%s\n%s\n%s\n%s" % (
1447
1419
                entry.file_id, parent_str, name_str, entry.revision,
1448
1420
                entry.symlink_target.encode("utf8"))
1449
1421
        elif entry.kind == 'tree-reference':
1450
 
            return b"tree: %s\n%s\n%s\n%s\n%s" % (
 
1422
            return "tree: %s\n%s\n%s\n%s\n%s" % (
1451
1423
                entry.file_id, parent_str, name_str, entry.revision,
1452
1424
                entry.reference_revision)
1453
1425
        else:
1515
1487
            keys = [StaticTuple(f,).intern() for f in directories_to_expand]
1516
1488
            directories_to_expand = set()
1517
1489
            items = self.parent_id_basename_to_file_id.iteritems(keys)
1518
 
            next_file_ids = {item[1] for item in items}
 
1490
            next_file_ids = set([item[1] for item in items])
1519
1491
            next_file_ids = next_file_ids.difference(interesting)
1520
1492
            interesting.update(next_file_ids)
1521
1493
            for entry in self._getitems(next_file_ids):
1565
1537
        return other
1566
1538
 
1567
1539
    @staticmethod
1568
 
    def _bytes_to_utf8name_key(data):
1569
 
        """Get the file_id, revision_id key out of data."""
 
1540
    def _bytes_to_utf8name_key(bytes):
 
1541
        """Get the file_id, revision_id key out of bytes."""
1570
1542
        # We don't normally care about name, except for times when we want
1571
1543
        # to filter out empty names because of non rich-root...
1572
 
        sections = data.split(b'\n')
1573
 
        kind, file_id = sections[0].split(b': ')
1574
 
        return (sections[2], bytesintern(file_id), bytesintern(sections[3]))
 
1544
        sections = bytes.split('\n')
 
1545
        kind, file_id = sections[0].split(': ')
 
1546
        return (sections[2], intern(file_id), intern(sections[3]))
1575
1547
 
1576
1548
    def _bytes_to_entry(self, bytes):
1577
1549
        """Deserialise a serialised entry."""
1578
 
        sections = bytes.split(b'\n')
1579
 
        if sections[0].startswith(b"file: "):
 
1550
        sections = bytes.split('\n')
 
1551
        if sections[0].startswith("file: "):
1580
1552
            result = InventoryFile(sections[0][6:],
1581
1553
                sections[2].decode('utf8'),
1582
1554
                sections[1])
1583
1555
            result.text_sha1 = sections[4]
1584
1556
            result.text_size = int(sections[5])
1585
 
            result.executable = sections[6] == b"Y"
1586
 
        elif sections[0].startswith(b"dir: "):
 
1557
            result.executable = sections[6] == "Y"
 
1558
        elif sections[0].startswith("dir: "):
1587
1559
            result = CHKInventoryDirectory(sections[0][5:],
1588
1560
                sections[2].decode('utf8'),
1589
1561
                sections[1], self)
1590
 
        elif sections[0].startswith(b"symlink: "):
 
1562
        elif sections[0].startswith("symlink: "):
1591
1563
            result = InventoryLink(sections[0][9:],
1592
1564
                sections[2].decode('utf8'),
1593
1565
                sections[1])
1594
1566
            result.symlink_target = sections[4].decode('utf8')
1595
 
        elif sections[0].startswith(b"tree: "):
 
1567
        elif sections[0].startswith("tree: "):
1596
1568
            result = TreeReference(sections[0][6:],
1597
1569
                sections[2].decode('utf8'),
1598
1570
                sections[1])
1599
1571
            result.reference_revision = sections[4]
1600
1572
        else:
1601
1573
            raise ValueError("Not a serialised entry %r" % bytes)
1602
 
        result.file_id = bytesintern(result.file_id)
1603
 
        result.revision = bytesintern(sections[3])
1604
 
        if result.parent_id == b'':
 
1574
        result.file_id = intern(result.file_id)
 
1575
        result.revision = intern(sections[3])
 
1576
        if result.parent_id == '':
1605
1577
            result.parent_id = None
1606
1578
        self._fileid_to_entry_cache[result.file_id] = result
1607
1579
        return result
1624
1596
        result = CHKInventory(self._search_key_name)
1625
1597
        if propagate_caches:
1626
1598
            # Just propagate the path-to-fileid cache for now
1627
 
            result._path_to_fileid_cache = self._path_to_fileid_cache.copy()
 
1599
            result._path_to_fileid_cache = dict(self._path_to_fileid_cache.iteritems())
1628
1600
        search_key_func = chk_map.search_key_registry.get(self._search_key_name)
1629
1601
        self.id_to_entry._ensure_root()
1630
1602
        maximum_size = self.id_to_entry._root_node.maximum_size
1717
1689
                if old_path is None:
1718
1690
                    old_key = None
1719
1691
                else:
1720
 
                    old_entry = self.get_entry(file_id)
 
1692
                    old_entry = self[file_id]
1721
1693
                    old_key = self._parent_id_basename_key(old_entry)
1722
1694
                if new_path is None:
1723
1695
                    new_key = None
1738
1710
                            new_key, [None, None])[1] = new_value
1739
1711
        # validate that deletes are complete.
1740
1712
        for file_id in deletes:
1741
 
            entry = self.get_entry(file_id)
 
1713
            entry = self[file_id]
1742
1714
            if entry.kind != 'directory':
1743
1715
                continue
1744
1716
            # This loop could potentially be better by using the id_basename
1745
1717
            # map to just get the child file ids.
1746
 
            for child in viewvalues(entry.children):
 
1718
            for child in entry.children.values():
1747
1719
                if child.file_id not in altered:
1748
1720
                    raise errors.InconsistentDelta(self.id2path(child.file_id),
1749
1721
                        child.file_id, "Child not deleted or reparented when "
1755
1727
            # re-keying, but its simpler to just output that as a delete+add
1756
1728
            # to spend less time calculating the delta.
1757
1729
            delta_list = []
1758
 
            for key, (old_key, value) in viewitems(parent_id_basename_delta):
 
1730
            for key, (old_key, value) in parent_id_basename_delta.iteritems():
1759
1731
                if value is not None:
1760
1732
                    delta_list.append((old_key, key, value))
1761
1733
                else:
1764
1736
        parents.discard(('', None))
1765
1737
        for parent_path, parent in parents:
1766
1738
            try:
1767
 
                if result.get_entry(parent).kind != 'directory':
 
1739
                if result[parent].kind != 'directory':
1768
1740
                    raise errors.InconsistentDelta(result.id2path(parent), parent,
1769
1741
                        'Not a directory, but given children')
1770
1742
            except errors.NoSuchId:
1785
1757
            for.
1786
1758
        :return: A CHKInventory
1787
1759
        """
1788
 
        lines = bytes.split(b'\n')
1789
 
        if lines[-1] != b'':
 
1760
        lines = bytes.split('\n')
 
1761
        if lines[-1] != '':
1790
1762
            raise AssertionError('bytes to deserialize must end with an eol')
1791
1763
        lines.pop()
1792
 
        if lines[0] != b'chkinventory:':
 
1764
        if lines[0] != 'chkinventory:':
1793
1765
            raise ValueError("not a serialised CHKInventory: %r" % bytes)
1794
1766
        info = {}
1795
 
        allowed_keys = frozenset((b'root_id', b'revision_id',
1796
 
                                  b'parent_id_basename_to_file_id',
1797
 
                                  b'search_key_name', b'id_to_entry'))
 
1767
        allowed_keys = frozenset(['root_id', 'revision_id', 'search_key_name',
 
1768
                                  'parent_id_basename_to_file_id',
 
1769
                                  'id_to_entry'])
1798
1770
        for line in lines[1:]:
1799
 
            key, value = line.split(b': ', 1)
 
1771
            key, value = line.split(': ', 1)
1800
1772
            if key not in allowed_keys:
1801
1773
                raise errors.BzrError('Unknown key in inventory: %r\n%r'
1802
1774
                                      % (key, bytes))
1804
1776
                raise errors.BzrError('Duplicate key in inventory: %r\n%r'
1805
1777
                                      % (key, bytes))
1806
1778
            info[key] = value
1807
 
        revision_id = bytesintern(info[b'revision_id'])
1808
 
        root_id = bytesintern(info[b'root_id'])
1809
 
        search_key_name = bytesintern(info.get(b'search_key_name', b'plain'))
1810
 
        parent_id_basename_to_file_id = bytesintern(info.get(
1811
 
            b'parent_id_basename_to_file_id', None))
1812
 
        if not parent_id_basename_to_file_id.startswith(b'sha1:'):
 
1779
        revision_id = intern(info['revision_id'])
 
1780
        root_id = intern(info['root_id'])
 
1781
        search_key_name = intern(info.get('search_key_name', 'plain'))
 
1782
        parent_id_basename_to_file_id = intern(info.get(
 
1783
            'parent_id_basename_to_file_id', None))
 
1784
        if not parent_id_basename_to_file_id.startswith('sha1:'):
1813
1785
            raise ValueError('parent_id_basename_to_file_id should be a sha1'
1814
1786
                             ' key not %r' % (parent_id_basename_to_file_id,))
1815
 
        id_to_entry = info[b'id_to_entry']
1816
 
        if not id_to_entry.startswith(b'sha1:'):
 
1787
        id_to_entry = info['id_to_entry']
 
1788
        if not id_to_entry.startswith('sha1:'):
1817
1789
            raise ValueError('id_to_entry should be a sha1'
1818
1790
                             ' key not %r' % (id_to_entry,))
1819
1791
 
1821
1793
        result.revision_id = revision_id
1822
1794
        result.root_id = root_id
1823
1795
        search_key_func = chk_map.search_key_registry.get(
1824
 
            result._search_key_name.decode("ascii"))
 
1796
                            result._search_key_name)
1825
1797
        if parent_id_basename_to_file_id is not None:
1826
1798
            result.parent_id_basename_to_file_id = chk_map.CHKMap(
1827
1799
                chk_store, StaticTuple(parent_id_basename_to_file_id,),
1887
1859
        if entry.parent_id is not None:
1888
1860
            parent_id = entry.parent_id
1889
1861
        else:
1890
 
            parent_id = b''
 
1862
            parent_id = ''
1891
1863
        return StaticTuple(parent_id, entry.name.encode('utf8')).intern()
1892
1864
 
1893
 
    def get_entry(self, file_id):
 
1865
    def __getitem__(self, file_id):
1894
1866
        """map a single file_id -> InventoryEntry."""
1895
1867
        if file_id is None:
1896
1868
            raise errors.NoSuchId(self, file_id)
1899
1871
            return result
1900
1872
        try:
1901
1873
            return self._bytes_to_entry(
1902
 
                next(self.id_to_entry.iteritems([StaticTuple(file_id,)]))[1])
 
1874
                self.id_to_entry.iteritems([StaticTuple(file_id,)]).next()[1])
1903
1875
        except StopIteration:
1904
1876
            # really we're passing an inventory, not a tree...
1905
1877
            raise errors.NoSuchId(self, file_id)
1906
1878
 
1907
1879
    def _getitems(self, file_ids):
1908
 
        """Similar to get_entry, but lets you query for multiple.
 
1880
        """Similar to __getitem__, but lets you query for multiple.
1909
1881
        
1910
1882
        The returned order is undefined. And currently if an item doesn't
1911
1883
        exist, it isn't included in the output.
1939
1911
        """Yield the parents of file_id up to the root."""
1940
1912
        while file_id is not None:
1941
1913
            try:
1942
 
                ie = self.get_entry(file_id)
 
1914
                ie = self[file_id]
1943
1915
            except KeyError:
1944
1916
                raise errors.NoSuchId(tree=self, file_id=file_id)
1945
1917
            yield ie
1946
1918
            file_id = ie.parent_id
1947
1919
 
1948
 
    def iter_all_ids(self):
 
1920
    def __iter__(self):
1949
1921
        """Iterate over all file-ids."""
1950
1922
        for key, _ in self.id_to_entry.iteritems():
1951
1923
            yield key[-1]
1982
1954
        last_parent_id = last_parent_ie = None
1983
1955
        pid_items = self.parent_id_basename_to_file_id.iteritems()
1984
1956
        for key, child_file_id in pid_items:
1985
 
            if key == (b'', b''): # This is the root
 
1957
            if key == ('', ''): # This is the root
1986
1958
                if child_file_id != self.root_id:
1987
1959
                    raise ValueError('Data inconsistency detected.'
1988
1960
                        ' We expected data with key ("","") to match'
2098
2070
 
2099
2071
    def _make_delta(self, old):
2100
2072
        """Make an inventory delta from two inventories."""
2101
 
        if not isinstance(old, CHKInventory):
 
2073
        if type(old) != CHKInventory:
2102
2074
            return CommonInventory._make_delta(self, old)
2103
2075
        delta = []
2104
2076
        for key, old_value, self_value in \
2121
2093
    def path2id(self, relpath):
2122
2094
        """See CommonInventory.path2id()."""
2123
2095
        # TODO: perhaps support negative hits?
2124
 
        if isinstance(relpath, (str, text_type)):
 
2096
        if isinstance(relpath, basestring):
2125
2097
            names = osutils.splitpath(relpath)
2126
2098
        else:
2127
2099
            names = relpath
2160
2132
 
2161
2133
    def to_lines(self):
2162
2134
        """Serialise the inventory to lines."""
2163
 
        lines = [b"chkinventory:\n"]
 
2135
        lines = ["chkinventory:\n"]
2164
2136
        if self._search_key_name != 'plain':
2165
2137
            # custom ordering grouping things that don't change together
2166
 
            lines.append(b'search_key_name: %s\n' % (
2167
 
                self._search_key_name.encode('ascii')))
2168
 
            lines.append(b"root_id: %s\n" % self.root_id)
2169
 
            lines.append(b'parent_id_basename_to_file_id: %s\n' %
 
2138
            lines.append('search_key_name: %s\n' % (self._search_key_name,))
 
2139
            lines.append("root_id: %s\n" % self.root_id)
 
2140
            lines.append('parent_id_basename_to_file_id: %s\n' %
2170
2141
                (self.parent_id_basename_to_file_id.key()[0],))
2171
 
            lines.append(b"revision_id: %s\n" % self.revision_id)
2172
 
            lines.append(b"id_to_entry: %s\n" % (self.id_to_entry.key()[0],))
 
2142
            lines.append("revision_id: %s\n" % self.revision_id)
 
2143
            lines.append("id_to_entry: %s\n" % (self.id_to_entry.key()[0],))
2173
2144
        else:
2174
 
            lines.append(b"revision_id: %s\n" % self.revision_id)
2175
 
            lines.append(b"root_id: %s\n" % self.root_id)
 
2145
            lines.append("revision_id: %s\n" % self.revision_id)
 
2146
            lines.append("root_id: %s\n" % self.root_id)
2176
2147
            if self.parent_id_basename_to_file_id is not None:
2177
 
                lines.append(b'parent_id_basename_to_file_id: %s\n' %
 
2148
                lines.append('parent_id_basename_to_file_id: %s\n' %
2178
2149
                    (self.parent_id_basename_to_file_id.key()[0],))
2179
 
            lines.append(b"id_to_entry: %s\n" % (self.id_to_entry.key()[0],))
 
2150
            lines.append("id_to_entry: %s\n" % (self.id_to_entry.key()[0],))
2180
2151
        return lines
2181
2152
 
2182
2153
    @property
2183
2154
    def root(self):
2184
2155
        """Get the root entry."""
2185
 
        return self.get_entry(self.root_id)
 
2156
        return self[self.root_id]
2186
2157
 
2187
2158
 
2188
2159
class CHKInventoryDirectory(InventoryDirectory):
2270
2241
        accessed on this platform by the normalized path.
2271
2242
    :return: The NFC normalised version of name.
2272
2243
    """
2273
 
    #------- This has been copied to breezy.dirstate.DirState.add, please
 
2244
    #------- This has been copied to bzrlib.dirstate.DirState.add, please
2274
2245
    # keep them synchronised.
2275
2246
    # we dont import normalized_filename directly because we want to be
2276
2247
    # able to change the implementation at runtime for tests.
2348
2319
        if item[2] is None:
2349
2320
            raise errors.InconsistentDelta(item[0] or item[1], item[2],
2350
2321
                "entry with file_id None %r" % entry)
2351
 
        if not isinstance(item[2], bytes):
 
2322
        if type(item[2]) != str:
2352
2323
            raise errors.InconsistentDelta(item[0] or item[1], item[2],
2353
2324
                "entry with non bytes file_id %r" % entry)
2354
2325
        yield item