Rev 2372: Add an InterDirStateTree InterTree optimiser. in sftp://bazaar.launchpad.net/%7Ebzr/bzr/dirstate/

Robert Collins robertc at robertcollins.net
Fri Feb 23 01:11:49 GMT 2007


At sftp://bazaar.launchpad.net/%7Ebzr/bzr/dirstate/

------------------------------------------------------------
revno: 2372
revision-id: robertc at robertcollins.net-20070223011050-5psttlyujfn5b5em
parent: john at arbash-meinel.com-20070222232615-46n3rjdpfrsbmu3b
committer: Robert Collins <robertc at robertcollins.net>
branch nick: dirstate
timestamp: Fri 2007-02-23 12:10:50 +1100
message:
  Add an InterDirStateTree InterTree optimiser.
modified:
  bzrlib/tests/test_workingtree.py testworkingtree.py-20051004024258-b88d0fe8f101d468
  bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
=== modified file 'bzrlib/tests/test_workingtree.py'
--- a/bzrlib/tests/test_workingtree.py	2007-02-16 06:57:53 +0000
+++ b/bzrlib/tests/test_workingtree.py	2007-02-23 01:10:50 +0000
@@ -21,7 +21,7 @@
 from bzrlib import dirstate, ignores
 import bzrlib
 from bzrlib.branch import Branch
-from bzrlib import bzrdir, conflicts, errors, workingtree
+from bzrlib import bzrdir, conflicts, errors, workingtree, workingtree_4
 from bzrlib.bzrdir import BzrDir
 from bzrlib.errors import NotBranchError, NotVersionedError
 from bzrlib.lockdir import LockDir
@@ -31,6 +31,7 @@
 from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
 from bzrlib.trace import mutter
 from bzrlib.transport import get_transport
+from bzrlib.tree import InterTree
 from bzrlib.workingtree import (
     TreeEntry,
     TreeDirectory,
@@ -233,7 +234,7 @@
         control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
         control.create_repository()
         control.create_branch()
-        tree = workingtree.WorkingTreeFormat4().initialize(control)
+        tree = workingtree_4.WorkingTreeFormat4().initialize(control)
         # we want:
         # format 'Bazaar Working Tree format 4'
         # stat-cache = ??
@@ -271,13 +272,15 @@
         tree.unlock()
         self.assertEquals(our_lock.peek(), None)
 
-    def make_workingtree(self):
-        url = self.get_url()
+    def make_workingtree(self, relpath=''):
+        url = self.get_url(relpath)
+        if relpath:
+            self.build_tree([relpath + '/'])
         dir = bzrdir.BzrDirMetaFormat1().initialize(url)
         repo = dir.create_repository()
         branch = dir.create_branch()
         try:
-            return workingtree.WorkingTreeFormat4().initialize(dir)
+            return workingtree_4.WorkingTreeFormat4().initialize(dir)
         except errors.NotLocalUrl:
             raise TestSkipped('Not a local URL')
 
@@ -310,41 +313,48 @@
         self.build_tree(['subdir/file-a',])
         subtree.add(['file-a'], ['id-a'])
         rev1 = subtree.commit('commit in subdir')
-        rev1_tree = subtree.basis_tree()
-        rev1_tree.lock_read()
-        self.addCleanup(rev1_tree.unlock)
 
         subtree2 = subtree.bzrdir.sprout('subdir2').open_workingtree()
         self.build_tree(['subdir2/file-b'])
         subtree2.add(['file-b'], ['id-b'])
         rev2 = subtree2.commit('commit in subdir2')
-        rev2_tree = subtree2.basis_tree()
-        rev2_tree.lock_read()
-        self.addCleanup(rev2_tree.unlock)
 
+        subtree.flush()
         subtree.merge_from_branch(subtree2.branch)
         rev3 = subtree.commit('merge from subdir2')
-        rev3_tree = subtree.basis_tree()
-        rev3_tree.lock_read()
-        self.addCleanup(rev3_tree.unlock)
 
         repo = tree.branch.repository
         repo.fetch(subtree.branch.repository, rev3)
         # will also pull the others...
 
+        # create repository based revision trees
+        rev1_revtree = subtree.branch.repository.revision_tree(rev1)
+        rev2_revtree = subtree2.branch.repository.revision_tree(rev2)
+        rev3_revtree = subtree.branch.repository.revision_tree(rev3)
         # tree doesn't contain a text merge yet but we'll just
         # set the parents as if a merge had taken place. 
         # this should cause the tree data to be folded into the 
         # dirstate.
         tree.set_parent_trees([
-            (rev1, rev1_tree),
-            (rev2, rev2_tree),
-            (rev3, rev3_tree), ])
+            (rev1, rev1_revtree),
+            (rev2, rev2_revtree),
+            (rev3, rev3_revtree), ])
+
+        # create tree-sourced revision trees
+        rev1_tree = tree.revision_tree(rev1)
+        rev1_tree.lock_read()
+        self.addCleanup(rev1_tree.unlock)
+        rev2_tree = tree.revision_tree(rev2)
+        rev2_tree.lock_read()
+        self.addCleanup(rev2_tree.unlock)
+        rev3_tree = tree.revision_tree(rev3)
+        rev3_tree.lock_read()
+        self.addCleanup(rev3_tree.unlock)
 
         # now we should be able to get them back out
