Rev 2359: Add WorkingTree4.paths2ids which is inventory-usage free if the trees being examined are in the dirstate. in sftp://bazaar.launchpad.net/%7Ebzr/bzr/dirstate/
Robert Collins
robertc at robertcollins.net
Thu Feb 22 05:08:04 GMT 2007
At sftp://bazaar.launchpad.net/%7Ebzr/bzr/dirstate/
------------------------------------------------------------
revno: 2359
revision-id: robertc at robertcollins.net-20070222050658-m9gdlj1ypv2gqvjx
parent: robertc at robertcollins.net-20070222024924-bokuavlg2yglgtja
committer: Robert Collins <robertc at robertcollins.net>
branch nick: dirstate
timestamp: Thu 2007-02-22 16:06:58 +1100
message:
Add WorkingTree4.paths2ids which is inventory-usage free if the trees being examined are in the dirstate.
modified:
bzrlib/tests/workingtree_implementations/test_paths2ids.py test_paths2ids.py-20070222011621-kesvovdwm69nndtx-1
bzrlib/tree.py tree.py-20050309040759-9d5f2496be663e77
bzrlib/workingtree_4.py workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
=== modified file 'bzrlib/tests/workingtree_implementations/test_paths2ids.py'
--- a/bzrlib/tests/workingtree_implementations/test_paths2ids.py 2007-02-22 02:49:24 +0000
+++ b/bzrlib/tests/workingtree_implementations/test_paths2ids.py 2007-02-22 05:06:58 +0000
@@ -36,6 +36,8 @@
# TODO: test that supplying paths with duplication - i.e. foo, foo, foo/bar -
# does not result in garbage out.
+# TODO: Are we meant to raise the precise unversioned paths when some are
+# unversioned - if so, test this.
class TestPaths2Ids(TestCaseWithWorkingTree):
@@ -54,6 +56,12 @@
self.assertEqual(set(ids), result)
tree.unlock()
+ def test_paths_none_result_none(self):
+ tree = self.make_branch_and_tree('tree')
+ tree.lock_read()
+ self.assertEqual(None, tree.paths2ids(None))
+ tree.unlock()
+
def test_find_single_root(self):
tree = self.make_branch_and_tree('tree')
self.assertExpectedIds([tree.path2id('')], tree, [''])
@@ -152,13 +160,24 @@
self.assertRaises(errors.PathsNotVersionedError, tree.paths2ids, ['unversioned'])
tree.unlock()
- def test_unversioned_multiple_trees(self):
- # in this test, the path is unversioned in only one tree, but it should
- # still raise an error.
- tree = self.make_branch_and_tree('tree')
- tree.commit('make basis')
- basis = tree.basis_tree()
- self.build_tree(['tree/unversioned'])
+ def test_unversioned_in_one_of_multiple_trees(self):
+ # in this test, the path is unversioned in only one tree, and thus
+ # should not raise an error: it must be unversioned in *all* trees to
+ # error.
+ tree = self.make_branch_and_tree('tree')
+ tree.commit('make basis')
+ basis = tree.basis_tree()
+ self.build_tree(['tree/in-one'])
+ tree.add(['in-one'], ['in-one'])
+ self.assertExpectedIds(['in-one'], tree, ['in-one'], [basis])
+
+ def test_unversioned_all_of_multiple_trees(self):
+ # in this test, the path is unversioned in every tree, and thus
+ # should not raise an error: it must be unversioned in *all* trees to
+ # error.
+ tree = self.make_branch_and_tree('tree')
+ tree.commit('make basis')
+ basis = tree.basis_tree()
self.assertExpectedIds([], tree, ['unversioned'], [basis],
require_versioned=False)
tree.lock_read()
=== modified file 'bzrlib/tree.py'
--- a/bzrlib/tree.py 2007-02-22 02:45:55 +0000
+++ b/bzrlib/tree.py 2007-02-22 05:06:58 +0000
@@ -226,6 +226,9 @@
tree, and vice versa.
:param paths: An iterable of paths to start converting to ids from.
+ Alternatively, if paths is None, no ids should be calculated and None
+ will be returned. This is offered to make calling the api unconditional
+ for code that *might* take a list of files.
:param trees: Additional trees to consider.
:param require_versioned: If False, do not raise NotVersionedError if
an element of paths is not versioned in this tree and all of trees.
=== modified file 'bzrlib/workingtree_4.py'
--- a/bzrlib/workingtree_4.py 2007-02-22 01:10:12 +0000
+++ b/bzrlib/workingtree_4.py 2007-02-22 05:06:58 +0000
@@ -550,6 +550,119 @@
return None
return entry[0][2].decode('utf8')
+ def paths2ids(self, paths, trees=[], require_versioned=True):
+ """See Tree.paths2ids().
+
+ This specialisation fast-paths the case where all the trees are in the
+ dirstate.
+ """
+ if paths is None:
+ return None
+ parents = self.get_parent_ids()
+ for tree in trees:
+ if not (isinstance(tree, DirStateRevisionTree) and tree._revision_id in
+ parents):
+ return super(WorkingTree4, self).paths2ids(paths, trees, require_versioned)
+ search_indexes = [0] + [1 + parents.index(tree._revision_id) for tree in trees]
+ # -- make all paths utf8 --
+ paths_utf8 = set()
+ for path in paths:
+ paths_utf8.add(path.encode('utf8'))
+ paths = paths_utf8
+ # -- paths is now a utf8 path set --
+ # -- get the state object and prepare it.
+ state = self.current_dirstate()
+ state._read_dirblocks_if_needed()
+ def _entries_for_path(path):
+ """Return a list with all the entries that match path for all ids.
+ """
+ dirname, basename = os.path.split(path)
+ key = (dirname, basename, '')
+ block_index, present = state._find_block_index_from_key(key)
+ if not present:
+ # the block which should contain path is absent.
+ return []
+ result = []
+ block = state._dirblocks[block_index][1]
+ entry_index, _ = state._find_entry_index(key, block)
+ # we may need to look at multiple entries at this path: walk while the paths match.
+ while (entry_index < len(block) and
+ block[entry_index][0][0:2] == key[0:2]):
+ result.append(block[entry_index])
+ entry_index += 1
+ return result
+ if require_versioned:
+ # -- check all supplied paths are versioned in all search trees. --
+ all_versioned = True
+ for path in paths:
+ path_entries = _entries_for_path(path)
+ if not path_entries:
+ # this specified path is not present at all: error
+ all_versioned = False
+ break
+ found_versioned = False
+ # for each id at this path
+ for entry in path_entries:
+ # for each tree.
+ for index in search_indexes:
+ if entry[1][index][0] != 'absent':
+ found_versioned = True
+ # all good: found a versioned cell
+ break
+ if not found_versioned:
+ # non of the indexes was not 'absent' at all ids for this
+ # path.
+ all_versioned = False
+ break
+ if not all_versioned:
+ raise errors.PathsNotVersionedError(paths)
+ # -- remove redundancy in supplied paths to prevent over-scanning --
+ search_paths = set()
+ for path in paths:
+ other_paths = paths.difference(set([path]))
+ if not osutils.is_inside_any(other_paths, path):
+ # this is a top level path, we must check it.
+ search_paths.add(path)
+ # sketch:
+ # for all search_indexs in each path at or under each element of
+ # search_paths, if the detail is relocated: add the id, and add the
+ # relocated path as one to search if its not searched already. If the
+ # detail is not relocated, add the id.
+ searched_paths = set()
+ found_ids = set()
+ def _process_entry(entry):
+ """Look at search_indexes within entry.
+
+ If a specific tree's details are relocated, add the relocation
+ target to search_paths if not searched already. If it is absent, do
+ nothing. Otherwise add the id to found_ids.
+ """
+ for index in search_indexes:
+ if entry[1][index][0] == 'relocated':
+ if not osutils.is_inside_any(searched_paths, entry[1][index][1]):
+ search_paths.add(entry[1][index][1])
+ elif entry[1][index][0] != 'absent':
+ found_ids.add(entry[0][2])
+ while search_paths:
+ current_root = search_paths.pop()
+ searched_paths.add(current_root)
+ # process the entries for this containing directory: the rest will be
+ # found by their parents recursively.
+ root_entries = _entries_for_path(current_root)
+ if not root_entries:
+ # this specified path is not present at all, skip it.
+ continue
+ for entry in root_entries:
+ _process_entry(entry)
+ initial_key = (current_root, '', '')
+ block_index, _ = state._find_block_index_from_key(initial_key)
+ while (block_index < len(state._dirblocks) and
+ osutils.is_inside(current_root, state._dirblocks[block_index][0])):
+ for entry in state._dirblocks[block_index][1]:
+ _process_entry(entry)
+ block_index += 1
+ return found_ids
+
def read_working_inventory(self):
"""Read the working inventory.
@@ -976,7 +1089,7 @@
return self._repository.get_revision(self._revision_id).parent_ids
def has_filename(self, filename):
- return bool(self.inventory.path2id(filename))
+ return bool(self.path2id(filename))
def kind(self, file_id):
return self.inventory[file_id].kind
More information about the bazaar-commits
mailing list