/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: Canonical.com Patch Queue Manager
  • Date: 2009-12-21 06:03:07 UTC
  • mfrom: (4665.7.3 serve-init)
  • Revision ID: pqm@pqm.ubuntu.com-20091221060307-uvja3vdy1o6dzzy0
(mbp) example debian init script

Show diffs side-by-side

added added

removed removed

Lines of Context:
51
51
    )
52
52
from bzrlib.symbol_versioning import deprecated_in, deprecated_method
53
53
from bzrlib.trace import mutter
 
54
from bzrlib.static_tuple import StaticTuple
54
55
 
55
56
 
56
57
class InventoryEntry(object):
958
959
        descend(self.root, u'')
959
960
        return accum
960
961
 
961
 
    def path2id(self, name):
 
962
    def path2id(self, relpath):
962
963
        """Walk down through directories to return entry of last component.
963
964
 
964
 
        names may be either a list of path components, or a single
965
 
        string, in which case it is automatically split.
 
965
        :param relpath: may be either a list of path components, or a single
 
966
            string, in which case it is automatically split.
966
967
 
967
968
        This returns the entry of the last component in the path,
968
969
        which may be either a file or a directory.
969
970
 
970
971
        Returns None IFF the path is not found.
971
972
        """
972
 
        if isinstance(name, basestring):
973
 
            name = osutils.splitpath(name)
974
 
 
975
 
        # mutter("lookup path %r" % name)
 
973
        if isinstance(relpath, basestring):
 
974
            names = osutils.splitpath(relpath)
 
975
        else:
 
976
            names = relpath
976
977
 
977
978
        try:
978
979
            parent = self.root
981
982
            return None
982
983
        if parent is None:
983
984
            return None
984
 
        for f in name:
 
985
        for f in names:
985
986
            try:
986
987
                children = getattr(parent, 'children', None)
987
988
                if children is None:
1197
1198
            raise errors.InconsistentDelta("<deleted>", parent_id,
1198
1199
                "The file id was deleted but its children were not deleted.")
1199
1200
 
 
1201
    def create_by_apply_delta(self, inventory_delta, new_revision_id,
 
1202
                              propagate_caches=False):
 
1203
        """See CHKInventory.create_by_apply_delta()"""
 
1204
        new_inv = self.copy()
 
1205
        new_inv.apply_delta(inventory_delta)
 
1206
        new_inv.revision_id = new_revision_id
 
1207
        return new_inv
 
1208
 
1200
1209
    def _set_root(self, ie):
1201
1210
        self.root = ie
1202
1211
        self._byid = {self.root.file_id: self.root}
1546
1555
        else:
1547
1556
            raise ValueError("unknown kind %r" % entry.kind)
1548
1557
 
 
1558
    def _expand_fileids_to_parents_and_children(self, file_ids):
 
1559
        """Give a more wholistic view starting with the given file_ids.
 
1560
 
 
1561
        For any file_id which maps to a directory, we will include all children
 
1562
        of that directory. We will also include all directories which are
 
1563
        parents of the given file_ids, but we will not include their children.
 
1564
 
 
1565
        eg:
 
1566
          /     # TREE_ROOT
 
1567
          foo/  # foo-id
 
1568
            baz # baz-id
 
1569
            frob/ # frob-id
 
1570
              fringle # fringle-id
 
1571
          bar/  # bar-id
 
1572
            bing # bing-id
 
1573
 
 
1574
        if given [foo-id] we will include
 
1575
            TREE_ROOT as interesting parents
 
1576
        and 
 
1577
            foo-id, baz-id, frob-id, fringle-id
 
1578
        As interesting ids.
 
1579
        """
 
1580
        interesting = set()
 
1581
        # TODO: Pre-pass over the list of fileids to see if anything is already
 
1582
        #       deserialized in self._fileid_to_entry_cache
 
1583
 
 
1584
        directories_to_expand = set()
 
1585
        children_of_parent_id = {}
 
1586
        # It is okay if some of the fileids are missing
 
1587
        for entry in self._getitems(file_ids):
 
1588
            if entry.kind == 'directory':
 
1589
                directories_to_expand.add(entry.file_id)
 
1590
            interesting.add(entry.parent_id)
 
