Rev 111: * bzr-loom requires bzr 2.2.0 (or very recent 2.2b releases) due to an API in http://bazaar.launchpad.net/~bzr-loom-devs/bzr-loom/trunk/

Robert Collins robertc at robertcollins.net
Mon Jun 14 07:59:27 BST 2010


At http://bazaar.launchpad.net/~bzr-loom-devs/bzr-loom/trunk/

------------------------------------------------------------
revno: 111
revision-id: robertc at robertcollins.net-20100614065918-pjvn2dtyu0i7dz0x
parent: robertc at robertcollins.net-20100420112812-yy07j2ogikz0s9np
committer: Robert Collins <robertc at robertcollins.net>
branch nick: trunk
timestamp: Mon 2010-06-14 18:59:18 +1200
message:
  * bzr-loom requires bzr 2.2.0 (or very recent 2.2b releases) due to an API
    change in bzr needed to fix branching and pulling of looms. On older versions
    of bzr bzr-loom will still work for most operations but will fail when making
    new branches as part of a push or branch operation. (Robert Collins, #201613)
=== modified file 'NEWS'
--- a/NEWS	2010-04-20 11:13:54 +0000
+++ b/NEWS	2010-06-14 06:59:18 +0000
@@ -10,6 +10,11 @@
 NOTES WHEN UPGRADING
 --------------------
 
+* bzr-loom requires bzr 2.2.0 (or very recent 2.2b releases) due to an API
+  change in bzr needed to fix branching and pulling of looms. On older versions
+  of bzr bzr-loom will still work for most operations but will fail when making
+  new branches as part of a push or branch operation. (Robert Collins, #201613)
+
 CHANGES
 -------
 

=== modified file 'branch.py'
--- a/branch.py	2010-04-20 11:13:54 +0000
+++ b/branch.py	2010-06-14 06:59:18 +0000
@@ -31,7 +31,7 @@
 from bzrlib.decorators import needs_read_lock, needs_write_lock
 import bzrlib.errors
 import bzrlib.osutils
-from bzrlib import symbol_versioning
+from bzrlib import remote, symbol_versioning
 import bzrlib.trace
 import bzrlib.ui
 from bzrlib.revision import is_null, NULL_REVISION
@@ -240,8 +240,10 @@
     def clone(self, to_bzrdir, revision_id=None, repository_policy=None):
         """Clone the branch into to_bzrdir.
         
-        This differs from the base clone by cloning the loom and 
-        setting the current nick to the top of the loom.
+        This differs from the base clone by cloning the loom, setting the
+        current nick to the top of the loom, not honouring any branch format
+        selection on the target bzrdir, and ensuring that the format of
+        the created branch is stacking compatible.
         """
         # If the target is a stackable repository, force-upgrade the
         # output loom format
@@ -253,79 +255,10 @@
         result = format.initialize(to_bzrdir)
         if repository_policy is not None:
             repository_policy.configure_branch(result)
-        self.copy_content_into(result, revision_id=revision_id)
+        bzrlib.branch.InterBranch.get(self, result).copy_content_into(
+            revision_id=revision_id)
         return result
 
-    @needs_read_lock
-    def copy_content_into(self, destination, revision_id=None):
-        # XXX: hint for bzrlib - break this into two routines, one for
-        # copying the last-rev pointer, one for copying parent etc.
-        destination.lock_write()
-        try:
-            source_nick = self.nick
-            state = self.get_loom_state()
-            parents = state.get_parents()
-            if parents:
-                loom_tip = parents[0]
-            else:
-                loom_tip = None
-            threads = self.get_threads(state.get_basis_revision_id())
-            if revision_id not in (None, NULL_REVISION):
-                if threads:
-                    # revision_id should be in the loom, or its an error 
-                    found_threads = [thread for thread, rev in threads
-                        if rev == revision_id]
-                    if not found_threads:
-                        # the thread we have been asked to set in the remote 
-                        # side has not been recorded yet, so its data is not
-                        # present at this point.
-                        raise UnrecordedRevision(self, revision_id)
-
-                # pull in the warp, which was skipped during the initial pull
-                # because the front end does not know what to pull.
-                # nb: this is mega huge hacky. THINK. RBC 2006062
-                nested = bzrlib.ui.ui_factory.nested_progress_bar()
-                try:
-                    if parents:
-                        destination.repository.fetch(self.repository,
-                            revision_id=parents[0])
-                    if threads:
-                        for thread, rev_id in reversed(threads):
-                            # fetch the loom content for this revision
-                            destination.repository.fetch(self.repository,
-                                revision_id=rev_id)
-                finally:
-                    nested.finished()
-            state = loom_state.LoomState()
-            try:
-                require_loom_branch(destination)
-                if threads:
-                    last_rev = threads[-1][1]
-                    if last_rev == EMPTY_REVISION:
-                        last_rev = bzrlib.revision.NULL_REVISION
-                    destination.generate_revision_history(last_rev)
-                    state.set_parents([loom_tip])
-                    state.set_threads(
-                        (thread + ([thread[1]],) for thread in threads)
-                        )
-                else:
-                    # no threads yet, be a normal branch.
-                    self._synchronize_history(destination, revision_id)
-                destination._set_last_loom(state)
-            except NotALoom:
-                self._synchronize_history(destination, revision_id)
-            try:
-                parent = self.get_parent()
-            except bzrlib.errors.InaccessibleParent, e:
-                bzrlib.trace.mutter('parent was not accessible to copy: %s', e)
-            else:
-                if parent:
-                    destination.set_parent(parent)
-            if threads:
-                destination.nick = threads[-1][0]
-        finally:
-            destination.unlock()
-
     def _get_checkout_format(self):
         """Checking out a Loom gets a regular branch for now.
         
@@ -453,24 +386,6 @@
             result.append((name, rev_id))
         return result
 
-    @needs_write_lock
-    def pull(self, source, overwrite=False, stop_revision=None,
-        run_hooks=True, possible_transports=None, _override_hook_target=None,
-        local=False):
-        """Pull from a branch into this loom.
-
-        If the remote branch is a non-loom branch, the pull is done against the
-        current warp. If it is a loom branch, then the pull is done against the
-        entire loom and the current thread set to the top thread.
-        """
-        if not isinstance(source, LoomSupport):
-            return super(LoomSupport, self).pull(source,
-                overwrite=overwrite, stop_revision=stop_revision,
-                possible_transports=possible_transports,
-                _override_hook_target=_override_hook_target, local=local)
-        return _Puller(source, self).transfer(overwrite, stop_revision,
-            run_hooks, possible_transports, _override_hook_target, local)
-
     @needs_read_lock
     def push(self, target, overwrite=False, stop_revision=None,
         _override_hook_source_branch=None):
@@ -625,10 +540,20 @@
 
 
 class _Puller(object):
+    # XXX: Move into InterLoomBranch.
 
     def __init__(self, source, target):
         self.target = target
         self.source = source
+        # If _Puller has been created, we need real branch objects.
+        self.real_target = self.unwrap_branch(target)
+        self.real_source = self.unwrap_branch(source)
+
+    def unwrap_branch(self, branch):
+        if isinstance(branch, remote.RemoteBranch):
+            branch._ensure_real()
+            return branch._real_branch
+        return branch
 
     def prepare_result(self, _override_hook_target):
         result = self.make_result()
@@ -699,7 +624,7 @@
             self.target.lock_write()
             self.source.lock_read()
             try:
-                source_state = self.source.get_loom_state()
+                source_state = self.real_source.get_loom_state()
                 source_parents = source_state.get_parents()
                 if not source_parents:
                     return self.plain_transfer(result, run_hooks,
@@ -952,6 +877,128 @@
 bzrlib.branch.BranchFormat.register_format(BzrBranchLoomFormat6())
 bzrlib.branch.BranchFormat.register_format(BzrBranchLoomFormat7())
 
+# Handle the smart server:
+
+class InterLoomBranch(bzrlib.branch.GenericInterBranch):
+
+    def unwrap_branch(self, branch):
+        if isinstance(branch, remote.RemoteBranch):
+            branch._ensure_real()
+            return branch._real_branch
+        return branch
+
+    @classmethod
+    def unwrap_format(klass, format):
+        if isinstance(format, remote.RemoteBranchFormat):
+            format._ensure_real()
+            return format._custom_format
+        return format
+
+    @classmethod
+    def is_compatible(klass, source, target):
+        source_format = klass.unwrap_format(source._format)
+        target_format = klass.unwrap_format(target._format)
+        # 1st cut: special case and handle all *->Loom and Loom->*
+        return (isinstance(source_format, LoomFormatMixin) or
+            isinstance(target_format, LoomFormatMixin))
+
+    def get_loom_state(self, branch):
+        branch = self.unwrap_branch(branch)
+        return branch.get_loom_state()
+
+    def get_threads(self, branch, revision_id):
+        branch = self.unwrap_branch(branch)
+        return branch.get_threads(revision_id)
+
+    @needs_write_lock
+    def copy_content_into(self, revision_id=None):
+        # XXX: hint for bzrlib - break this into two routines, one for
+        # copying the last-rev pointer, one for copying parent etc.
+        source_nick = self.source.nick
+        state = self.get_loom_state(self.source)
+        parents = state.get_parents()
+        if parents:
+            loom_tip = parents[0]
+        else:
+            loom_tip = None
+        threads = self.get_threads(self.source, state.get_basis_revision_id())
+        if revision_id not in (None, NULL_REVISION):
+            if threads:
+                # revision_id should be in the loom, or its an error 
+                found_threads = [thread for thread, rev in threads
+                    if rev == revision_id]
+                if not found_threads:
+                    # the thread we have been asked to set in the remote 
+                    # side has not been recorded yet, so its data is not
+                    # present at this point.
+                    raise UnrecordedRevision(self.source, revision_id)
+
+            # pull in the warp, which was skipped during the initial pull
+            # because the front end does not know what to pull.
+            # nb: this is mega huge hacky. THINK. RBC 2006062
+            nested = bzrlib.ui.ui_factory.nested_progress_bar()
+            try:
+                if parents:
+                    self.target.repository.fetch(self.source.repository,
+                        revision_id=parents[0])
+                if threads:
+                    for thread, rev_id in reversed(threads):
+                        # fetch the loom content for this revision
+                        self.target.repository.fetch(self.source.repository,
+                            revision_id=rev_id)
+            finally:
+                nested.finished()
+        state = loom_state.LoomState()
+        try:
+            require_loom_branch(self.target)
+            if threads:
+                last_rev = threads[-1][1]
+                if last_rev == EMPTY_REVISION:
+                    last_rev = bzrlib.revision.NULL_REVISION
+                self.target.generate_revision_history(last_rev)
+                state.set_parents([loom_tip])
+                state.set_threads(
+                    (thread + ([thread[1]],) for thread in threads)
+                    )
+            else:
+                # no threads yet, be a normal branch.
+                self.source._synchronize_history(self.target, revision_id)
+            self.target._set_last_loom(state)
+        except NotALoom:
+            self.source._synchronize_history(self.target, revision_id)
+        try:
+            parent = self.source.get_parent()
+        except bzrlib.errors.InaccessibleParent, e:
+            bzrlib.trace.mutter('parent was not accessible to copy: %s', e)
+        else:
+            if parent:
+                self.target.set_parent(parent)
+        if threads:
+            self.target.nick = threads[-1][0]
+
+    @needs_write_lock
+    def pull(self, overwrite=False, stop_revision=None,
+        run_hooks=True, possible_transports=None, _override_hook_target=None,
+        local=False):
+        """Pull from a branch into this loom.
+
+        If the remote branch is a non-loom branch, the pull is done against the
+        current warp. If it is a loom branch, then the pull is done against the
+        entire loom and the current thread set to the top thread.
+        """
+        # Special code only needed when target is a loom
+        target_format = self.__class__.unwrap_format(self.target._format)
+        if not isinstance(target_format, LoomFormatMixin):
+            return super(InterLoomBranch, self).pull(
+                overwrite=overwrite, stop_revision=stop_revision,
+                possible_transports=possible_transports,
+                _override_hook_target=_override_hook_target, local=local)
+        return _Puller(self.source, self.target).transfer(overwrite, stop_revision,
+            run_hooks, possible_transports, _override_hook_target, local)
+
+
+bzrlib.branch.InterBranch.register_optimiser(InterLoomBranch)
+
 
 LOOM_FORMATS = [
     BzrBranchLoomFormat1,

=== modified file 'tests/__init__.py'
--- a/tests/__init__.py	2007-11-23 01:36:56 +0000
+++ b/tests/__init__.py	2010-06-14 06:59:18 +0000
@@ -22,6 +22,7 @@
 import bzrlib.plugins.loom.branch
 from bzrlib.tests import TestCaseWithTransport
 from bzrlib.tests.TestUtil import TestLoader, TestSuite
+from bzrlib.workingtree import WorkingTree
 
 
 def test_suite():
@@ -41,7 +42,9 @@
 
     def get_tree_with_loom(self, path="."):
         """Get a tree with no commits in loom format."""
-        tree = self.make_branch_and_tree(path)
+        # May open on Remote - we want the vfs backed version for loom tests.
+        self.make_branch_and_tree(path)
+        tree = WorkingTree.open(path)
         bzrlib.plugins.loom.branch.loomify(tree.branch)
         return tree.bzrdir.open_workingtree()
 

=== modified file 'tests/test_branch.py'
--- a/tests/test_branch.py	2009-09-22 03:40:53 +0000
+++ b/tests/test_branch.py	2010-06-14 06:59:18 +0000
@@ -33,7 +33,10 @@
 from bzrlib.plugins.loom.tree import LoomTreeDecorator
 import bzrlib.revision
 from bzrlib.revision import NULL_REVISION
-from bzrlib.tests import TestCaseWithTransport
+from bzrlib.tests import (
+    TestCaseWithTransport,
+    test_server,
+    )
 from bzrlib.transport import get_transport
 from bzrlib.workingtree import WorkingTree
 
@@ -253,6 +256,9 @@
 
     def test_clone_nonempty_loom_bottom(self):
         """Cloning loom should reset the current loom pointer."""
+        self.make_and_clone_simple_loom()
+
+    def make_and_clone_simple_loom(self):
         source_tree = self.get_tree_with_one_commit('source')
         source_tree.branch.new_thread('bottom')
         source_tree.branch.new_thread('top')
@@ -260,10 +266,17 @@
         source_tree.commit('phwoar', allow_pointless=True)
         source_tree.branch.record_loom('commit to loom')
         LoomTreeDecorator(source_tree).down_thread()
-        # now clone
-        target_tree = source_tree.bzrdir.clone('target').open_workingtree()
+        # now clone from the 'default url' - transport_server rather than
+        # vfs_server.
+        source_branch = Branch.open(self.get_url('source'))
+        target_tree = source_branch.bzrdir.sprout('target').open_workingtree()
         self.assertLoomSproutedOk(source_tree, target_tree)
 
+    def test_sprout_remote_loom(self):
+        # RemoteBranch should permit sprouting properly.
+        self.transport_server = test_server.SmartTCPServer_for_testing
+        self.make_and_clone_simple_loom()
+
     def test_sprout_nonempty_loom_bottom(self):
         """Sprouting always resets the loom to the top."""
         source_tree = self.get_tree_with_one_commit('source')
@@ -356,6 +369,9 @@
         
     def test_pull_into_empty_loom(self):
         """Doing a pull into a loom with no loom revisions works."""
+        self.pull_into_empty_loom()
+
+    def pull_into_empty_loom(self):
         source = self.get_tree_with_loom('source')
         target = source.bzrdir.sprout('target').open_branch()
         source.branch.new_thread('a thread')
@@ -363,7 +379,10 @@
         # put a commit in the thread for source.
         bottom_rev1 = source.commit('commit a thread')
         source.branch.record_loom('commit to loom')
-        target.pull(source.branch)
+        # now pull from the 'default url' - transport_server rather than
+        # vfs_server - this may be a RemoteBranch.
+        source_branch = Branch.open(self.get_url('source'))
+        target.pull(source_branch)
         # check loom threads
         threads = target.get_loom_state().get_threads()
         self.assertEqual(
@@ -375,6 +394,11 @@
             self.assertTrue(target.repository.has_revision(rev_id))
         self.assertEqual(source.branch.loom_parents(), target.loom_parents())
 
+    def test_pull_remote_loom(self):
+        # RemoteBranch should permit sprouting properly.
+        self.transport_server = test_server.SmartTCPServer_for_testing
+        self.pull_into_empty_loom()
+
     def test_pull_thread_at_null(self):
         """Doing a pull when the source loom has a thread with no history."""
         source = self.get_tree_with_loom('source')




More information about the bazaar-commits mailing list