~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: Danny van Heumen
  • Date: 2010-03-09 21:42:11 UTC
  • mto: (4634.139.5 2.0)
  • mto: This revision was merged to the branch mainline in revision 5160.
  • Revision ID: danny@dannyvanheumen.nl-20100309214211-iqh42x6qcikgd9p3
Reverted now-useless TODO list.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 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
35
35
import re
36
36
import tarfile
37
37
 
 
38
import bzrlib
38
39
from bzrlib import (
39
40
    chk_map,
40
41
    errors,
41
42
    generate_ids,
42
43
    osutils,
 
44
    symbol_versioning,
43
45
    )
44
46
""")
45
47
 
47
49
    BzrCheckError,
48
50
    BzrError,
49
51
    )
 
52
from bzrlib.symbol_versioning import deprecated_in, deprecated_method
50
53
from bzrlib.trace import mutter
51
 
from bzrlib.static_tuple import StaticTuple
52
54
 
53
55
 
54
56
class InventoryEntry(object):
128
130
    RENAMED = 'renamed'
129
131
    MODIFIED_AND_RENAMED = 'modified and renamed'
130
132
 
131
 
    __slots__ = ['file_id', 'revision', 'parent_id', 'name']
132
 
 
133
 
    # Attributes that all InventoryEntry instances are expected to have, but
134
 
    # that don't vary for all kinds of entry.  (e.g. symlink_target is only
135
 
    # relevant to InventoryLink, so there's no reason to make every
136
 
    # InventoryFile instance allocate space to hold a value for it.)
137
 
    # Attributes that only vary for files: executable, text_sha1, text_size,
138
 
    # text_id
139
 
    executable = False
140
 
    text_sha1 = None
141
 
    text_size = None
142
 
    text_id = None
143
 
    # Attributes that only vary for symlinks: symlink_target
144
 
    symlink_target = None
145
 
    # Attributes that only vary for tree-references: reference_revision
146
 
    reference_revision = None
147
 
 
 
133
    __slots__ = []
148
134
 
149
135
    def detect_changes(self, old_entry):
150
136
        """Return a (text_modified, meta_modified) from this to old_entry.
189
175
                    candidates[ie.revision] = ie
190
176
        return candidates
191
177
 
 
178
    @deprecated_method(deprecated_in((1, 6, 0)))
 
179
    def get_tar_item(self, root, dp, now, tree):
 
180
        """Get a tarfile item and a file stream for its content."""
 
181
        item = tarfile.TarInfo(osutils.pathjoin(root, dp).encode('utf8'))
 
182
        # TODO: would be cool to actually set it to the timestamp of the
 
183
        # revision it was last changed
 
184
        item.mtime = now
 
185
        fileobj = self._put_in_tar(item, tree)
 
186
        return item, fileobj
 
187
 
192
188
    def has_text(self):
193
189
        """Return true if the object this entry represents has textual data.
194
190
 
200
196
        """
201
197
        return False
202
198
 
203
 
    def __init__(self, file_id, name, parent_id):
 
199
    def __init__(self, file_id, name, parent_id, text_id=None):
204
200
        """Create an InventoryEntry
205
201
 
206
202
        The filename must be a single component, relative to the
217
213
        """
218
214
        if '/' in name or '\\' in name:
219
215
            raise errors.InvalidEntryName(name=name)
 
216
        self.executable = False
 
217
        self.revision = None
 
218
        self.text_sha1 = None
 
219
        self.text_size = None
220
220
        self.file_id = file_id
221
 
        self.revision = None
222
221
        self.name = name
 
222
        self.text_id = text_id
223
223
        self.parent_id = parent_id
 
224
        self.symlink_target = None
 
225
        self.reference_revision = None
224
226
 
225
227
    def kind_character(self):
226
228
        """Return a short kind indicator useful for appending to names."""
228
230
 
229
231
    known_kinds = ('file', 'directory', 'symlink')
230
232
 
 
233
    def _put_in_tar(self, item, tree):
 
234
        """populate item for stashing in a tar, and return the content stream.
 
235
 
 
236
        If no content is available, return None.
 
237
        """
 