1591
            children_of_parent_id.setdefault(entry.parent_id, []
 
1592
                                             ).append(entry.file_id)
 
1593
 
 
1594
        # Now, interesting has all of the direct parents, but not the
 
1595
        # parents of those parents. It also may have some duplicates with
 
1596
        # specific_fileids
 
1597
        remaining_parents = interesting.difference(file_ids)
 
1598
        # When we hit the TREE_ROOT, we'll get an interesting parent of None,
 
1599
        # but we don't actually want to recurse into that
 
1600
        interesting.add(None) # this will auto-filter it in the loop
 
1601
        remaining_parents.discard(None) 
 
1602
        while remaining_parents:
 
1603
            next_parents = set()
 
1604
            for entry in self._getitems(remaining_parents):
 
1605
                next_parents.add(entry.parent_id)
 
1606
                children_of_parent_id.setdefault(entry.parent_id, []
 
1607
                                                 ).append(entry.file_id)
 
1608
            # Remove any search tips we've already processed
 
1609
            remaining_parents = next_parents.difference(interesting)
 
1610
            interesting.update(remaining_parents)
 
1611
            # We should probably also .difference(directories_to_expand)
 
1612
        interesting.update(file_ids)
 
1613
        interesting.discard(None)
 
1614
        while directories_to_expand:
 
1615
            # Expand directories by looking in the
 
1616
            # parent_id_basename_to_file_id map
 
1617
            keys = [StaticTuple(f,).intern() for f in directories_to_expand]
 
1618
            directories_to_expand = set()
 
1619
            items = self.parent_id_basename_to_file_id.iteritems(keys)
 
1620
            next_file_ids = set([item[1] for item in items])
 
1621
            next_file_ids = next_file_ids.difference(interesting)
 
1622
            interesting.update(next_file_ids)
 
1623
            for entry in self._getitems(next_file_ids):
 
1624
                if entry.kind == 'directory':
 
1625
                    directories_to_expand.add(entry.file_id)
 
1626
                children_of_parent_id.setdefault(entry.parent_id, []
 
1627
                                                 ).append(entry.file_id)
 
1628
        return interesting, children_of_parent_id
 
1629
 
 
1630
    def filter(self, specific_fileids):
 
1631
        """Get an inventory view filtered against a set of file-ids.
 
1632
 
 
1633
        Children of directories and parents are included.
 
1634
 
 
1635
        The result may or may not reference the underlying inventory
 
1636
        so it should be treated as immutable.
 
1637
        """
 
1638
        (interesting,
 
1639
         parent_to_children) = self._expand_fileids_to_parents_and_children(
 
1640
                                specific_fileids)
 
1641
        # There is some overlap here, but we assume that all interesting items
 
1642
        # are in the _fileid_to_entry_cache because we had to read them to
 
1643
        # determine if they were a dir we wanted to recurse, or just a file
 
1644
        # This should give us all the entries we'll want to add, so start
 
1645
        # adding
 
1646
        other = Inventory(self.root_id)
 
1647
        other.root.revision = self.root.revision
 
1648
        other.revision_id = self.revision_id
 
1649
        if not interesting or not parent_to_children:
 
1650
            # empty filter, or filtering entrys that don't exist
 
1651
            # (if even 1 existed, then we would have populated
 
1652
            # parent_to_children with at least the tree root.)
 
1653
            return other
 
1654
        cache = self._fileid_to_entry_cache
 
1655
        try:
 
1656
            remaining_children = collections.deque(parent_to_children[self.root_id])
 
1657
        except:
 
1658
            import pdb; pdb.set_trace()
 
1659
            raise
 
1660
        while remaining_children:
 
1661
            file_id = remaining_children.popleft()
 
1662
            ie = cache[file_id]
 
1663
            if ie.kind == 'directory':
 
1664
                ie = ie.copy() # We create a copy to depopulate the .children attribute
 
1665
            # TODO: depending on the uses of 'other' we should probably alwyas
 
1666
            #       '.copy()' to prevent someone from mutating other and
 
1667
            #       invaliding our internal cache
 
1668
            other.add(ie)
 
1669
            if file_id in parent_to_children:
 
1670
                remaining_children.extend(parent_to_children[file_id])
 
1671
        return other
 
1672
 
1549
1673
    @staticmethod
