13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
"""Tests for branch.push behaviour."""
19
from cStringIO import StringIO
35
from bzrlib.smart import (
38
from bzrlib.tests import (
44
class TestPush(per_branch.TestCaseWithBranch):
21
from bzrlib import bzrdir, errors
22
from bzrlib.branch import Branch
23
from bzrlib.bzrdir import BzrDir
24
from bzrlib.memorytree import MemoryTree
25
from bzrlib.remote import RemoteBranch
26
from bzrlib.revision import NULL_REVISION
27
from bzrlib.tests.branch_implementations.test_branch import TestCaseWithBranch
28
from bzrlib.transport.local import LocalURLServer
31
class TestPush(TestCaseWithBranch):
46
33
def test_push_convergence_simple(self):
47
# when revisions are pushed, the left-most accessible parents must
34
# when revisions are pushed, the left-most accessible parents must
48
35
# become the revision-history.
49
36
mine = self.make_branch_and_tree('mine')
50
37
mine.commit('1st post', rev_id='P1', allow_pointless=True)
53
40
mine.merge_from_branch(other.branch)
54
41
mine.commit('merge my change', rev_id='P2')
55
42
result = mine.branch.push(other.branch)
56
self.assertEqual('P2', other.branch.last_revision())
43
self.assertEqual(['P1', 'P2'], other.branch.revision_history())
57
44
# result object contains some structured data
58
45
self.assertEqual(result.old_revid, 'M1')
59
46
self.assertEqual(result.new_revid, 'P2')
47
# and it can be treated as an integer for compatibility
48
self.assertEqual(int(result), 0)
61
50
def test_push_merged_indirect(self):
62
51
# it should be possible to do a push from one branch into another
109
98
self.assertRaises(errors.BoundBranchConnectionFailure,
110
99
other.branch.push, checkout.branch)
112
def test_push_new_tag_to_bound_branch(self):
113
master = self.make_branch('master')
114
bound = self.make_branch('bound')
117
except errors.UpgradeRequired:
118
raise tests.TestNotApplicable(
119
'Format does not support bound branches')
120
other = bound.bzrdir.sprout('other').open_branch()
122
other.tags.set_tag('new-tag', 'some-rev')
123
except errors.TagsNotSupported:
124
raise tests.TestNotApplicable('Format does not support tags')
126
self.assertEqual({'new-tag': 'some-rev'}, bound.tags.get_tag_dict())
127
self.assertEqual({'new-tag': 'some-rev'}, master.tags.get_tag_dict())
129
101
def test_push_uses_read_lock(self):
130
102
"""Push should only need a read lock on the source side."""
131
103
source = self.make_branch_and_tree('source')
184
153
self.assertEqual(tree.branch.last_revision(),
185
154
to_branch.last_revision())
187
def test_push_overwrite_with_older_mainline_rev(self):
188
"""Pushing an older mainline revision with overwrite.
190
This was <https://bugs.launchpad.net/bzr/+bug/386576>.
192
source = self.make_branch_and_tree('source')
193
target = self.make_branch('target')
195
source.commit('1st commit')
196
source.commit('2nd commit', rev_id='rev-2')
197
source.commit('3rd commit')
198
source.branch.push(target)
199
source.branch.push(target, stop_revision='rev-2', overwrite=True)
200
self.assertEqual('rev-2', target.last_revision())
202
156
def test_push_overwrite_of_non_tip_with_stop_revision(self):
203
157
"""Combining the stop_revision and overwrite options works.
205
159
This was <https://bugs.launchpad.net/bzr/+bug/234229>.
207
161
source = self.make_branch_and_tree('source')
215
169
source.branch.push(target, stop_revision='rev-2', overwrite=True)
216
170
self.assertEqual('rev-2', target.last_revision())
218
def test_push_repository_no_branch_doesnt_fetch_all_revs(self):
219
# See https://bugs.launchpad.net/bzr/+bug/465517
220
t = self.get_transport('target')
222
bzrdir = self.bzrdir_format.initialize_on_transport(t)
225
except errors.NotBranchError:
228
raise tests.TestNotApplicable('older formats can\'t have a repo'
231
source = self.make_branch_builder('source',
232
format=self.bzrdir_format)
233
except errors.UninitializableFormat:
234
raise tests.TestNotApplicable('cannot initialize this format')
235
source.start_series()
236
source.build_snapshot('A', None, [
237
('add', ('', 'root-id', 'directory', None))])
238
source.build_snapshot('B', ['A'], [])
239
source.build_snapshot('C', ['A'], [])
240
source.finish_series()
241
b = source.get_branch()
242
# Note: We can't read lock the source branch. Some formats take a write
243
# lock to 'set_push_location', which breaks
244
self.addCleanup(b.lock_write().unlock)
245
repo = bzrdir.create_repository()
246
# This means 'push the source branch into this dir'
247
bzrdir.push_branch(b)
248
self.addCleanup(repo.lock_read().unlock)
249
# We should have pushed 'C', but not 'B', since it isn't in the
251
self.assertEqual(['A', 'C'], sorted(repo.all_revision_ids()))
253
def test_push_with_default_stacking_does_not_create_broken_branch(self):
254
"""Pushing a new standalone branch works even when there's a default
255
stacking policy at the destination.
257
The new branch will preserve the repo format (even if it isn't the
258
default for the branch), and will be stacked when the repo format
259
allows (which means that the branch format isn't necessarly preserved).
261
if self.bzrdir_format.fixed_components:
262
raise tests.TestNotApplicable('Not a metadir format.')
263
if isinstance(self.branch_format, branch.BranchReferenceFormat):
264
# This test could in principle apply to BranchReferenceFormat, but
265
# make_branch_builder doesn't support it.
266
raise tests.TestSkipped(
267
"BranchBuilder can't make reference branches.")
268
# Make a branch called "local" in a stackable repository
269
# The branch has 3 revisions:
270
# - rev-1, adds a file
271
# - rev-2, no changes
272
# - rev-3, modifies the file.
273
repo = self.make_repository('repo', shared=True, format='1.6')
274
builder = self.make_branch_builder('repo/local')
275
builder.start_series()
276
builder.build_snapshot('rev-1', None, [
277
('add', ('', 'root-id', 'directory', '')),
278
('add', ('filename', 'f-id', 'file', 'content\n'))])
279
builder.build_snapshot('rev-2', ['rev-1'], [])
280
builder.build_snapshot('rev-3', ['rev-2'],
281
[('modify', ('f-id', 'new-content\n'))])
282
builder.finish_series()
283
trunk = builder.get_branch()
284
# Sprout rev-1 to "trunk", so that we can stack on it.
285
trunk.bzrdir.sprout(self.get_url('trunk'), revision_id='rev-1')
286
# Set a default stacking policy so that new branches will automatically
288
self.make_bzrdir('.').get_config().set_default_stack_on('trunk')
289
# Push rev-2 to a new branch "remote". It will be stacked on "trunk".
291
push._show_push_branch(trunk, 'rev-2', self.get_url('remote'), output)
292
# Push rev-3 onto "remote". If "remote" not stacked and is missing the
293
# fulltext record for f-id @ rev-1, then this will fail.
294
remote_branch = branch.Branch.open(self.get_url('remote'))
295
trunk.push(remote_branch)
296
check.check_dwim(remote_branch.base, False, True, True)
299
class TestPushHook(per_branch.TestCaseWithBranch):
173
class TestPushHook(TestCaseWithBranch):
302
176
self.hook_calls = []
303
super(TestPushHook, self).setUp()
177
TestCaseWithBranch.setUp(self)
305
179
def capture_post_push_hook(self, result):
306
180
"""Capture post push hook calls to self.hook_calls.
308
182
The call is logged, as is some state of the two branches.
310
184
if result.local_branch:
324
198
def test_post_push_empty_history(self):
325
199
target = self.make_branch('target')
326
200
source = self.make_branch('source')
327
branch.Branch.hooks.install_named_hook(
328
'post_push', self.capture_post_push_hook, None)
201
Branch.hooks.install_named_hook('post_push',
202
self.capture_post_push_hook, None)
329
203
source.push(target)
330
204
# with nothing there we should still get a notification, and
331
205
# have both branches locked at the notification time.
332
206
self.assertEqual([
333
('post_push', source, None, target.base, 0, revision.NULL_REVISION,
334
0, revision.NULL_REVISION, True, None, True)
207
('post_push', source, None, target.base, 0, NULL_REVISION,
208
0, NULL_REVISION, True, None, True)
338
212
def test_post_push_bound_branch(self):
339
213
# pushing to a bound branch should pass in the master branch to the
340
214
# hook, allowing the correct number of emails to be sent, while still
341
# allowing hooks that want to modify the target to do so to both
215
# allowing hooks that want to modify the target to do so to both
343
217
target = self.make_branch('target')
344
218
local = self.make_branch('local')
350
224
# remotebranches can't be bound. Let's instead make a new local
351
225
# branch of the default type, which does allow binding.
352
226
# See https://bugs.launchpad.net/bzr/+bug/112020
353
local = controldir.ControlDir.create_branch_convenience('local2')
227
local = BzrDir.create_branch_convenience('local2')
354
228
local.bind(target)
355
229
source = self.make_branch('source')
356
branch.Branch.hooks.install_named_hook(
357
'post_push', self.capture_post_push_hook, None)
230
Branch.hooks.install_named_hook('post_push',
231
self.capture_post_push_hook, None)
358
232
source.push(local)
359
233
# with nothing there we should still get a notification, and
360
234
# have both branches locked at the notification time.
361
235
self.assertEqual([
362
('post_push', source, local.base, target.base, 0,
363
revision.NULL_REVISION, 0, revision.NULL_REVISION,
236
('post_push', source, local.base, target.base, 0, NULL_REVISION,
237
0, NULL_REVISION, True, True, True)
384
257
2, rev2, True, None, True)
389
class EmptyPushSmartEffortTests(per_branch.TestCaseWithBranch):
390
"""Tests that a push of 0 revisions should make a limited number of smart
395
# Skip some scenarios that don't apply to these tests.
396
if (self.transport_server is not None
397
and issubclass(self.transport_server,
398
test_server.SmartTCPServer_for_testing)):
399
raise tests.TestNotApplicable(
400
'Does not apply when remote backing branch is also '
402
if not self.branch_format.supports_leaving_lock():
403
raise tests.TestNotApplicable(
404
'Branch format is not usable via HPSS.')
405
super(EmptyPushSmartEffortTests, self).setUp()
406
# Create a smart server that publishes whatever the backing VFS server
408
self.smart_server = test_server.SmartTCPServer_for_testing()
409
self.start_server(self.smart_server, self.get_server())
410
# Make two empty branches, 'empty' and 'target'.
411
self.empty_branch = self.make_branch('empty')
412
self.make_branch('target')
413
# Log all HPSS calls into self.hpss_calls.
414
client._SmartClient.hooks.install_named_hook(
415
'call', self.capture_hpss_call, None)
418
def capture_hpss_call(self, params):
419
self.hpss_calls.append(params.method)
421
def test_empty_branch_api(self):
422
"""The branch_obj.push API should make a limited number of HPSS calls.
424
t = transport.get_transport_from_url(self.smart_server.get_url()).clone('target')
425
target = branch.Branch.open_from_transport(t)
426
self.empty_branch.push(target)
429
'BzrDir.open_branchV3',
430
'BzrDir.find_repositoryV3',
431
'Branch.get_stacked_on_url',
433
'Branch.last_revision_info',
437
def test_empty_branch_command(self):
438
"""The 'bzr push' command should make a limited number of HPSS calls.
440
cmd = builtins.cmd_push()
441
cmd.outf = tests.StringIOWrapper()
443
directory=self.get_url('empty'),
444
location=self.smart_server.get_url() + 'target')
445
# HPSS calls as of 2008/09/22:
446
# [BzrDir.open, BzrDir.open_branch, BzrDir.find_repositoryV2,
447
# Branch.get_stacked_on_url, get, get, Branch.lock_write,
448
# Branch.last_revision_info, Branch.unlock]
449
self.assertTrue(len(self.hpss_calls) <= 9, self.hpss_calls)
452
class TestLossyPush(per_branch.TestCaseWithBranch):
456
super(TestLossyPush, self).setUp()
458
def test_lossy_push_raises_same_vcs(self):
459
target = self.make_branch('target')
460
source = self.make_branch('source')
461
self.assertRaises(errors.LossyPushToSameVCS, source.push, target, lossy=True)