238
        raise BzrError("don't know how to export {%s} of kind %r" %
 
239
                       (self.file_id, self.kind))
 
240
 
 
241
    @deprecated_method(deprecated_in((1, 6, 0)))
 
242
    def put_on_disk(self, dest, dp, tree):
 
243
        """Create a representation of self on disk in the prefix dest.
 
244
 
 
245
        This is a template method - implement _put_on_disk in subclasses.
 
246
        """
 
247
        fullpath = osutils.pathjoin(dest, dp)
 
248
        self._put_on_disk(fullpath, tree)
 
249
        # mutter("  export {%s} kind %s to %s", self.file_id,
 
250
        #         self.kind, fullpath)
 
251
 
 
252
    def _put_on_disk(self, fullpath, tree):
 
253
        """Put this entry onto disk at fullpath, from tree tree."""
 
254
        raise BzrError("don't know how to export {%s} of kind %r" % (self.file_id, self.kind))
 
255
 
231
256
    def sorted_children(self):
232
257
        return sorted(self.children.items())
233
258
 
371
396
        pass
372
397
 
373
398
 
 
399
class RootEntry(InventoryEntry):
 
400
 
 
401
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
 
402
                 'text_id', 'parent_id', 'children', 'executable',
 
403
                 'revision', 'symlink_target', 'reference_revision']
 
404
 
 
405
    def _check(self, checker, rev_id):
 
406
        """See InventoryEntry._check"""
 
407
 
 
408
    def __init__(self, file_id):
 
409
        self.file_id = file_id
 
410
        self.children = {}
 
411
        self.kind = 'directory'
 
412
        self.parent_id = None
 
413
        self.name = u''
 
414
        self.revision = None
 
415
        symbol_versioning.warn('RootEntry is deprecated as of bzr 0.10.'
 
416
                               '  Please use InventoryDirectory instead.',
 
417
                               DeprecationWarning, stacklevel=2)
 
418
 
 
419
    def __eq__(self, other):
 
420
        if not isinstance(other, RootEntry):
 
421
            return NotImplemented
 
422
 
 
423
        return (self.file_id == other.file_id) \
 
424
               and (self.children == other.children)
 
425
 
 
426
 
374
427
class InventoryDirectory(InventoryEntry):
375
428
    """A directory in an inventory."""
376
429
 
377
 
    __slots__ = ['children']
378
 
 
379
 
    kind = 'directory'
 
430
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
 
431
                 'text_id', 'parent_id', 'children', 'executable',
 
432
                 'revision', 'symlink_target', 'reference_revision']
380
433
 
381
434
    def _check(self, checker, rev_id):
382
435
        """See InventoryEntry._check"""
 
436
        if (self.text_sha1 is not None or self.text_size is not None or
 
437
            self.text_id is not None):
 
438
            checker._report_items.append('directory {%s} has text in revision {%s}'
 
439
                                % (self.file_id, rev_id))
383
440
        # In non rich root repositories we do not expect a file graph for the
384
441
        # root.
385
442
        if self.name == '' and not checker.rich_roots:
401
458
    def __init__(self, file_id, name, parent_id):
402
459
        super(InventoryDirectory, self).__init__(file_id, name, parent_id)
403
460
        self.children = {}
 
461
        self.kind = 'directory'
404
462
 
405
463
    def kind_character(self):
406
464
        """See InventoryEntry.kind_character."""
407
465
        return '/'
408
466
 
 
467
    def _put_in_tar(self, item, tree):
 
468
        """See InventoryEntry._put_in_tar."""
 
469
        item.type = tarfile.DIRTYPE
 
470
        fileobj = None
 
471
        item.name += '/'
 
472
        item.size = 0
 
473
        item.mode = 0755
 
474
        return fileobj
 
475
 
 
476
    def _put_on_disk(self, fullpath, tree):
 
477
        """See InventoryEntry._put_on_disk."""
 
478
        os.mkdir(fullpath)
 
479
 
409
480
 
410
481
class InventoryFile(InventoryEntry):
411
482
    """A file in an inventory."""
412
483
 