1550
1674
    def _bytes_to_utf8name_key(bytes):
1551
1675
        """Get the file_id, revision_id key out of bytes."""
1553
1677
        # to filter out empty names because of non rich-root...
1554
1678
        sections = bytes.split('\n')
1555
1679
        kind, file_id = sections[0].split(': ')
1556
 
        return (sections[2], file_id, sections[3])
 
1680
        return (sections[2], intern(file_id), intern(sections[3]))
1557
1681
 
1558
1682
    def _bytes_to_entry(self, bytes):
1559
1683
        """Deserialise a serialised entry."""
1581
1705
            result.reference_revision = sections[4]
1582
1706
        else:
1583
1707
            raise ValueError("Not a serialised entry %r" % bytes)
1584
 
        result.revision = sections[3]
 
1708
        result.file_id = intern(result.file_id)
 
1709
        result.revision = intern(sections[3])
1585
1710
        if result.parent_id == '':
1586
1711
            result.parent_id = None
1587
1712
        self._fileid_to_entry_cache[result.file_id] = result
1685
1810
                        pass
1686
1811
                deletes.add(file_id)
1687
1812
            else:
1688
 
                new_key = (file_id,)
 
1813
                new_key = StaticTuple(file_id,)
1689
1814
                new_value = result._entry_to_bytes(entry)
1690
1815
                # Update caches. It's worth doing this whether
1691
1816
                # we're propagating the old caches or not.
1694
1819
            if old_path is None:
1695
1820
                old_key = None
1696
1821
            else:
1697
 
                old_key = (file_id,)
 
1822
                old_key = StaticTuple(file_id,)
1698
1823
                if self.id2path(file_id) != old_path:
1699
1824
                    raise errors.InconsistentDelta(old_path, file_id,
1700
1825
                        "Entry was at wrong other path %r." %
1701
1826
                        self.id2path(file_id))
1702
1827
                altered.add(file_id)
1703
 
            id_to_entry_delta.append((old_key, new_key, new_value))
 
1828
            id_to_entry_delta.append(StaticTuple(old_key, new_key, new_value))
1704
1829
            if result.parent_id_basename_to_file_id is not None:
1705
1830
                # parent_id, basename changes
1706
1831
                if old_path is None:
1793
1918
                raise errors.BzrError('Duplicate key in inventory: %r\n%r'
1794
1919
                                      % (key, bytes))
1795
1920
            info[key] = value
1796
 
        revision_id = info['revision_id']
1797
 
        root_id = info['root_id']
1798
 
        search_key_name = info.get('search_key_name', 'plain')
1799
 
        parent_id_basename_to_file_id = info.get(
1800
 
            'parent_id_basename_to_file_id', None)
 
1921
        revision_id = intern(info['revision_id'])
 
1922
        root_id = intern(info['root_id'])
 
1923
        search_key_name = intern(info.get('search_key_name', 'plain'))
 
1924
        parent_id_basename_to_file_id = intern(info.get(
 
1925
            'parent_id_basename_to_file_id', None))
 
1926
        if not parent_id_basename_to_file_id.startswith('sha1:'):
 
1927
            raise ValueError('parent_id_basename_to_file_id should be a sha1'
 
1928
                             ' key not %r' % (parent_id_basename_to_file_id,))
1801
1929
        id_to_entry = info['id_to_entry']
 
1930
        if not id_to_entry.startswith('sha1:'):
 
1931
            raise ValueError('id_to_entry should be a sha1'
 
1932
                             ' key not %r' % (id_to_entry,))
1802
1933
 
1803
1934
        result = CHKInventory(search_key_name)
1804
1935
        result.revision_id = revision_id
1807
1938
                            result._search_key_name)
1808
1939
        if parent_id_basename_to_file_id is not None:
1809
1940
            result.parent_id_basename_to_file_id = chk_map.CHKMap(
1810
 
                chk_store, (parent_id_basename_to_file_id,),
 
1941
                chk_store, StaticTuple(parent_id_basename_to_file_id,),
1811
1942
                search_key_func=search_key_func)
1812
1943
        else:
1813
1944
            result.parent_id_basename_to_file_id = None
1814
1945
 
