~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

(spiv) Make better use of smart server when a local repository is stacked on
 a remote repository. (Andrew Bennetts)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 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
31
31
lazy_import(globals(), """
32
32
import collections
33
33
import copy
34
 
import os
35
34
import re
36
35
import tarfile
37
36
 
43
42
    )
44
43
""")
45
44
 
46
 
from bzrlib.errors import (
47
 
    BzrCheckError,
48
 
    BzrError,
 
45
from bzrlib import (
 
46
    lazy_regex,
 
47
    trace,
49
48
    )
50
 
from bzrlib.trace import mutter
 
49
 
51
50
from bzrlib.static_tuple import StaticTuple
52
51
 
53
52
 
224
223
 
225
224
    def kind_character(self):
226
225
        """Return a short kind indicator useful for appending to names."""
227
 
        raise BzrError('unknown kind %r' % self.kind)
 
226
        raise errors.BzrError('unknown kind %r' % self.kind)
228
227
 
229
228
    known_kinds = ('file', 'directory', 'symlink')
230
229
 
250
249
        """
251
250
        if self.parent_id is not None:
252
251
            if not inv.has_id(self.parent_id):
253
 
                raise BzrCheckError('missing parent {%s} in inventory for revision {%s}'
254
 
                        % (self.parent_id, rev_id))
 
252
                raise errors.BzrCheckError(
 
253
                    'missing parent {%s} in inventory for revision {%s}' % (
 
254
                        self.parent_id, rev_id))
255
255
        checker._add_entry_to_text_key_references(inv, self)
256
256
        self._check(checker, rev_id)
257
257
 
539
539
        # FIXME: which _modified field should we use ? RBC 20051003
540
540
        text_modified = (self.symlink_target != old_entry.symlink_target)
541
541
        if text_modified:
542
 
            mutter("    symlink target changed")
 
542
            trace.mutter("    symlink target changed")
543
543
        meta_modified = False
544
544
        return text_modified, meta_modified
545
545
 
718
718
                # if we finished all children, pop it off the stack
719
719
                stack.pop()
720
720
 
 
721
    def _preload_cache(self):
 
722
        """Populate any caches, we are about to access all items.
 
723
        
 
724
        The default implementation does nothing, because CommonInventory doesn't
 
725
        have a cache.
 
726
        """
 
727
        pass
 
728
    
721
729
    def iter_entries_by_dir(self, from_dir=None, specific_file_ids=None,
722
730
        yield_parents=False):
723
731
        """Iterate over the entries in a directory first order.
736
744
            specific_file_ids = set(specific_file_ids)
737
745
        # TODO? Perhaps this should return the from_dir so that the root is
738
746
        # yielded? or maybe an option?
 
747
        if from_dir is None and specific_file_ids is None:
 
748
            # They are iterating from the root, and have not specified any
 
749
            # specific entries to look at. All current callers fully consume the
 
750
            # iterator, so we can safely assume we are accessing all entries
 
751
            self._preload_cache()
739
752
        if from_dir is None:
740
753
            if self.root is None:
741
754
                return
809
822
                    file_id, self[file_id]))
810
823
        return delta
811
824
 
812
 
    def _get_mutable_inventory(self):
813
 
        """Returns a mutable copy of the object.
814
 
 
815
 
        Some inventories are immutable, yet working trees, for example, needs
816
 
        to mutate exisiting inventories instead of creating a new one.
817
 
        """
818
 
        raise NotImplementedError(self._get_mutable_inventory)
819
 
 
820
825
    def make_entry(self, kind, name, parent_id, file_id=None):
821
826
        """Simple thunk to bzrlib.inventory.make_entry."""
822
827
        return make_entry(kind, name, parent_id, file_id)
836
841
                if ie.kind == 'directory':
837
842
                    descend(ie, child_path)
838
843
 
839
 
        descend(self.root, u'')
 
844
        if self.root is not None:
 
845
            descend(self.root, u'')
840
846
        return accum
