Rev 2246: Split branch pushing out of branch pulling. in file:///home/robertc/source/baz/split-branch-pull/
Robert Collins
robertc at robertcollins.net
Tue Jan 30 20:58:27 GMT 2007
------------------------------------------------------------
revno: 2246
revision-id: robertc at robertcollins.net-20070130205825-cagx4gvf17ioe2i5
parent: pqm at pqm.ubuntu.com-20070125194626-4ded330415b7276d
committer: Robert Collins <robertc at robertcollins.net>
branch nick: split-branch-pull
timestamp: Wed 2007-01-31 07:58:25 +1100
message:
Split branch pushing out of branch pulling.
added:
bzrlib/tests/branch_implementations/test_push.py test_push.py-20070130153159-fhfap8uoifevg30j-1
modified:
bzrlib/branch.py branch.py-20050309040759-e4baf4e0d046576e
bzrlib/tests/branch_implementations/__init__.py __init__.py-20060123013057-b12a52c3f361daf4
bzrlib/tests/branch_implementations/test_bound_sftp.py test_bound_sftp.py-20051231055311-2f96048c4f0940ef
bzrlib/tests/branch_implementations/test_pull.py test_pull.py-20060410103942-83c35b26657414fc
=== added file 'bzrlib/tests/branch_implementations/test_push.py'
--- a/bzrlib/tests/branch_implementations/test_push.py 1970-01-01 00:00:00 +0000
+++ b/bzrlib/tests/branch_implementations/test_push.py 2007-01-30 20:58:25 +0000
@@ -0,0 +1,79 @@
+# Copyright (C) 2004, 2005, 2007 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+"""Tests for branch.push behaviour."""
+
+import os
+
+from bzrlib.branch import Branch
+from bzrlib import errors
+from bzrlib.tests import TestCaseWithTransport
+
+
+class TestPush(TestCaseWithTransport):
+
+ def test_push_convergence_simple(self):
+ # when revisions are pushed, the left-most accessible parents must
+ # become the revision-history.
+ mine = self.make_branch_and_tree('mine')
+ mine.commit('1st post', rev_id='P1', allow_pointless=True)
+ other = mine.bzrdir.sprout('other').open_workingtree()
+ other.commit('my change', rev_id='M1', allow_pointless=True)
+ mine.merge_from_branch(other.branch)
+ mine.commit('merge my change', rev_id='P2')
+ mine.branch.push(other.branch)
+ self.assertEqual(['P1', 'P2'], other.branch.revision_history())
+
+ def test_push_merged_indirect(self):
+ # it should be possible to do a push from one branch into another
+ # when the tip of the target was merged into the source branch
+ # via a third branch - so its buried in the ancestry and is not
+ # directly accessible.
+ mine = self.make_branch_and_tree('mine')
+ mine.commit('1st post', rev_id='P1', allow_pointless=True)
+ target = mine.bzrdir.sprout('target').open_workingtree()
+ target.commit('my change', rev_id='M1', allow_pointless=True)
+ other = mine.bzrdir.sprout('other').open_workingtree()
+ other.merge_from_branch(target.branch)
+ other.commit('merge my change', rev_id='O2')
+ mine.merge_from_branch(other.branch)
+ mine.commit('merge other', rev_id='P2')
+ mine.branch.push(target.branch)
+ self.assertEqual(['P1', 'P2'], target.branch.revision_history())
+
+ def test_push_to_checkout_updates_master(self):
+ """Pushing into a checkout updates the checkout and the master branch"""
+ master_tree = self.make_branch_and_tree('master')
+ rev1 = master_tree.commit('master')
+ checkout = master_tree.branch.create_checkout('checkout')
+
+ other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
+ rev2 = other.commit('other commit')
+ # now push, which should update both checkout and master.
+ other.branch.push(checkout.branch)
+ self.assertEqual([rev1, rev2], checkout.branch.revision_history())
+ self.assertEqual([rev1, rev2], master_tree.branch.revision_history())
+
+ def test_push_raises_specific_error_on_master_connection_error(self):
+ master_tree = self.make_branch_and_tree('master')
+ checkout = master_tree.branch.create_checkout('checkout')
+ other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
+ # move the branch out of the way on disk to cause a connection
+ # error.
+ os.rename('master', 'master_gone')
+ # try to push, which should raise a BoundBranchConnectionFailure.
+ self.assertRaises(errors.BoundBranchConnectionFailure,
+ other.branch.push, checkout.branch)
=== modified file 'bzrlib/branch.py'
--- a/bzrlib/branch.py 2007-01-17 15:37:08 +0000
+++ b/bzrlib/branch.py 2007-01-30 20:58:25 +0000
@@ -375,8 +375,19 @@
return history[revno - 1]
def pull(self, source, overwrite=False, stop_revision=None):
+ """Mirror source into this branch.
+
+ This branch is considered to be 'local', having low latency.
+ """
raise NotImplementedError(self.pull)
+ def push(self, target, overwrite=False, stop_revision=None):
+ """Mirror this branch into target.
+
+ This branch is considered to be 'local', having low latency.
+ """
+ raise NotImplementedError(self.push)
+
def basis_tree(self):
"""Return `Tree` object for last revision."""
return self.repository.revision_tree(self.last_revision())
@@ -599,7 +610,7 @@
format = self.repository.bzrdir.cloning_metadir()
return format
- def create_checkout(self, to_location, revision_id=None,
+ def create_checkout(self, to_location, revision_id=None,
lightweight=False):
"""Create a checkout of a branch.
@@ -1205,6 +1216,24 @@
finally:
source.unlock()
+ @needs_read_lock
+ def push(self, target, overwrite=False, stop_revision=None):
+ """See Branch.push."""
+ target.lock_write()
+ try:
+ old_count = len(target.revision_history())
+ try:
+ target.update_revisions(self, stop_revision)
+ except DivergedBranches:
+ if not overwrite:
+ raise
+ if overwrite:
+ target.set_revision_history(self.revision_history())
+ new_count = len(target.revision_history())
+ return new_count - old_count
+ finally:
+ target.unlock()
+
def get_parent(self):
"""See Branch.get_parent."""
@@ -1283,7 +1312,7 @@
@needs_write_lock
def pull(self, source, overwrite=False, stop_revision=None):
- """Updates branch.pull to be bound branch aware."""
+ """Extends branch.pull to be bound branch aware."""
bound_location = self.get_bound_location()
if source.base != bound_location:
# not pulling from master, so we need to update master.
@@ -1293,6 +1322,22 @@
source = master_branch
return super(BzrBranch5, self).pull(source, overwrite, stop_revision)
+ @needs_write_lock
+ def push(self, target, overwrite=False, stop_revision=None):
+ """Updates branch.push to be bound branch aware."""
+ bound_location = target.get_bound_location()
+ if target.base != bound_location:
+ # not pushing to master, so we need to update master.
+ master_branch = target.get_master_branch()
+ if master_branch:
+ # push into the master from this branch.
+ super(BzrBranch5, self).push(master_branch, overwrite,
+ stop_revision)
+ # and push into the target branch from this. Note that we push from
+ # this branch again, because its considered the highest bandwidth
+ # repository.
+ return super(BzrBranch5, self).push(target, overwrite, stop_revision)
+
def get_bound_location(self):
try:
return self.control_files.get_utf8('bound').read()[:-1]
=== modified file 'bzrlib/tests/branch_implementations/__init__.py'
--- a/bzrlib/tests/branch_implementations/__init__.py 2006-10-11 23:08:27 +0000
+++ b/bzrlib/tests/branch_implementations/__init__.py 2007-01-30 20:58:25 +0000
@@ -47,6 +47,7 @@
'bzrlib.tests.branch_implementations.test_parent',
'bzrlib.tests.branch_implementations.test_permissions',
'bzrlib.tests.branch_implementations.test_pull',
+ 'bzrlib.tests.branch_implementations.test_push',
'bzrlib.tests.branch_implementations.test_update',
]
adapter = BranchTestProviderAdapter(
=== modified file 'bzrlib/tests/branch_implementations/test_bound_sftp.py'
--- a/bzrlib/tests/branch_implementations/test_bound_sftp.py 2007-01-05 17:12:03 +0000
+++ b/bzrlib/tests/branch_implementations/test_bound_sftp.py 2007-01-30 20:58:25 +0000
@@ -184,18 +184,6 @@
self.assertEqual(['r at b-1'], wt_child.branch.revision_history())
self.assertEqual(['r at b-1'], sftp_b_newbase.revision_history())
- def test_pull_updates_both(self):
- b_base, wt_child = self.create_branches()
-
- wt_newchild = b_base.bzrdir.sprout('newchild').open_workingtree()
- open('newchild/b', 'wb').write('newchild b contents\n')
- wt_newchild.commit('newchild', rev_id='r at d-2')
- self.assertEqual(['r at b-1', 'r at d-2'], wt_newchild.branch.revision_history())
-
- wt_child.pull(wt_newchild.branch)
- self.assertEqual(['r at b-1', 'r at d-2'], wt_child.branch.revision_history())
- self.assertEqual(['r at b-1', 'r at d-2'], b_base.revision_history())
-
def test_bind_diverged(self):
from bzrlib.builtins import merge
@@ -333,25 +321,6 @@
self.assertRaises(errors.BoundBranchConnectionFailure,
wt_child.commit, 'added text', rev_id='r at c-2')
- def test_pull_fails(self):
- b_base, wt_child = self.create_branches()
-
- wt_other = wt_child.bzrdir.sprout('other').open_workingtree()
- open('other/a', 'wb').write('new contents\n')
- wt_other.commit('changed a', rev_id='r at d-2')
-
- self.assertEqual(['r at b-1'], b_base.revision_history())
- self.assertEqual(['r at b-1'], wt_child.branch.revision_history())
- self.assertEqual(['r at b-1', 'r at d-2'], wt_other.branch.revision_history())
-
- # this deletes the branch from memory
- del b_base
- # and this moves it out of the way on disk
- os.rename('base', 'hidden_base')
-
- self.assertRaises(errors.BoundBranchConnectionFailure,
- wt_child.pull, wt_other.branch)
-
# TODO: jam 20051231 We need invasive failure tests, so that we can show
# performance even when something fails.
=== modified file 'bzrlib/tests/branch_implementations/test_pull.py'
--- a/bzrlib/tests/branch_implementations/test_pull.py 2006-10-11 23:08:27 +0000
+++ b/bzrlib/tests/branch_implementations/test_pull.py 2007-01-30 20:58:25 +0000
@@ -19,7 +19,7 @@
import os
from bzrlib.branch import Branch
-from bzrlib.osutils import abspath, realpath
+from bzrlib import errors
from bzrlib.tests import TestCaseWithTransport
@@ -53,3 +53,27 @@
parent.commit('merge other', rev_id='P2')
mine.pull(parent.branch)
self.assertEqual(['P1', 'P2'], mine.branch.revision_history())
+
+ def test_pull_updates_checkout_and_master(self):
+ """Pulling into a checkout updates the checkout and the master branch"""
+ master_tree = self.make_branch_and_tree('master')
+ rev1 = master_tree.commit('master')
+ checkout = master_tree.branch.create_checkout('checkout')
+
+ other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
+ rev2 = other.commit('other commit')
+ # now pull, which should update both checkout and master.
+ checkout.branch.pull(other.branch)
+ self.assertEqual([rev1, rev2], checkout.branch.revision_history())
+ self.assertEqual([rev1, rev2], master_tree.branch.revision_history())
+
+ def test_pull_raises_specific_error_on_master_connection_error(self):
+ master_tree = self.make_branch_and_tree('master')
+ checkout = master_tree.branch.create_checkout('checkout')
+ other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
+ # move the branch out of the way on disk to cause a connection
+ # error.
+ os.rename('master', 'master_gone')
+ # try to pull, which should raise a BoundBranchConnectionFailure.
+ self.assertRaises(errors.BoundBranchConnectionFailure,
+ checkout.branch.pull, other.branch)
More information about the bazaar-commits
mailing list