413
 
    __slots__ = ['text_sha1', 'text_size', 'text_id', 'executable']
414
 
 
415
 
    kind = 'file'
416
 
 
417
 
    def __init__(self, file_id, name, parent_id):
418
 
        super(InventoryFile, self).__init__(file_id, name, parent_id)
419
 
        self.text_sha1 = None
420
 
        self.text_size = None
421
 
        self.text_id = None
422
 
        self.executable = False
 
484
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
 
485
                 'text_id', 'parent_id', 'children', 'executable',
 
486
                 'revision', 'symlink_target', 'reference_revision']
423
487
 
424
488
    def _check(self, checker, tree_revision_id):
425
489
        """See InventoryEntry._check"""
468
532
        """See InventoryEntry.has_text."""
469
533
        return True
470
534
 
 
535
    def __init__(self, file_id, name, parent_id):
 
536
        super(InventoryFile, self).__init__(file_id, name, parent_id)
 
537
        self.kind = 'file'
 
538
 
471
539
    def kind_character(self):
472
540
        """See InventoryEntry.kind_character."""
473
541
        return ''
474
542
 
 
543
    def _put_in_tar(self, item, tree):
 
544
        """See InventoryEntry._put_in_tar."""
 
545
        item.type = tarfile.REGTYPE
 
546
        fileobj = tree.get_file(self.file_id)
 
547
        item.size = self.text_size
 
548
        if tree.is_executable(self.file_id):
 
549
            item.mode = 0755
 
550
        else:
 
551
            item.mode = 0644
 
552
        return fileobj
 
553
 
 
554
    def _put_on_disk(self, fullpath, tree):
 
555
        """See InventoryEntry._put_on_disk."""
 
556
        osutils.pumpfile(tree.get_file(self.file_id), file(fullpath, 'wb'))
 
557
        if tree.is_executable(self.file_id):
 
558
            os.chmod(fullpath, 0755)
 
559
 
475
560
    def _read_tree_state(self, path, work_tree):
476
561
        """See InventoryEntry._read_tree_state."""
477
562
        self.text_sha1 = work_tree.get_file_sha1(self.file_id, path=path)
509
594
class InventoryLink(InventoryEntry):
510
595
    """A file in an inventory."""
511
596
 
512
 
    __slots__ = ['symlink_target']
513
 
 
514
 
    kind = 'symlink'
515
 
 
516
 
    def __init__(self, file_id, name, parent_id):
517
 
        super(InventoryLink, self).__init__(file_id, name, parent_id)
518
 
        self.symlink_target = None
 
597
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
 
598
                 'text_id', 'parent_id', 'children', 'executable',
 
599
                 'revision', 'symlink_target', 'reference_revision']
519
600
 
520
601
    def _check(self, checker, tree_revision_id):
521
602
        """See InventoryEntry._check"""
 
603
        if self.text_sha1 is not None or self.text_size is not None or self.text_id is not None:
 
604
            checker._report_items.append(
 
605
               'symlink {%s} has text in revision {%s}'
 
606
                    % (self.file_id, tree_revision_id))
522
607
        if self.symlink_target is None:
523
608
            checker._report_items.append(
524
609
                'symlink {%s} has no target in revision {%s}'
562
647
        differ = DiffSymlink(old_tree, new_tree, output_to)
563
648
        return differ.diff_symlink(old_target, new_target)
564
649
 
 
650
    def __init__(self, file_id, name, parent_id):
 
651
        super(InventoryLink, self).__init__(file_id, name, parent_id)
 
652
        self.kind = 'symlink'
 
653
 
565
654
    def kind_character(self):
566
655
        """See InventoryEntry.kind_character."""
567
656
        return ''
568
657
 
 
658
    def _put_in_tar(self, item, tree):
 
659
        """See InventoryEntry._put_in_tar."""
 
660
        item.type = tarfile.SYMTYPE
 
661
        fileobj = None
 
662
        item.size = 0
 
663
        item.mode = 0755
 
664
        item.linkname = self.symlink_target
 
665
        return fileobj
 
666
 
 
667
    def _put_on_disk(self, fullpath, tree):
 
668
        """See InventoryEntry._put_on_disk."""
 
669
        try:
 
670
            os.symlink(self.symlink_target, fullpath)
 
671
        except OSError,e:
 
672
            raise BzrError("Failed to create symlink %r -> %r, error: %s" % (fullpath, self.symlink_target, e))
 
673
 
569
674
    def _read_tree_state(self, path, work_tree):
570
675
        """See InventoryEntry._read_tree_state."""
571
676
        self.symlink_target = work_tree.get_symlink_target(self.file_id)
583
688
 
584
689
class TreeReference(InventoryEntry):
585
690
 
586
 
    __slots__ = ['reference_revision']
587
 
 
588
691
    kind = 'tree-reference'
589
692
 
590
693
    def __init__(self, file_id, name, parent_id, revision=None,
855
958
        descend(self.root, u'')
856
959
        return accum
857
960
 
858
 
    def path2id(self, relpath):
 
961
    def path2id(self, name):
859
962
        """Walk down through directories to return entry of last component.
860
963
 
861
 
        :param relpath: may be either a list of path components, or a single
862
 
            string, in which case it is automatically split.
 
964
        names may be either a list of path components, or a single
 
965
        string, in which case it is automatically split.
863
966
 
864
967
        This returns the entry of the last component in the path,
865
968
        which may be either a file or a directory.
866
969
 
867
970
        Returns None IFF the path is not found.
868
971
        """
869
 
        if isinstance(relpath, basestring):
870
 
            names = osutils.splitpath(relpath)
871
 
        else:
872
 
            names = relpath
 
972
        if isinstance(name, basestring):
 
973
            name = osutils.splitpath(name)
 
974
 
 
975
        # mutter("lookup path %r" % name)
873
976
 
874
977
        try:
875
978
            parent = self.root
878
981
            return None
879
982
        if parent is None:
880
983
            return None
881
 
        for f in names:
 
984
        for f in name:
882
985
            try:
883
986
                children = getattr(parent, 'children', None)
884
987
                if children is None:
1178
1281
    def add(self, entry):
1179
1282
        """Add entry to inventory.
1180
1283
 
 
1284
        To add  a file to a branch ready to be committed, use Branch.add,
 
1285
        which calls this.
 
1286
 
1181
1287
        :return: entry
1182
1288
        """
1183
1289
        if entry.file_id in self._byid:
1493
1599
        interesting.add(None) # this will auto-filter it in the loop
1494
1600
        remaining_parents.discard(None) 
1495
1601
        while remaining_parents:
 
1602
            if None in remaining_parents:
 
1603
                import pdb; pdb.set_trace()
1496
1604
            next_parents = set()
1497
1605
            for entry in self._getitems(remaining_parents):
1498
1606
                next_parents.add(entry.parent_id)
1507
1615
        while directories_to_expand:
1508
1616
            # Expand directories by looking in the
1509
1617
            # parent_id_basename_to_file_id map
1510
 
            keys = [StaticTuple(f,).intern() for f in directories_to_expand]
 
1618
            keys = [(f,) for f in directories_to_expand]
1511
1619
            directories_to_expand = set()
1512
1620
            items = self.parent_id_basename_to_file_id.iteritems(keys)
1513
1621
            next_file_ids = set([item[1] for item in items])
1545
1653
            # parent_to_children with at least the tree root.)
1546
1654
            return other
1547
1655
        cache = self._fileid_to_entry_cache
1548
 
        remaining_children = collections.deque(parent_to_children[self.root_id])
 
1656
        try:
 
1657
            remaining_children = collections.deque(parent_to_children[self.root_id])
 
1658
        except:
 
1659
            import pdb; pdb.set_trace()
 
1660
            raise
1549
1661
        while remaining_children:
1550
1662
            file_id = remaining_children.popleft()
1551
1663
            ie = cache[file_id]
1566
1678
        # to filter out empty names because of non rich-root...
1567
1679
        sections = bytes.split('\n')
1568
1680
        kind, file_id = sections[0].split(': ')
1569
 
        return (sections[2], intern(file_id), intern(sections[3]))
 
1681
        return (sections[2], file_id, sections[3])
1570
1682
 
1571
1683
    def _bytes_to_entry(self, bytes):
1572
1684
        """Deserialise a serialised entry."""
1594
1706
            result.reference_revision = sections[4]
1595
1707
        else:
1596
1708
            raise ValueError("Not a serialised entry %r" % bytes)
1597
 
        result.file_id = intern(result.file_id)
1598
 
        result.revision = intern(sections[3])
 
1709
        result.revision = sections[3]
1599
1710
        if result.parent_id == '':
1600
1711
            result.parent_id = None
1601
1712
        self._fileid_to_entry_cache[result.file_id] = result
1699
1810
                        pass
1700
1811
                deletes.add(file_id)
1701
1812
            else:
1702
 
                new_key = StaticTuple(file_id,)
 
1813
                new_key = (file_id,)
1703
1814
                new_value = result._entry_to_bytes(entry)
1704
1815
                # Update caches. It's worth doing this whether
1705
1816
                # we're propagating the old caches or not.
1708
1819
            if old_path is None:
1709
1820
                old_key = None
1710
1821
            else:
1711
 
                old_key = StaticTuple(file_id,)
 
1822
                old_key = (file_id,)
1712
1823
                if self.id2path(file_id) != old_path:
1713
1824
                    raise errors.InconsistentDelta(old_path, file_id,
1714
1825
                        "Entry was at wrong other path %r." %
1715
1826
                        self.id2path(file_id))
1716
1827
                altered.add(file_id)
1717
 
            id_to_entry_delta.append(StaticTuple(old_key, new_key, new_value))
 
1828
            id_to_entry_delta.append((old_key, new_key, new_value))
1718
1829
            if result.parent_id_basename_to_file_id is not None:
1719
1830
                # parent_id, basename changes
1720
1831
                if old_path is None:
1807
1918
                raise errors.BzrError('Duplicate key in inventory: %r\n%r'
1808
1919
                                      % (key, bytes))
1809
1920
            info[key] = value
1810
 
        revision_id = intern(info['revision_id'])
1811
 
        root_id = intern(info['root_id'])
1812
 
        search_key_name = intern(info.get('search_key_name', 'plain'))
1813
 
        parent_id_basename_to_file_id = intern(info.get(
1814
 
            'parent_id_basename_to_file_id', None))
1815
 
        if not parent_id_basename_to_file_id.startswith('sha1:'):
1816
 
            raise ValueError('parent_id_basename_to_file_id should be a sha1'
1817
 
                             ' key not %r' % (parent_id_basename_to_file_id,))
 
1921
        revision_id = info['revision_id']
 
1922
        root_id = info['root_id']
 
1923
        search_key_name = info.get('search_key_name', 'plain')
 
1924
        parent_id_basename_to_file_id = info.get(
 
1925
            'parent_id_basename_to_file_id', None)
1818
1926
        id_to_entry = info['id_to_entry']
1819
 
        if not id_to_entry.startswith('sha1:'):
1820
 
            raise ValueError('id_to_entry should be a sha1'
1821
 
                             ' key not %r' % (id_to_entry,))
1822
1927
 
1823
1928
        result = CHKInventory(search_key_name)
1824
1929
        result.revision_id = revision_id
1827
1932
                            result._search_key_name)
1828
1933
        if parent_id_basename_to_file_id is not None:
1829
1934
            result.parent_id_basename_to_file_id = chk_map.CHKMap(
1830
 
                chk_store, StaticTuple(parent_id_basename_to_file_id,),
 
1935
                chk_store, (parent_id_basename_to_file_id,),
1831
1936
                search_key_func=search_key_func)
1832
1937
        else:
1833
1938
            result.parent_id_basename_to_file_id = None
1834
1939
 
1835
 
        result.id_to_entry = chk_map.CHKMap(chk_store,
1836
 
                                            StaticTuple(id_to_entry,),
 
1940
        result.id_to_entry = chk_map.CHKMap(chk_store, (id_to_entry,),
1837
1941
                                            search_key_func=search_key_func)
1838
1942
        if (result.revision_id,) != expected_revision_id:
1839
1943
            raise ValueError("Mismatched revision id and expected: %r, %r" %
1861
1965
        id_to_entry_dict = {}
1862
1966
        parent_id_basename_dict = {}
1863
1967
        for path, entry in inventory.iter_entries():
1864
 
            key = StaticTuple(entry.file_id,).intern()
1865
 
            id_to_entry_dict[key] = entry_to_bytes(entry)
 
1968
            id_to_entry_dict[(entry.file_id,)] = entry_to_bytes(entry)
1866
1969
            p_id_key = parent_id_basename_key(entry)
1867
1970
            parent_id_basename_dict[p_id_key] = entry.file_id
1868
1971
 
1891
1994
            parent_id = entry.parent_id
1892
1995
        else:
1893
1996
            parent_id = ''
1894
 
        return StaticTuple(parent_id, entry.name.encode('utf8')).intern()
 
1997
        return parent_id, entry.name.encode('utf8')
1895
1998
 
1896
1999
    def __getitem__(self, file_id):
1897
2000
        """map a single file_id -> InventoryEntry."""
1902
2005
            return result
1903
2006
        try:
1904
2007
            return self._bytes_to_entry(
1905
 
                self.id_to_entry.iteritems([StaticTuple(file_id,)]).next()[1])
 
2008
                self.id_to_entry.iteritems([(file_id,)]).next()[1])
1906
2009
        except StopIteration:
1907
2010
            # really we're passing an inventory, not a tree...
1908
2011
            raise errors.NoSuchId(self, file_id)
1921
2024
                remaining.append(file_id)
1922
2025
            else:
1923
2026
                result.append(entry)
1924
 
        file_keys = [StaticTuple(f,).intern() for f in remaining]
 
2027
        file_keys = [(f,) for f in remaining]
1925
2028
        for file_key, value in self.id_to_entry.iteritems(file_keys):
1926
2029
            entry = self._bytes_to_entry(value)
1927
2030
            result.append(entry)
1932
2035
        # Perhaps have an explicit 'contains' method on CHKMap ?
1933
2036
        if self._fileid_to_entry_cache.get(file_id, None) is not None:
1934
2037
            return True
1935
 
        return len(list(
1936
 
            self.id_to_entry.iteritems([StaticTuple(file_id,)]))) == 1
 
2038
        return len(list(self.id_to_entry.iteritems([(file_id,)]))) == 1
1937
2039
 
1938
2040
    def is_root(self, file_id):
1939
2041
        return file_id == self.root_id
2068
2170
            delta.append((old_path, new_path, file_id, entry))
2069
2171
        return delta
2070
2172
 
2071
 
    def path2id(self, relpath):
 
2173
    def path2id(self, name):
2072
2174
        """See CommonInventory.path2id()."""
2073
2175
        # TODO: perhaps support negative hits?
2074
 
        result = self._path_to_fileid_cache.get(relpath, None)
 
2176
        result = self._path_to_fileid_cache.get(name, None)
2075
2177
        if result is not None:
2076
2178
            return result
2077
 
        if isinstance(relpath, basestring):
2078
 
            names = osutils.splitpath(relpath)
 
2179
        if isinstance(name, basestring):
 
2180
            names = osutils.splitpath(name)
2079
2181
        else:
2080
 
            names = relpath
 
2182
            names = name
2081
2183
        current_id = self.root_id
2082
2184
        if current_id is None:
2083
2185
            return None
2084
2186
        parent_id_index = self.parent_id_basename_to_file_id
2085
 
        cur_path = None
2086
2187
        for basename in names:
2087
 
            if cur_path is None:
2088
 
                cur_path = basename
2089
 
            else:
2090
 
                cur_path = cur_path + '/' + basename
 
2188
            # TODO: Cache each path we figure out in this function.
2091
2189
            basename_utf8 = basename.encode('utf8')
2092
 
            file_id = self._path_to_fileid_cache.get(cur_path, None)
 
2190
            key_filter = [(current_id, basename_utf8)]
 
2191
            file_id = None
 
2192
            for (parent_id, name_utf8), file_id in parent_id_index.iteritems(
 
2193
                key_filter=key_filter):
 
2194
                if parent_id != current_id or name_utf8 != basename_utf8:
 
2195
                    raise errors.BzrError("corrupt inventory lookup! "
 
2196
                        "%r %r %r %r" % (parent_id, current_id, name_utf8,
 
2197
                        basename_utf8))
2093
2198
            if file_id is None:
2094
 
                key_filter = [StaticTuple(current_id, basename_utf8)]
2095
 
                items = parent_id_index.iteritems(key_filter)
2096
 
                for (parent_id, name_utf8), file_id in items:
2097
 
                    if parent_id != current_id or name_utf8 != basename_utf8:
2098
 
                        raise errors.BzrError("corrupt inventory lookup! "
2099
 
                            "%r %r %r %r" % (parent_id, current_id, name_utf8,
2100
 
                            basename_utf8))
2101
 
                if file_id is None:
2102
 
                    return None
2103
 
                else:
2104
 
                    self._path_to_fileid_cache[cur_path] = file_id
 
2199
                return None
2105
2200
            current_id = file_id
 
2201
        self._path_to_fileid_cache[name] = current_id
2106
2202
        return current_id
2107
2203
 
2108
2204
    def to_lines(self):
2113
2209
            lines.append('search_key_name: %s\n' % (self._search_key_name,))
2114
2210
            lines.append("root_id: %s\n" % self.root_id)
2115
2211
            lines.append('parent_id_basename_to_file_id: %s\n' %
2116
 
                (self.parent_id_basename_to_file_id.key()[0],))
 
2212
                self.parent_id_basename_to_file_id.key())
2117
2213
            lines.append("revision_id: %s\n" % self.revision_id)
2118
 
            lines.append("id_to_entry: %s\n" % (self.id_to_entry.key()[0],))
 
2214
            lines.append("id_to_entry: %s\n" % self.id_to_entry.key())
2119
2215
        else:
2120
2216
            lines.append("revision_id: %s\n" % self.revision_id)
2121
2217
            lines.append("root_id: %s\n" % self.root_id)
2122
2218
            if self.parent_id_basename_to_file_id is not None:
2123
2219
                lines.append('parent_id_basename_to_file_id: %s\n' %
2124
 
                    (self.parent_id_basename_to_file_id.key()[0],))
2125
 
            lines.append("id_to_entry: %s\n" % (self.id_to_entry.key()[0],))
 
2220
                    self.parent_id_basename_to_file_id.key())
 