841
847
 
842
848
    def directories(self):
1119
1125
            other.add(entry.copy())
1120
1126
        return other
1121
1127
 
1122
 
    def _get_mutable_inventory(self):
1123
 
        """See CommonInventory._get_mutable_inventory."""
1124
 
        return copy.deepcopy(self)
1125
 
 
1126
1128
    def __iter__(self):
1127
1129
        """Iterate over all file-ids."""
1128
1130
        return iter(self._byid)
1168
1170
    def _add_child(self, entry):
1169
1171
        """Add an entry to the inventory, without adding it to its parent"""
1170
1172
        if entry.file_id in self._byid:
1171
 
            raise BzrError("inventory already contains entry with id {%s}" %
1172
 
                           entry.file_id)
 
1173
            raise errors.BzrError(
 
1174
                "inventory already contains entry with id {%s}" %
 
1175
                entry.file_id)
1173
1176
        self._byid[entry.file_id] = entry
1174
1177
        for child in getattr(entry, 'children', {}).itervalues():
1175
1178
            self._add_child(child)
1339
1342
        """
1340
1343
        new_name = ensure_normalized_name(new_name)
1341
1344
        if not is_valid_name(new_name):
1342
 
            raise BzrError("not an acceptable filename: %r" % new_name)
 
1345
            raise errors.BzrError("not an acceptable filename: %r" % new_name)
1343
1346
 
1344
1347
        new_parent = self._byid[new_parent_id]
1345
1348
        if new_name in new_parent.children:
1346
 
            raise BzrError("%r already exists in %r" % (new_name, self.id2path(new_parent_id)))
 
1349
            raise errors.BzrError("%r already exists in %r" %
 
1350
                (new_name, self.id2path(new_parent_id)))
1347
1351
 
1348
1352
        new_parent_idpath = self.get_idpath(new_parent_id)
1349
1353
        if file_id in new_parent_idpath:
1350
 
            raise BzrError("cannot move directory %r into a subdirectory of itself, %r"
 
1354
            raise errors.BzrError(
 
1355
                "cannot move directory %r into a subdirectory of itself, %r"
1351
1356
                    % (self.id2path(file_id), self.id2path(new_parent_id)))
1352
1357
 
1353
1358
        file_ie = self._byid[file_id]
1389
1394
    def __init__(self, search_key_name):
1390
1395
        CommonInventory.__init__(self)
1391
1396
        self._fileid_to_entry_cache = {}
 
1397
        self._fully_cached = False
1392
1398
        self._path_to_fileid_cache = {}
1393
1399
        self._search_key_name = search_key_name
1394
1400
        self.root_id = None
1601
1607
        self._fileid_to_entry_cache[result.file_id] = result
1602
1608
        return result
1603
1609
 
1604
 
    def _get_mutable_inventory(self):
1605
 
        """See CommonInventory._get_mutable_inventory."""
1606
 
        entries = self.iter_entries()
1607
 
        inv = Inventory(None, self.revision_id)
1608
 
        for path, inv_entry in entries:
1609
 
            inv.add(inv_entry.copy())
1610
 
        return inv
1611
 
 
1612
1610
    def create_by_apply_delta(self, inventory_delta, new_revision_id,
1613
1611
        propagate_caches=False):
1614
1612
        """Create a new CHKInventory by applying inventory_delta to this one.
1955
1953
 
1956
1954
    def iter_just_entries(self):