1815
 
        result.id_to_entry = chk_map.CHKMap(chk_store, (id_to_entry,),
 
1946
        result.id_to_entry = chk_map.CHKMap(chk_store,
 
1947
                                            StaticTuple(id_to_entry,),
1816
1948
                                            search_key_func=search_key_func)
1817
1949
        if (result.revision_id,) != expected_revision_id:
1818
1950
            raise ValueError("Mismatched revision id and expected: %r, %r" %
1840
1972
        id_to_entry_dict = {}
1841
1973
        parent_id_basename_dict = {}
1842
1974
        for path, entry in inventory.iter_entries():
1843
 
            id_to_entry_dict[(entry.file_id,)] = entry_to_bytes(entry)
 
1975
            key = StaticTuple(entry.file_id,).intern()
 
1976
            id_to_entry_dict[key] = entry_to_bytes(entry)
1844
1977
            p_id_key = parent_id_basename_key(entry)
1845
1978
            parent_id_basename_dict[p_id_key] = entry.file_id
1846
1979
 
1869
2002
            parent_id = entry.parent_id
1870
2003
        else:
1871
2004
            parent_id = ''
1872
 
        return parent_id, entry.name.encode('utf8')
 
2005
        return StaticTuple(parent_id, entry.name.encode('utf8')).intern()
1873
2006
 
1874
2007
    def __getitem__(self, file_id):
1875
2008
        """map a single file_id -> InventoryEntry."""
1880
2013
            return result
1881
2014
        try:
1882
2015
            return self._bytes_to_entry(
1883
 
                self.id_to_entry.iteritems([(file_id,)]).next()[1])
 
2016
                self.id_to_entry.iteritems([StaticTuple(file_id,)]).next()[1])
1884
2017
        except StopIteration:
1885
2018
            # really we're passing an inventory, not a tree...
1886
2019
            raise errors.NoSuchId(self, file_id)
1887
2020
 
 
2021
    def _getitems(self, file_ids):
 
2022
        """Similar to __getitem__, but lets you query for multiple.
 
2023
        
 
2024
        The returned order is undefined. And currently if an item doesn't
 
2025
        exist, it isn't included in the output.
 
2026
        """
 
2027
        result = []
 
2028
        remaining = []
 
2029
        for file_id in file_ids:
 
2030
            entry = self._fileid_to_entry_cache.get(file_id, None)
 
2031
            if entry is None:
 
2032
                remaining.append(file_id)
 
2033
            else:
 
2034
                result.append(entry)
 
2035
        file_keys = [StaticTuple(f,).intern() for f in remaining]
 
2036
        for file_key, value in self.id_to_entry.iteritems(file_keys):
 
2037
            entry = self._bytes_to_entry(value)
 
2038
            result.append(entry)
 
2039
            self._fileid_to_entry_cache[entry.file_id] = entry
 
2040
        return result
 
2041
 
1888
2042
    def has_id(self, file_id):
1889
2043
        # Perhaps have an explicit 'contains' method on CHKMap ?
1890
2044
        if self._fileid_to_entry_cache.get(file_id, None) is not None:
1891
2045
            return True
1892
 
        return len(list(self.id_to_entry.iteritems([(file_id,)]))) == 1
 
2046
        return len(list(
 
2047
            self.id_to_entry.iteritems([StaticTuple(file_id,)]))) == 1
1893
2048
 
1894
2049
    def is_root(self, file_id):
1895
2050
        return file_id == self.root_id
2024
2179
            delta.append((old_path, new_path, file_id, entry))
2025
2180
        return delta
2026
2181
 
2027
 
    def path2id(self, name):
 
2182
    def path2id(self, relpath):
2028
2183
        """See CommonInventory.path2id()."""
2029
2184
        # TODO: perhaps support negative hits?
2030
 
        result = self._path_to_fileid_cache.get(name, None)
 
2185
        result = self._path_to_fileid_cache.get(relpath, None)
2031
2186
        if result is not None:
2032
2187
            return result
2033
 
        if isinstance(name, basestring):
2034
 
            names = osutils.splitpath(name)
 
2188
        if isinstance(relpath, basestring):
 
2189
            names = osutils.splitpath(relpath)
2035
2190
        else:
2036
 
            names = name
 
2191
            names = relpath
2037
2192
        current_id = self.root_id
2038
2193
        if current_id is None:
2039
2194
            return None
2040
2195
        parent_id_index = self.parent_id_basename_to_file_id
 
2196
        cur_path = None
2041
2197
        for basename in names:
2042
 
            # TODO: Cache each path we figure out in this function.
 
2198
            if cur_path is None:
 
2199
                cur_path = basename
 
2200
            else:
 
2201
                cur_path = cur_path + '/' + basename
2043
2202
            basename_utf8 = basename.encode('utf8')
2044
 
            key_filter = [(current_id, basename_utf8)]
2045
 
            file_id = None
2046
 
            for (parent_id, name_utf8), file_id in parent_id_index.iteritems(
2047
 
                key_filter=key_filter):
2048
 
                if parent_id != current_id or name_utf8 != basename_utf8:
2049
 
                    raise errors.BzrError("corrupt inventory lookup! "
2050
 
                        "%r %r %r %r" % (parent_id, current_id, name_utf8,
2051
 
                        basename_utf8))
 
2203
            file_id = self._path_to_fileid_cache.get(cur_path, None)
2052
2204
            if file_id is None:
2053
 
                return None
 
2205
                key_filter = [StaticTuple(current_id, basename_utf8)]
 
2206
                items = parent_id_index.iteritems(key_filter)
 
2207
                for (parent_id, name_utf8), file_id in items:
 
2208
                    if parent_id != current_id or name_utf8 != basename_utf8:
 
2209
                        raise errors.BzrError("corrupt inventory lookup! "
 
2210
                            "%r %r %r %r" % (parent_id, current_id, name_utf8,
 
2211
                            basename_utf8))
 
2212
                if file_id is None:
 
2213
                    return None
 
2214
                else:
 
2215
                    self._path_to_fileid_cache[cur_path] = file_id
2054
2216
            current_id = file_id
2055
 
        self._path_to_fileid_cache[name] = current_id
2056
2217
        return current_id
2057
2218
 
2058
2219
    def to_lines(self):
2063
2224
            lines.append('search_key_name: %s\n' % (self._search_key_name,))
2064
2225
            lines.append("root_id: %s\n" % self.root_id)
2065
2226
            lines.append('parent_id_basename_to_file_id: %s\n' %
2066
 
                self.parent_id_basename_to_file_id.key())
 
2227
                (self.parent_id_basename_to_file_id.key()[0],))
