Rev 3093: switch for heavyweight checkouts in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Fri Dec 7 06:46:59 GMT 2007
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 3093
revision-id:pqm at pqm.ubuntu.com-20071207064646-1nif1u2vsep2vqud
parent: pqm at pqm.ubuntu.com-20071207051359-iyupti9xt33jccin
parent: ian.clatworthy at internode.on.net-20071207055345-ek2m118pm79nowuf
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Fri 2007-12-07 06:46:46 +0000
message:
switch for heavyweight checkouts
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/branch.py branch.py-20050309040759-e4baf4e0d046576e
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/graph.py graph_walker.py-20070525030359-y852guab65d4wtn0-1
bzrlib/switch.py switch.py-20071116011000-v5lnw7d2wkng9eux-1
bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
bzrlib/tests/test_switch.py test_switch.py-20071116011000-v5lnw7d2wkng9eux-2
------------------------------------------------------------
revno: 3092.1.1
revision-id:ian.clatworthy at internode.on.net-20071207055345-ek2m118pm79nowuf
parent: pqm at pqm.ubuntu.com-20071207051359-iyupti9xt33jccin
parent: ian.clatworthy at internode.on.net-20071207054814-u8dzhvxnksy1dwj2
committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
branch nick: ianc-integration
timestamp: Fri 2007-12-07 15:53:45 +1000
message:
switch for heavyweight checkouts
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/branch.py branch.py-20050309040759-e4baf4e0d046576e
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/graph.py graph_walker.py-20070525030359-y852guab65d4wtn0-1
bzrlib/switch.py switch.py-20071116011000-v5lnw7d2wkng9eux-1
bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
bzrlib/tests/test_switch.py test_switch.py-20071116011000-v5lnw7d2wkng9eux-2
------------------------------------------------------------
revno: 3078.2.8
revision-id:ian.clatworthy at internode.on.net-20071207054814-u8dzhvxnksy1dwj2
parent: ian.clatworthy at internode.on.net-20071207054651-p34puo2num38ig0j
committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
branch nick: bzr.switch
timestamp: Fri 2007-12-07 15:48:14 +1000
message:
tweak NEWS
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
------------------------------------------------------------
revno: 3078.2.7
revision-id:ian.clatworthy at internode.on.net-20071207054651-p34puo2num38ig0j
parent: ian.clatworthy at internode.on.net-20071207053154-k9tmyczcf8niwonm
committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
branch nick: bzr.switch
timestamp: Fri 2007-12-07 15:46:51 +1000
message:
added smoke test for set_reference
modified:
bzrlib/branch.py branch.py-20050309040759-e4baf4e0d046576e
bzrlib/tests/branch_implementations/test_branch.py testbranch.py-20050711070244-121d632bc37d7253
------------------------------------------------------------
revno: 3078.2.6
revision-id:ian.clatworthy at internode.on.net-20071207053154-k9tmyczcf8niwonm
parent: ian.clatworthy at internode.on.net-20071207045158-y91slghnuoa7klfd
committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
branch nick: bzr.switch
timestamp: Fri 2007-12-07 15:31:54 +1000
message:
fix efficiency of local commit detection as recommended by jameinel's review
modified:
bzrlib/graph.py graph_walker.py-20070525030359-y852guab65d4wtn0-1
bzrlib/switch.py switch.py-20071116011000-v5lnw7d2wkng9eux-1
bzrlib/tests/test_switch.py test_switch.py-20071116011000-v5lnw7d2wkng9eux-2
------------------------------------------------------------
revno: 3078.2.5
revision-id:ian.clatworthy at internode.on.net-20071207045158-y91slghnuoa7klfd
parent: ian.clatworthy at internode.on.net-20071205125451-oe6p3gekbfhyxuzt
committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
branch nick: bzr.switch
timestamp: Fri 2007-12-07 14:51:58 +1000
message:
make switch fail without --force if branch missing
modified:
bzrlib/switch.py switch.py-20071116011000-v5lnw7d2wkng9eux-1
bzrlib/tests/test_switch.py test_switch.py-20071116011000-v5lnw7d2wkng9eux-2
------------------------------------------------------------
revno: 3078.2.4
revision-id:ian.clatworthy at internode.on.net-20071205125451-oe6p3gekbfhyxuzt
parent: ian.clatworthy at internode.on.net-20071205125414-pmgapgkgx7vpi33y
committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
branch nick: bzr.switch
timestamp: Wed 2007-12-05 22:54:51 +1000
message:
Add test for local commits handling
modified:
bzrlib/tests/test_switch.py test_switch.py-20071116011000-v5lnw7d2wkng9eux-2
------------------------------------------------------------
revno: 3078.2.3
revision-id:ian.clatworthy at internode.on.net-20071205125414-pmgapgkgx7vpi33y
parent: ian.clatworthy at internode.on.net-20071205074743-gsf610r9r0dhjmzs
committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
branch nick: bzr.switch
timestamp: Wed 2007-12-05 22:54:14 +1000
message:
Add NEWS
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
------------------------------------------------------------
revno: 3078.2.2
revision-id:ian.clatworthy at internode.on.net-20071205074743-gsf610r9r0dhjmzs
parent: ian.clatworthy at internode.on.net-20071205064946-snjfrx883fc49osl
committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
branch nick: bzr.switch
timestamp: Wed 2007-12-05 17:47:43 +1000
message:
get switch tests passing on heavyweight checkouts
modified:
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/switch.py switch.py-20071116011000-v5lnw7d2wkng9eux-1
bzrlib/tests/test_switch.py test_switch.py-20071116011000-v5lnw7d2wkng9eux-2
------------------------------------------------------------
revno: 3078.2.1
revision-id:ian.clatworthy at internode.on.net-20071205064946-snjfrx883fc49osl
parent: pqm at pqm.ubuntu.com-20071205035041-vjo05rrhyrqqmgxf
committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
branch nick: bzr.switch
timestamp: Wed 2007-12-05 16:49:46 +1000
message:
Refactor switch to support heavyweight checkouts
modified:
bzrlib/branch.py branch.py-20050309040759-e4baf4e0d046576e
bzrlib/switch.py switch.py-20071116011000-v5lnw7d2wkng9eux-1
=== modified file 'NEWS'
--- a/NEWS 2007-12-07 01:45:23 +0000
+++ b/NEWS 2007-12-07 05:53:45 +0000
@@ -29,6 +29,16 @@
the catalog, provided in pdf and png format and updated to refer
to ``send`` instead of ``bundle``. (Ian Clatworthy, #165080)
+ * ``switch`` can now be used on heavyweight checkouts as well as
+ lightweight ones. After switching a heavyweight checkout, the
+ local branch is a mirror/cache of the new bound branch and
+ uncommitted changes in the working tree are merged. As a safety
+ check, if there are local commits in a checkout which have not
+ been committed to the previously bound branch, then ``switch``
+ fails unless the ``--force`` option is given. This option is
+ now also required if the branch a lightweight checkout is pointing
+ to has been moved. (Ian Clatworthy)
+
INTERNALS:
* New -Dhttp debug option reports http connections, requests and responses.
=== modified file 'bzrlib/branch.py'
--- a/bzrlib/branch.py 2007-12-03 22:21:35 +0000
+++ b/bzrlib/branch.py 2007-12-07 05:46:51 +0000
@@ -868,6 +868,19 @@
"""
return None
+ @classmethod
+ def set_reference(self, a_bzrdir, to_branch):
+ """Set the target reference of the branch in a_bzrdir.
+
+ format probing must have been completed before calling
+ this method - it is assumed that the format of the branch
+ in a_bzrdir is correct.
+
+ :param a_bzrdir: The bzrdir to set the branch reference for.
+ :param to_branch: branch that the checkout is to reference
+ """
+ raise NotImplementedError(self.set_reference)
+
def get_format_string(self):
"""Return the ASCII format string that identifies this format."""
raise NotImplementedError(self.get_format_string)
@@ -1198,6 +1211,11 @@
transport = a_bzrdir.get_branch_transport(None)
return transport.get('location').read()
+ def set_reference(self, a_bzrdir, to_branch):
+ """See BranchFormat.set_reference()."""
+ transport = a_bzrdir.get_branch_transport(None)
+ location = transport.put_bytes('location', to_branch.base)
+
def initialize(self, a_bzrdir, target_branch=None):
"""Create a branch of this format in a_bzrdir."""
if target_branch is None:
@@ -1860,6 +1878,19 @@
return None
@classmethod
+ def set_reference(self, a_bzrdir, to_branch):
+ """Set the target reference of the branch in a_bzrdir.
+
+ format probing must have been completed before calling
+ this method - it is assumed that the format of the branch
+ in a_bzrdir is correct.
+
+ :param a_bzrdir: The bzrdir to set the branch reference for.
+ :param to_branch: branch that the checkout is to reference
+ """
+ raise NotImplementedError(self.set_reference)
+
+ @classmethod
def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
lock_class):
branch_transport = a_bzrdir.get_branch_transport(cls)
=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py 2007-12-06 12:03:35 +0000
+++ b/bzrlib/builtins.py 2007-12-07 05:53:45 +0000
@@ -4353,16 +4353,30 @@
class cmd_switch(Command):
- """Set the branch of a lightweight checkout and update."""
+ """Set the branch of a checkout and update.
+
+ For lightweight checkouts, this changes the branch being referenced.
+ For heavyweight checkouts, this checks that there are no local commits
+ versus the current bound branch, then it makes the local branch a mirror
+ of the new location and binds to it.
+
+ In both cases, the working tree is updated and uncommitted changes
+ are merged. The user can commit or revert these as they desire.
+
+ Pending merges need to be committed or reverted before using switch.
+ """
takes_args = ['to_location']
+ takes_options = [Option('force',
+ help='Switch even if local commits will be lost.')
+ ]
- def run(self, to_location):
+ def run(self, to_location, force=False):
from bzrlib import switch
to_branch = Branch.open(to_location)
tree_location = '.'
control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
- switch.switch(control_dir, to_branch)
+ switch.switch(control_dir, to_branch, force)
note('Switched to branch: %s',
urlutils.unescape_for_display(to_branch.base, 'utf-8'))
=== modified file 'bzrlib/graph.py'
--- a/bzrlib/graph.py 2007-12-04 00:34:34 +0000
+++ b/bzrlib/graph.py 2007-12-07 05:31:54 +0000
@@ -361,7 +361,7 @@
"""Determine whether a revision is an ancestor of another.
We answer this using heads() as heads() has the logic to perform the
- smallest number of parent looksup to determine the ancestral
+ smallest number of parent lookups to determine the ancestral
relationship between N revisions.
"""
return set([candidate_descendant]) == self.heads(
=== modified file 'bzrlib/switch.py'
--- a/bzrlib/switch.py 2007-11-23 03:33:17 +0000
+++ b/bzrlib/switch.py 2007-12-07 05:31:54 +0000
@@ -16,59 +16,42 @@
# Original author: David Allouche
-from bzrlib import errors, merge
+from bzrlib import errors, merge, revision
from bzrlib.branch import Branch, BranchFormat, BranchReferenceFormat
from bzrlib.bzrdir import BzrDir
from bzrlib.trace import note
-def switch(control_dir, to_branch):
+def switch(control_dir, to_branch, force=False):
"""Switch the branch associated with a checkout.
:param control_dir: BzrDir of the checkout to change
:param to_branch: branch that the checkout is to reference
+ :param force: skip the check for local commits in a heavy checkout
"""
- _check_switch_branch_format(control_dir)
- _check_pending_merges(control_dir)
+ _check_pending_merges(control_dir, force)
try:
source_repository = control_dir.open_branch().repository
except errors.NotBranchError:
source_repository = to_branch.repository
- _set_branch_location(control_dir, to_branch)
+ _set_branch_location(control_dir, to_branch, force)
tree = control_dir.open_workingtree()
_update(tree, source_repository)
-def _check_switch_branch_format(control):
- """Check that the branch format supports the switch operation.
-
- Note: Only lightweight checkouts are currently supported.
- This may change in the future though.
-
- :param control: BzrDir of the branch to check
- """
- branch_format = BranchFormat.find_format(control)
- format_string = branch_format.get_format_string()
- if not format_string.startswith("Bazaar-NG Branch Reference Format "):
- raise errors.BzrCommandError(
- 'The switch command can only be used on a lightweight checkout.\n'
- 'Expected branch reference, found %s at %s' % (
- format_string.strip(), control.root_transport.base))
- if not format_string == BranchReferenceFormat().get_format_string():
- raise errors.BzrCommandError(
- 'Unsupported: %r' % (format_string.strip(),))
-
-
-def _check_pending_merges(control):
+def _check_pending_merges(control, force=False):
"""Check that there are no outstanding pending merges before switching.
:param control: BzrDir of the branch to check
"""
try:
tree = control.open_workingtree()
- except errors.NotBranchError:
- # old branch is gone
- return
+ except errors.NotBranchError, ex:
+ # Lightweight checkout and branch is no longer there
+ if force:
+ return
+ else:
+ raise ex
# XXX: Should the tree be locked for get_parent_ids?
existing_pending_merges = tree.get_parent_ids()[1:]
if len(existing_pending_merges) > 0:
@@ -76,14 +59,57 @@
'committed or reverted before using switch.')
-def _set_branch_location(control, to_branch):
+def _set_branch_location(control, to_branch, force=False):
"""Set location value of a branch reference.
:param control: BzrDir of the checkout to change
:param to_branch: branch that the checkout is to reference
+ :param force: skip the check for local commits in a heavy checkout
"""
- transport = control.get_branch_transport(None)
- location = transport.put_bytes('location', to_branch.base)
+ branch_format = control.find_branch_format()
+ if branch_format.get_reference(control) is not None:
+ # Lightweight checkout: update the branch reference
+ branch_format.set_reference(control, to_branch)
+ else:
+ b = control.open_branch()
+ bound_branch = b.get_bound_location()
+ if bound_branch is not None:
+ # Heavyweight checkout: check all local commits
+ # have been pushed to the current bound branch then
+ # synchronise the local branch with the new remote branch
+ # and bind to it
+ possible_transports = []
+ if not force and _any_local_commits(b, possible_transports):
+ raise errors.BzrCommandError(
+ 'Cannot switch as local commits found in the checkout. '
+ 'Commit these to the bound branch or use --force to '
+ 'throw them away.')
+ b.set_bound_location(None)
+ b.pull(to_branch, overwrite=True,
+ possible_transports=possible_transports)
+ b.set_bound_location(to_branch.base)
+ else:
+ raise errors.BzrCommandError('Cannot switch a branch, '
+ 'only a checkout.')
+
+
+def _any_local_commits(this_branch, possible_transports):
+ """Does this branch have any commits not in the master branch?"""
+ last_rev = revision.ensure_null(this_branch.last_revision())
+ if last_rev != revision.NULL_REVISION:
+ other_branch = this_branch.get_master_branch(possible_transports)
+ this_branch.lock_read()
+ other_branch.lock_read()
+ try:
+ other_last_rev = other_branch.last_revision()
+ graph = this_branch.repository.get_graph(
+ other_branch.repository)
+ if not graph.is_ancestor(last_rev, other_last_rev):
+ return True
+ finally:
+ other_branch.unlock()
+ this_branch.unlock()
+ return False
def _update(tree, source_repository):
=== modified file 'bzrlib/tests/branch_implementations/test_branch.py'
--- a/bzrlib/tests/branch_implementations/test_branch.py 2007-11-26 01:31:22 +0000
+++ b/bzrlib/tests/branch_implementations/test_branch.py 2007-12-07 05:46:51 +0000
@@ -577,6 +577,24 @@
self.assertEqual(None,
made_branch._format.get_reference(made_branch.bzrdir))
+ def test_set_reference(self):
+ """set_reference on all regular branches should be callable."""
+ if not self.branch_format.is_supported():
+ # unsupported formats are not loopback testable
+ # because the default open will not open them and
+ # they may not be initializable.
+ return
+ this_branch = self.make_branch('this')
+ other_branch = self.make_branch('other')
+ try:
+ this_branch._format.set_reference(this_branch.bzrdir, other_branch)
+ except NotImplementedError:
+ # that's ok
+ pass
+ else:
+ ref = this_branch._format.get_reference(this_branch.bzrdir)
+ self.assertEqual(ref, other_branch.base)
+
def test_format_initialize_find_open(self):
# loopback test to check the current format initializes to itself.
if not self.branch_format.is_supported():
=== modified file 'bzrlib/tests/test_switch.py'
--- a/bzrlib/tests/test_switch.py 2007-11-30 00:34:19 +0000
+++ b/bzrlib/tests/test_switch.py 2007-12-07 05:31:54 +0000
@@ -24,6 +24,10 @@
class TestSwitch(tests.TestCaseWithTransport):
+ def setUp(self):
+ super(TestSwitch, self).setUp()
+ self.lightweight = True
+
def _setup_tree(self):
tree = self.make_branch_and_tree('branch-1')
self.build_tree(['branch-1/file-1'])
@@ -39,7 +43,8 @@
tree.add('file-2')
tree.remove('file-1')
tree.commit('rev2')
- checkout = tree.branch.create_checkout('checkout', lightweight=True)
+ checkout = tree.branch.create_checkout('checkout',
+ lightweight=self.lightweight)
self.build_tree(['checkout/file-3'])
checkout.add('file-3')
self.failIfExists('checkout/file-1')
@@ -52,7 +57,8 @@
def test_switch_after_branch_moved(self):
"""Test switch after the branch is moved."""
tree = self._setup_tree()
- checkout = tree.branch.create_checkout('checkout', lightweight=True)
+ checkout = tree.branch.create_checkout('checkout',
+ lightweight=self.lightweight)
self.build_tree(['branch-1/file-2'])
tree.add('file-2')
tree.remove('file-1')
@@ -63,27 +69,22 @@
# rename the branch on disk, the checkout object is now invalid.
os.rename('branch-1', 'branch-2')
to_branch = branch.Branch.open('branch-2')
- switch.switch(checkout.bzrdir, to_branch)
+ # Check fails without --force
+ err = self.assertRaises((errors.NotBranchError,
+ errors.BoundBranchConnectionFailure),
+ switch.switch, checkout.bzrdir, to_branch)
+ switch.switch(checkout.bzrdir, to_branch, force=True)
self.failIfExists('checkout/file-1')
self.failUnlessExists('checkout/file-2')
self.failUnlessExists('checkout/file-3')
- def test_switch_on_heavy_checkout(self):
- """Test graceful failure on heavyweight checkouts."""
- tree = self._setup_tree()
- checkout = tree.branch.create_checkout('checkout-1', lightweight=False)
- branch2 = self.make_branch('branch-2')
- err = self.assertRaises(errors.BzrCommandError,
- switch.switch, checkout.bzrdir, branch2)
- self.assertContainsRe(str(err),
- "The switch command can only be used on a lightweight checkout")
-
def test_switch_when_pending_merges(self):
"""Test graceful failure if pending merges are outstanding."""
# Create 2 branches and a checkout
tree = self._setup_tree()
tree2 = tree.bzrdir.sprout('branch-2').open_workingtree()
- checkout = tree.branch.create_checkout('checkout', lightweight=True)
+ checkout = tree.branch.create_checkout('checkout',
+ lightweight=self.lightweight)
# Change tree2 and merge it into the checkout without committing
self.build_tree(['branch-2/file-2'])
tree2.add('file-2')
@@ -94,3 +95,42 @@
switch.switch, checkout.bzrdir, tree2.branch)
self.assertContainsRe(str(err),
"Pending merges must be committed or reverted before using switch")
+
+
+class TestSwitchHeavyweight(TestSwitch):
+
+ def setUp(self):
+ super(TestSwitchHeavyweight, self).setUp()
+ self.lightweight = False
+
+ def test_switch_with_local_commits(self):
+ """Test switch complains about local commits unless --force given."""
+ tree = self._setup_tree()
+ to_branch = tree.bzrdir.sprout('branch-2').open_branch()
+ self.build_tree(['branch-1/file-2'])
+ tree.add('file-2')
+ tree.remove('file-1')
+ tree.commit('rev2')
+ checkout = tree.branch.create_checkout('checkout')
+ self.build_tree(['checkout/file-3'])
+ checkout.add('file-3')
+ checkout.commit(message='local only commit', local=True)
+ self.build_tree(['checkout/file-4'])
+ # Check the error reporting is as expected
+ err = self.assertRaises(errors.BzrCommandError,
+ switch.switch, checkout.bzrdir, to_branch)
+ self.assertContainsRe(str(err),
+ 'Cannot switch as local commits found in the checkout.')
+ # Check all is ok when force is given
+ self.failIfExists('checkout/file-1')
+ self.failUnlessExists('checkout/file-2')
+ switch.switch(checkout.bzrdir, to_branch, force=True)
+ self.failUnlessExists('checkout/file-1')
+ self.failIfExists('checkout/file-2')
+ self.failIfExists('checkout/file-3')
+ self.failUnlessExists('checkout/file-4')
+ # Check that the checkout is a true mirror of the bound branch
+ missing_in_checkout = checkout.branch.missing_revisions(to_branch)
+ self.assertEqual([], missing_in_checkout)
+ missing_in_remote = to_branch.missing_revisions(checkout.branch)
+ self.assertEqual([], missing_in_remote)
More information about the bazaar-commits
mailing list