Rev 5781: (jelmer) Split inventory-specific functionality out of Tree into in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Mon Apr 11 18:32:17 UTC 2011
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 5781 [merge]
revision-id: pqm at pqm.ubuntu.com-20110411183146-s7r7m89gnp0dl3sr
parent: pqm at pqm.ubuntu.com-20110411165943-g2uhdcy7zz164e4x
parent: jelmer at samba.org-20110411112126-cr080gw4hwofqial
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Mon 2011-04-11 18:31:46 +0000
message:
(jelmer) Split inventory-specific functionality out of Tree into
InventoryTree. (Jelmer Vernooij)
modified:
bzrlib/memorytree.py memorytree.py-20060906023413-4wlkalbdpsxi2r4y-1
bzrlib/mutabletree.py mutabletree.py-20060906023413-4wlkalbdpsxi2r4y-2
bzrlib/revisiontree.py revisiontree.py-20060724012533-bg8xyryhxd0o0i0h-1
bzrlib/tests/per_workingtree/test_inv.py test_inv.py-20070311221604-ighlq8tbn5xq0kuo-1
bzrlib/transform.py transform.py-20060105172343-dd99e54394d91687
bzrlib/tree.py tree.py-20050309040759-9d5f2496be663e77
bzrlib/workingtree_4.py workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
doc/en/release-notes/bzr-2.4.txt bzr2.4.txt-20110114053217-k7ym9jfz243fddjm-1
=== modified file 'bzrlib/memorytree.py'
--- a/bzrlib/memorytree.py 2011-03-06 18:10:47 +0000
+++ b/bzrlib/memorytree.py 2011-04-11 09:56:15 +0000
@@ -26,6 +26,8 @@
errors,
mutabletree,
revision as _mod_revision,
+ revisiontree,
+ tree,
)
from bzrlib.decorators import needs_read_lock
from bzrlib.osutils import sha_file
@@ -33,7 +35,7 @@
from bzrlib.transport.memory import MemoryTransport
-class MemoryTree(mutabletree.MutableTree):
+class MemoryTree(mutabletree.MutableTree, tree.InventoryTree):
"""A MemoryTree is a specialisation of MutableTree.
It maintains nearly no state outside of read_lock and write_lock
=== modified file 'bzrlib/mutabletree.py'
--- a/bzrlib/mutabletree.py 2011-04-05 01:12:15 +0000
+++ b/bzrlib/mutabletree.py 2011-04-09 19:25:42 +0000
@@ -54,7 +54,7 @@
return tree_write_locked
-class MutableTree(tree.Tree):
+class MutableTree(tree.InventoryTree):
"""A MutableTree is a specialisation of Tree which is able to be mutated.
Generally speaking these mutations are only possible within a lock_write
=== modified file 'bzrlib/revisiontree.py'
--- a/bzrlib/revisiontree.py 2010-11-29 23:35:13 +0000
+++ b/bzrlib/revisiontree.py 2011-04-09 19:25:42 +0000
@@ -25,7 +25,7 @@
)
-class RevisionTree(tree.Tree):
+class RevisionTree(tree.InventoryTree):
"""Tree viewing a previous revision.
File text can be retrieved from the text store.
=== modified file 'bzrlib/tests/per_workingtree/test_inv.py'
--- a/bzrlib/tests/per_workingtree/test_inv.py 2009-07-10 07:14:02 +0000
+++ b/bzrlib/tests/per_workingtree/test_inv.py 2011-04-09 19:25:42 +0000
@@ -18,12 +18,8 @@
import os
-import time
-from bzrlib import (
- errors,
- inventory,
- )
+from bzrlib import inventory
from bzrlib.tests.per_workingtree import TestCaseWithWorkingTree
=== modified file 'bzrlib/transform.py'
--- a/bzrlib/transform.py 2011-04-08 12:28:05 +0000
+++ b/bzrlib/transform.py 2011-04-09 20:17:43 +0000
@@ -1932,7 +1932,7 @@
raise NotImplementedError(self.new_orphan)
-class _PreviewTree(tree.Tree):
+class _PreviewTree(tree.InventoryTree):
"""Partial implementation of Tree to support show_diff_trees"""
def __init__(self, transform):
=== modified file 'bzrlib/tree.py'
--- a/bzrlib/tree.py 2011-04-09 15:16:09 +0000
+++ b/bzrlib/tree.py 2011-04-11 18:31:46 +0000
@@ -50,13 +50,6 @@
* `RevisionTree` is a tree as recorded at some point in the past.
- Trees contain an `Inventory` object, and also know how to retrieve
- file texts mentioned in the inventory, either from a working
- directory or from a store.
-
- It is possible for trees to contain files that are not described
- in their inventory or vice versa; for this use `filenames()`.
-
Trees can be compared, etc, regardless of whether they are working
trees or versioned trees.
"""
@@ -128,13 +121,13 @@
raise NotImplementedError(self.has_filename)
def has_id(self, file_id):
- return self.inventory.has_id(file_id)
+ raise NotImplementedError(self.has_id)
def __contains__(self, file_id):
return self.has_id(file_id)
def has_or_had_id(self, file_id):
- return self.inventory.has_id(file_id)
+ raise NotImplementedError(self.has_or_had_id)
def is_ignored(self, filename):
"""Check whether the filename is ignored by this tree.
@@ -145,7 +138,8 @@
return False
def __iter__(self):
- return iter(self.inventory)
+ """Yield all file ids in this tree."""
+ raise NotImplementedError(self.__iter__)
def all_file_ids(self):
"""Iterate through all file ids, including ids for missing files."""
@@ -156,9 +150,8 @@
:raises NoSuchId:
"""
- return self.inventory.id2path(file_id)
+ raise NotImplementedError(self.id2path)
- @needs_read_lock
def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
"""Walk the tree in 'by_dir' order.
@@ -187,8 +180,7 @@
down to specific_file_ids that have been requested. This has no
impact if specific_file_ids is None.
"""
- return self.inventory.iter_entries_by_dir(
- specific_file_ids=specific_file_ids, yield_parents=yield_parents)
+ raise NotImplementedError(self.iter_entries_by_dir)
def iter_references(self):
if self.supports_tree_reference():
@@ -245,9 +237,6 @@
def _file_size(self, entry, stat_value):
raise NotImplementedError(self._file_size)
- def _get_inventory(self):
- return self._inventory
-
def get_file(self, file_id, path=None):
"""Return a file object for the file file_id in the tree.
@@ -322,7 +311,7 @@
raise NotImplementedError(self.get_file_size)
def get_file_by_path(self, path):
- return self.get_file(self._inventory.path2id(path), path)
+ raise NotImplementedError(self.get_file_by_path)
def is_executable(self, file_id, path=None):
"""Check if a file is executable.
@@ -369,83 +358,6 @@
"""
raise NotImplementedError(self.get_symlink_target)
- def get_canonical_inventory_paths(self, paths):
- """Like get_canonical_inventory_path() but works on multiple items.
-
- :param paths: A sequence of paths relative to the root of the tree.
- :return: A list of paths, with each item the corresponding input path
- adjusted to account for existing elements that match case
- insensitively.
- """
- return list(self._yield_canonical_inventory_paths(paths))
-
- def get_canonical_inventory_path(self, path):
- """Returns the first inventory item that case-insensitively matches path.
-
- If a path matches exactly, it is returned. If no path matches exactly
- but more than one path matches case-insensitively, it is implementation
- defined which is returned.
-
- If no path matches case-insensitively, the input path is returned, but
- with as many path entries that do exist changed to their canonical
- form.
-
- If you need to resolve many names from the same tree, you should
- use get_canonical_inventory_paths() to avoid O(N) behaviour.
-
- :param path: A paths relative to the root of the tree.
- :return: The input path adjusted to account for existing elements
- that match case insensitively.
- """
- return self._yield_canonical_inventory_paths([path]).next()
-
- def _yield_canonical_inventory_paths(self, paths):
- for path in paths:
- # First, if the path as specified exists exactly, just use it.
- if self.path2id(path) is not None:
- yield path
- continue
- # go walkin...
- cur_id = self.get_root_id()
- cur_path = ''
- bit_iter = iter(path.split("/"))
- for elt in bit_iter:
- lelt = elt.lower()
- new_path = None
- for child in self.iter_children(cur_id):
- try:
- # XXX: it seem like if the child is known to be in the
- # tree, we shouldn't need to go from its id back to
- # its path -- mbp 2010-02-11
- #
- # XXX: it seems like we could be more efficient
- # by just directly looking up the original name and
- # only then searching all children; also by not
- # chopping paths so much. -- mbp 2010-02-11
- child_base = os.path.basename(self.id2path(child))
- if (child_base == elt):
- # if we found an exact match, we can stop now; if
- # we found an approximate match we need to keep
- # searching because there might be an exact match
- # later.
- cur_id = child
- new_path = osutils.pathjoin(cur_path, child_base)
- break
- elif child_base.lower() == lelt:
- cur_id = child
- new_path = osutils.pathjoin(cur_path, child_base)
- except errors.NoSuchId:
- # before a change is committed we can see this error...
- continue
- if new_path:
- cur_path = new_path
- else:
- # got to the end of this directory and no entries matched.
- # Return what matched so far, plus the rest as specified.
- cur_path = osutils.pathjoin(cur_path, elt, *list(bit_iter))
- break
- yield cur_path
- # all done.
def get_root_id(self):
"""Return the file_id for the root of this tree."""
@@ -513,6 +425,7 @@
@staticmethod
def _file_revision(revision_tree, file_id):
"""Determine the revision associated with a file in a given tree."""
+ # FIXME: Shouldn't this be a RevisionTree method?
revision_tree.lock_read()
try:
return revision_tree.inventory[file_id].revision
@@ -537,9 +450,6 @@
vf.fallback_versionedfiles.append(base_vf)
return last_revision
- inventory = property(_get_inventory,
- doc="Inventory of this Tree")
-
def _check_retrieved(self, ie, f):
if not __debug__:
return
@@ -562,10 +472,9 @@
"file is actually %s" % fp['sha1'],
"store is probably damaged/corrupt"])
- @needs_read_lock
def path2id(self, path):
"""Return the id for path in this tree."""
- return self._inventory.path2id(path)
+ raise NotImplementedError(self.path2id)
def paths2ids(self, paths, trees=[], require_versioned=True):
"""Return all the ids that can be reached by walking from paths.
@@ -628,11 +537,7 @@
:return: set of paths.
"""
- # NB: we specifically *don't* call self.has_filename, because for
- # WorkingTrees that can indicate files that exist on disk but that
- # are not versioned.
- pred = self.inventory.has_filename
- return set((p for p in paths if not pred(p)))
+ raise NotImplementedError(self.filter_unversioned_files)
def walkdirs(self, prefix=""):
"""Walk the contents of this tree from path down.
@@ -735,6 +640,156 @@
return searcher
+class InventoryTree(Tree):
+ """A tree that relies on an inventory for its metadata.
+
+ Trees contain an `Inventory` object, and also know how to retrieve
+ file texts mentioned in the inventory, either from a working
+ directory or from a store.
+
+ It is possible for trees to contain files that are not described
+ in their inventory or vice versa; for this use `filenames()`.
+
+ Subclasses should set the _inventory attribute, which is considered
+ private to external API users.
+ """
+
+ def get_canonical_inventory_paths(self, paths):
+ """Like get_canonical_inventory_path() but works on multiple items.
+
+ :param paths: A sequence of paths relative to the root of the tree.
+ :return: A list of paths, with each item the corresponding input path
+ adjusted to account for existing elements that match case
+ insensitively.
+ """
+ return list(self._yield_canonical_inventory_paths(paths))
+
+ def get_canonical_inventory_path(self, path):
+ """Returns the first inventory item that case-insensitively matches path.
+
+ If a path matches exactly, it is returned. If no path matches exactly
+ but more than one path matches case-insensitively, it is implementation
+ defined which is returned.
+
+ If no path matches case-insensitively, the input path is returned, but
+ with as many path entries that do exist changed to their canonical
+ form.
+
+ If you need to resolve many names from the same tree, you should
+ use get_canonical_inventory_paths() to avoid O(N) behaviour.
+
+ :param path: A paths relative to the root of the tree.
+ :return: The input path adjusted to account for existing elements
+ that match case insensitively.
+ """
+ return self._yield_canonical_inventory_paths([path]).next()
+
+ def _yield_canonical_inventory_paths(self, paths):
+ for path in paths:
+ # First, if the path as specified exists exactly, just use it.
+ if self.path2id(path) is not None:
+ yield path
+ continue
+ # go walkin...
+ cur_id = self.get_root_id()
+ cur_path = ''
+ bit_iter = iter(path.split("/"))
+ for elt in bit_iter:
+ lelt = elt.lower()
+ new_path = None
+ for child in self.iter_children(cur_id):
+ try:
+ # XXX: it seem like if the child is known to be in the
+ # tree, we shouldn't need to go from its id back to
+ # its path -- mbp 2010-02-11
+ #
+ # XXX: it seems like we could be more efficient
+ # by just directly looking up the original name and
+ # only then searching all children; also by not
+ # chopping paths so much. -- mbp 2010-02-11
+ child_base = os.path.basename(self.id2path(child))
+ if (child_base == elt):
+ # if we found an exact match, we can stop now; if
+ # we found an approximate match we need to keep
+ # searching because there might be an exact match
+ # later.
+ cur_id = child
+ new_path = osutils.pathjoin(cur_path, child_base)
+ break
+ elif child_base.lower() == lelt:
+ cur_id = child
+ new_path = osutils.pathjoin(cur_path, child_base)
+ except errors.NoSuchId:
+ # before a change is committed we can see this error...
+ continue
+ if new_path:
+ cur_path = new_path
+ else:
+ # got to the end of this directory and no entries matched.
+ # Return what matched so far, plus the rest as specified.
+ cur_path = osutils.pathjoin(cur_path, elt, *list(bit_iter))
+ break
+ yield cur_path
+ # all done.
+
+ def _get_inventory(self):
+ return self._inventory
+
+ inventory = property(_get_inventory,
+ doc="Inventory of this Tree")
+
+ @needs_read_lock
+ def path2id(self, path):
+ """Return the id for path in this tree."""
+ return self._inventory.path2id(path)
+
+ def id2path(self, file_id):
+ """Return the path for a file id.
+
+ :raises NoSuchId:
+ """
+ return self.inventory.id2path(file_id)
+
+ def has_id(self, file_id):
+ return self.inventory.has_id(file_id)
+
+ def has_or_had_id(self, file_id):
+ return self.inventory.has_id(file_id)
+
+ def __iter__(self):
+ return iter(self.inventory)
+
+ def filter_unversioned_files(self, paths):
+ """Filter out paths that are versioned.
+
+ :return: set of paths.
+ """
+ # NB: we specifically *don't* call self.has_filename, because for
+ # WorkingTrees that can indicate files that exist on disk but that
+ # are not versioned.
+ pred = self.inventory.has_filename
+ return set((p for p in paths if not pred(p)))
+
+ @needs_read_lock
+ def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
+ """Walk the tree in 'by_dir' order.
+
+ This will yield each entry in the tree as a (path, entry) tuple.
+ The order that they are yielded is:
+
+ See Tree.iter_entries_by_dir for details.
+
+ :param yield_parents: If True, yield the parents from the root leading
+ down to specific_file_ids that have been requested. This has no
+ impact if specific_file_ids is None.
+ """
+ return self.inventory.iter_entries_by_dir(
+ specific_file_ids=specific_file_ids, yield_parents=yield_parents)
+
+ def get_file_by_path(self, path):
+ return self.get_file(self._inventory.path2id(path), path)
+
+
######################################################################
# diff
=== modified file 'bzrlib/workingtree_4.py'
--- a/bzrlib/workingtree_4.py 2011-04-08 09:54:43 +0000
+++ b/bzrlib/workingtree_4.py 2011-04-09 19:25:42 +0000
@@ -60,12 +60,19 @@
safe_unicode,
)
from bzrlib.transport.local import LocalTransport
-from bzrlib.tree import InterTree
-from bzrlib.tree import Tree
-from bzrlib.workingtree import WorkingTree, WorkingTree3, WorkingTreeFormat3
+from bzrlib.tree import (
+ InterTree,
+ InventoryTree,
+ )
+from bzrlib.workingtree import (
+ WorkingTree,
+ WorkingTree3,
+ WorkingTreeFormat3,
+ )
class DirStateWorkingTree(WorkingTree3):
+
def __init__(self, basedir,
branch,
_control_files=None,
@@ -1250,7 +1257,7 @@
def rename_one(self, from_rel, to_rel, after=False):
"""See WorkingTree.rename_one"""
self.flush()
- WorkingTree.rename_one(self, from_rel, to_rel, after)
+ super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
@needs_tree_write_lock
def apply_inventory_delta(self, changes):
@@ -1604,7 +1611,7 @@
return True
-class DirStateRevisionTree(Tree):
+class DirStateRevisionTree(InventoryTree):
"""A revision tree pulling the inventory from a dirstate.
Note that this is one of the historical (ie revision) trees cached in the
=== modified file 'doc/en/release-notes/bzr-2.4.txt'
--- a/doc/en/release-notes/bzr-2.4.txt 2011-04-08 22:51:59 +0000
+++ b/doc/en/release-notes/bzr-2.4.txt 2011-04-11 09:56:15 +0000
@@ -95,6 +95,10 @@
mysteriously silent.)
(Martin Pool)
+* Inventory-specific functionality has been split out of ``Tree`` into
+ a new ``InventoryTree`` class. Tree instances no longer
+ necessarily provide an ``inventory`` attribute. (Jelmer Vernooij)
+
* New method ``Hooks.uninstall_named_hook``. (Jelmer Vernooij, #301472)
* ``revision_graph_can_have_wrong_parents`` is now an attribute
More information about the bazaar-commits
mailing list