-        self.assertTreesEqual(tree.revision_tree(rev1), rev1_tree)
-        self.assertTreesEqual(tree.revision_tree(rev2), rev2_tree)
-        self.assertTreesEqual(tree.revision_tree(rev3), rev3_tree)
+        self.assertTreesEqual(rev1_revtree, rev1_tree)
+        self.assertTreesEqual(rev2_revtree, rev2_tree)
+        self.assertTreesEqual(rev3_revtree, rev3_tree)
 
     def test_dirstate_doesnt_read_parents_from_repo_when_setting(self):
         """Setting parent trees on a dirstate working tree takes
@@ -387,10 +397,12 @@
         rev1 = subtree.commit('commit in subdir')
         rev1_tree = subtree.basis_tree()
         rev1_tree.lock_read()
+        rev1_tree.inventory
         self.addCleanup(rev1_tree.unlock)
         rev2 = subtree.commit('second commit in subdir', allow_pointless=True)
         rev2_tree = subtree.basis_tree()
         rev2_tree.lock_read()
+        rev2_tree.inventory
         self.addCleanup(rev2_tree.unlock)
 
         tree.branch.pull(subtree.branch)
@@ -467,6 +479,128 @@
         lock_and_compare_all_current_dirstate(tree, 'lock_write')
         lock_and_compare_all_current_dirstate(tree, 'lock_write')
 
+    def test_revtree_to_revtree_not_interdirstate(self):
+        # we should not get a dirstate optimiser for two repository sourced
+        # revtrees. we can't prove a negative, so we dont do exhaustive tests
+        # of all formats; though that could be written in the future it doesn't
+        # seem well worth it.
+        tree = self.make_workingtree()
+        rev_id = tree.commit('first post')
+        rev_id2 = tree.commit('second post')
+        rev_tree = tree.branch.repository.revision_tree(rev_id)
+        rev_tree2 = tree.branch.repository.revision_tree(rev_id2)
+        optimiser = InterTree.get(rev_tree, rev_tree2)
+        self.assertIsInstance(optimiser, InterTree)
+        self.assertFalse(isinstance(optimiser, workingtree_4.InterDirStateTree))
+        optimiser = InterTree.get(rev_tree2, rev_tree)
+        self.assertIsInstance(optimiser, InterTree)
+        self.assertFalse(isinstance(optimiser, workingtree_4.InterDirStateTree))
+
+    def test_revtree_not_in_dirstate_to_dirstate_not_interdirstate(self):
+        # we should not get a dirstate optimiser when the revision id for of
+        # the source is not in the dirstate of the target.
+        tree = self.make_workingtree()
+        rev_id = tree.commit('first post')
+        rev_id2 = tree.commit('second post')
+        rev_tree = tree.branch.repository.revision_tree(rev_id)
+        tree.lock_read()
+        optimiser = InterTree.get(rev_tree, tree)
+        self.assertIsInstance(optimiser, InterTree)
+        self.assertFalse(isinstance(optimiser, workingtree_4.InterDirStateTree))
+        optimiser = InterTree.get(tree, rev_tree)
+        self.assertIsInstance(optimiser, InterTree)
+        self.assertFalse(isinstance(optimiser, workingtree_4.InterDirStateTree))
+        tree.unlock()
+
+    def test_empty_basis_to_dirstate_tree(self):
+        # we should get a InterDirStateTree for doing
+        # 'changes_from' from the first basis dirstate revision tree to a
+        # WorkingTree4.
+        tree = self.make_workingtree()
+        tree.lock_read()
+        basis_tree = tree.basis_tree()
+        basis_tree.lock_read()
+        optimiser = InterTree.get(basis_tree, tree)
+        tree.unlock()
+        basis_tree.unlock()
+        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
+
+    def test_nonempty_basis_to_dirstate_tree(self):
+        # we should get a InterDirStateTree for doing
+        # 'changes_from' from a non-null basis dirstate revision tree to a
+        # WorkingTree4.
+        tree = self.make_workingtree()
+        tree.commit('first post')
+        tree.lock_read()
+        basis_tree = tree.basis_tree()
+        basis_tree.lock_read()
+        optimiser = InterTree.get(basis_tree, tree)
+        tree.unlock()
+        basis_tree.unlock()
+        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
+
+    def test_empty_basis_revtree_to_dirstate_tree(self):
+        # we should get a InterDirStateTree for doing
+        # 'changes_from' from an empty repository based rev tree to a
+        # WorkingTree4.
+        tree = self.make_workingtree()
+        tree.lock_read()
+        basis_tree = tree.branch.repository.revision_tree(tree.last_revision())
+        basis_tree.lock_read()
+        optimiser = InterTree.get(basis_tree, tree)
+        tree.unlock()
+        basis_tree.unlock()
+        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
+
+    def test_nonempty_basis_revtree_to_dirstate_tree(self):
+        # we should get a InterDirStateTree for doing
+        # 'changes_from' from a non-null repository based rev tree to a
+        # WorkingTree4.
+        tree = self.make_workingtree()
+        tree.commit('first post')
+        tree.lock_read()
+        basis_tree = tree.branch.repository.revision_tree(tree.last_revision())
+        basis_tree.lock_read()
+        optimiser = InterTree.get(basis_tree, tree)
+        tree.unlock()
+        basis_tree.unlock()
+        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
+
+    def test_tree_to_basis_in_other_tree(self):
+        # we should get a InterDirStateTree when
+        # the source revid is in the dirstate object of the target and
+        # the dirstates are different. This is largely covered by testing
+        # with repository revtrees, so is just for extra confidence.
+        tree = self.make_workingtree('a')
+        tree.commit('first post')
+        tree2 = self.make_workingtree('b')
+        tree2.pull(tree.branch)
+        basis_tree = tree.basis_tree()
+        tree2.lock_read()
+        basis_tree.lock_read()
+        optimiser = InterTree.get(basis_tree, tree2)
+        tree2.unlock()
+        basis_tree.unlock()
+        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
+
+    def test_merged_revtree_to_tree(self):
+        # we should get a InterDirStateTree when
+        # the source tree is a merged tree present in the dirstate of target.
+        tree = self.make_workingtree('a')
+        tree.commit('first post')
+        tree.commit('tree 1 commit 2')
+        tree2 = self.make_workingtree('b')
+        tree2.pull(tree.branch)
+        tree2.commit('tree 2 commit 2')
+        tree.merge_from_branch(tree2.branch)
+        second_parent_tree = tree.revision_tree(tree.get_parent_ids()[1])
+        second_parent_tree.lock_read()
+        tree.lock_read()
+        optimiser = InterTree.get(second_parent_tree, tree)
+        tree.unlock()
+        second_parent_tree.unlock()
+        self.assertIsInstance(optimiser, workingtree_4.InterDirStateTree)
+
 
 class TestFormat2WorkingTree(TestCaseWithTransport):
     """Tests that are specific to format 2 trees."""

=== modified file 'bzrlib/workingtree_4.py'
--- a/bzrlib/workingtree_4.py	2007-02-22 23:26:15 +0000
+++ b/bzrlib/workingtree_4.py	2007-02-23 01:10:50 +0000
@@ -50,6 +50,7 @@
     ignores,
     merge,
     osutils,
+    revisiontree,
     textui,
     transform,
     urlutils,
@@ -82,6 +83,7 @@
     )
 from bzrlib.trace import mutter, note
 from bzrlib.transport.local import LocalTransport
+from bzrlib.tree import InterTree
 from bzrlib.progress import DummyProgress, ProgressPhase
 from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
 from bzrlib.rio import RioReader, rio_file, Stanza
@@ -1200,3 +1202,36 @@
             for dir in reversed(dirblock):
                 if dir[2] == _directory:
                     pending.append((dir[0], dir[4]))
+
+
+class InterDirStateTree(InterTree):
+    """Fast path optimiser for changes_from with dirstate trees."""
+
+    @staticmethod
+    def revision_tree_from_workingtree(tree):
+        """Create a revision tree from a working tree."""
+        revid = tree.commit('save tree', allow_pointless=True)
+        return tree.branch.repository.revision_tree(revid)
+    _from_tree_converter = revision_tree_from_workingtree
+    _matching_from_tree_format = WorkingTreeFormat4()
+    _matching_to_tree_format = WorkingTreeFormat4()
+    _to_tree_converter = staticmethod(lambda x: x)
+
+    @staticmethod
+    def is_compatible(source, target):
+        # the target must be a dirstate working tree
+        if not isinstance(target, WorkingTree4):
+            return False
+        # the source must be a revtreee or dirstate rev tree.
+        if not isinstance(source,
+            (revisiontree.RevisionTree, DirStateRevisionTree)):
+            return False
+        # the source revid must be in the target dirstate
+        if not (source._revision_id == NULL_REVISION or
+            source._revision_id in target.get_parent_ids()):
+            # TODO: what about ghosts? it may well need to 
+            # check for them explicitly.
+            return False
+        return True
+
+InterTree.register_optimiser(InterDirStateTree)



More information about the bazaar-commits mailing list