2067
2228
            lines.append("revision_id: %s\n" % self.revision_id)
2068
 
            lines.append("id_to_entry: %s\n" % self.id_to_entry.key())
 
2229
            lines.append("id_to_entry: %s\n" % (self.id_to_entry.key()[0],))
2069
2230
        else:
2070
2231
            lines.append("revision_id: %s\n" % self.revision_id)
2071
2232
            lines.append("root_id: %s\n" % self.root_id)
2072
2233
            if self.parent_id_basename_to_file_id is not None:
2073
2234
                lines.append('parent_id_basename_to_file_id: %s\n' %
2074
 
                    self.parent_id_basename_to_file_id.key())
2075
 
            lines.append("id_to_entry: %s\n" % self.id_to_entry.key())
 
2235
                    (self.parent_id_basename_to_file_id.key()[0],))
 
2236
            lines.append("id_to_entry: %s\n" % (self.id_to_entry.key()[0],))
2076
2237
        return lines
2077
2238
 
2078
2239
    @property
2119
2280
        parent_id_index = self._chk_inventory.parent_id_basename_to_file_id
2120
2281
        child_keys = set()
2121
2282
        for (parent_id, name_utf8), file_id in parent_id_index.iteritems(
2122
 
            key_filter=[(self.file_id,)]):
2123
 
            child_keys.add((file_id,))
 
2283
            key_filter=[StaticTuple(self.file_id,)]):
 
2284
            child_keys.add(StaticTuple(file_id,))
2124
2285
        cached = set()
2125
2286
        for file_id_key in child_keys:
2126
2287
            entry = self._chk_inventory._fileid_to_entry_cache.get(
2159
2320
    try:
2160
2321
        factory = entry_factory[kind]
2161
2322
    except KeyError:
2162
 
        raise BzrError("unknown kind %r" % kind)
 
2323
        raise errors.BadFileKindError(name, kind)
2163
2324
    return factory(file_id, name, parent_id)
2164
2325
 
2165
2326