1957
1955
        """Iterate over all entries.
1958
 
        
 
1956
 
1959
1957
        Unlike iter_entries(), just the entries are returned (not (path, ie))
1960
1958
        and the order of entries is undefined.
1961
1959
 
1969
1967
                self._fileid_to_entry_cache[file_id] = ie
1970
1968
            yield ie
1971
1969
 
 
1970
    def _preload_cache(self):
 
1971
        """Make sure all file-ids are in _fileid_to_entry_cache"""
 
1972
        if self._fully_cached:
 
1973
            return # No need to do it again
 
1974
        # The optimal sort order is to use iteritems() directly
 
1975
        cache = self._fileid_to_entry_cache
 
1976
        for key, entry in self.id_to_entry.iteritems():
 
1977
            file_id = key[0]
 
1978
            if file_id not in cache:
 
1979
                ie = self._bytes_to_entry(entry)
 
1980
                cache[file_id] = ie
 
1981
            else:
 
1982
                ie = cache[file_id]
 
1983
        last_parent_id = last_parent_ie = None
 
1984
        pid_items = self.parent_id_basename_to_file_id.iteritems()
 
1985
        for key, child_file_id in pid_items:
 
1986
            if key == ('', ''): # This is the root
 
1987
                if child_file_id != self.root_id:
 
1988
                    raise ValueError('Data inconsistency detected.'
 
1989
                        ' We expected data with key ("","") to match'
 
1990
                        ' the root id, but %s != %s'
 
1991
                        % (child_file_id, self.root_id))
 
1992
                continue
 
1993
            parent_id, basename = key
 
1994
            ie = cache[child_file_id]
 
1995
            if parent_id == last_parent_id:
 
1996
                parent_ie = last_parent_ie
 
1997
            else:
 
1998
                parent_ie = cache[parent_id]
 
1999
            if parent_ie.kind != 'directory':
 
2000
                raise ValueError('Data inconsistency detected.'
 
2001
                    ' An entry in the parent_id_basename_to_file_id map'
 
2002
                    ' has parent_id {%s} but the kind of that object'
 
2003
                    ' is %r not "directory"' % (parent_id, parent_ie.kind))
 
2004
            if parent_ie._children is None:
 
2005
                parent_ie._children = {}
 
2006
            basename = basename.decode('utf-8')
 
2007
            if basename in parent_ie._children:
 
2008
                existing_ie = parent_ie._children[basename]
 
2009
                if existing_ie != ie:
 
2010
                    raise ValueError('Data inconsistency detected.'
 
2011
                        ' Two entries with basename %r were found'
 
2012
                        ' in the parent entry {%s}'
 
2013
                        % (basename, parent_id))
 
2014
            if basename != ie.name:
 
2015
                raise ValueError('Data inconsistency detected.'
 
2016
                    ' In the parent_id_basename_to_file_id map, file_id'
 
2017
                    ' {%s} is listed as having basename %r, but in the'
 
2018
                    ' id_to_entry map it is %r'
 
2019
                    % (child_file_id, basename, ie.name))
 
2020
            parent_ie._children[basename] = ie
 
2021
        self._fully_cached = True
 
2022
 
1972
2023
    def iter_changes(self, basis):
1973
2024
        """Generate a Tree.iter_changes change list between this and basis.
1974
2025
 
2231
2282
    return name
2232
2283
 
2233
2284
 
2234
 
_NAME_RE = None
 
2285
_NAME_RE = lazy_regex.lazy_compile(r'^[^/\\]+$')
2235
2286
 
2236
2287
def is_valid_name(name):
2237
 
    global _NAME_RE
2238
 
    if _NAME_RE is None:
2239
 
        _NAME_RE = re.compile(r'^[^/\\]+$')
2240
 
 
2241
2288
    return bool(_NAME_RE.match(name))
2242
2289
 
2243
2290
 
2333
2380
            raise errors.InconsistentDelta(new_path, item[1],
2334
2381
                "new_path with no entry")
2335
2382
        yield item
 
2383
 
 
2384
 
 
2385
def mutable_inventory_from_tree(tree):
 
2386
    """Create a new inventory that has the same contents as a specified tree.
 
2387
 
 
2388
    :param tree: Revision tree to create inventory from
 
2389
    """
 
2390
    entries = tree.iter_entries_by_dir()
 
2391
    inv = Inventory(None, tree.get_revision_id())
 
2392
    for path, inv_entry in entries:
 
2393
        inv.add(inv_entry.copy())
 
2394
    return inv