2221
            lines.append("id_to_entry: %s\n" % self.id_to_entry.key())
2126
2222
        return lines
2127
2223
 
2128
2224
    @property
2134
2230
class CHKInventoryDirectory(InventoryDirectory):
2135
2231
    """A directory in an inventory."""
2136
2232
 
2137
 
    __slots__ = ['_children', '_chk_inventory']
 
2233
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
 
2234
                 'text_id', 'parent_id', '_children', 'executable',
 
2235
                 'revision', 'symlink_target', 'reference_revision',
 
2236
                 '_chk_inventory']
2138
2237
 
2139
2238
    def __init__(self, file_id, name, parent_id, chk_inventory):
2140
2239
        # Don't call InventoryDirectory.__init__ - it isn't right for this
2141
2240
        # class.
2142
2241
        InventoryEntry.__init__(self, file_id, name, parent_id)
2143
2242
        self._children = None
 
2243
        self.kind = 'directory'
2144
2244
        self._chk_inventory = chk_inventory
2145
2245
 
2146
2246
    @property
2165
2265
        parent_id_index = self._chk_inventory.parent_id_basename_to_file_id
2166
2266
        child_keys = set()
2167
2267
        for (parent_id, name_utf8), file_id in parent_id_index.iteritems(
2168
 
            key_filter=[StaticTuple(self.file_id,)]):
2169
 
            child_keys.add(StaticTuple(file_id,))
 
2268
            key_filter=[(self.file_id,)]):
 
2269
            child_keys.add((file_id,))
2170
2270
        cached = set()
2171
2271
        for file_id_key in child_keys:
2172
2272
            entry = self._chk_inventory._fileid_to_entry_cache.get(