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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
"""Tests for branch.push behaviour."""
19
from cStringIO import StringIO
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):
35
from bzrlib.smart import (
38
from bzrlib.tests import (
44
class TestPush(per_branch.TestCaseWithBranch):
33
46
def test_push_convergence_simple(self):
34
# when revisions are pushed, the left-most accessible parents must
47
# when revisions are pushed, the left-most accessible parents must
35
48
# become the revision-history.
36
49
mine = self.make_branch_and_tree('mine')
37
50
mine.commit('1st post', rev_id='P1', allow_pointless=True)
40
53
mine.merge_from_branch(other.branch)
41
54
mine.commit('merge my change', rev_id='P2')
42
55
result = mine.branch.push(other.branch)
43
self.assertEqual(['P1', 'P2'], other.branch.revision_history())
56
self.assertEqual('P2', other.branch.last_revision())
44
57
# result object contains some structured data
45
58
self.assertEqual(result.old_revid, 'M1')
46
59
self.assertEqual(result.new_revid, 'P2')
47
# and it can be treated as an integer for compatibility
48
self.assertEqual(int(result), 0)
50
61
def test_push_merged_indirect(self):
51
62
# it should be possible to do a push from one branch into another
98
109
self.assertRaises(errors.BoundBranchConnectionFailure,
99
110
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())
101
129
def test_push_uses_read_lock(self):
102
130
"""Push should only need a read lock on the source side."""
103
131
source = self.make_branch_and_tree('source')
153
184
self.assertEqual(tree.branch.last_revision(),
154
185
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())
156
202
def test_push_overwrite_of_non_tip_with_stop_revision(self):
157
203
"""Combining the stop_revision and overwrite options works.
159
205
This was <https://bugs.launchpad.net/bzr/+bug/234229>.
161
207
source = self.make_branch_and_tree('source')
169
215
source.branch.push(target, stop_revision='rev-2', overwrite=True)
170
216
self.assertEqual('rev-2', target.last_revision())
173
class TestPushHook(TestCaseWithBranch):
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):
176
302
self.hook_calls = []
177
TestCaseWithBranch.setUp(self)
303
super(TestPushHook, self).setUp()
179
305
def capture_post_push_hook(self, result):
180
306
"""Capture post push hook calls to self.hook_calls.
182
308
The call is logged, as is some state of the two branches.
184
310
if result.local_branch:
198
324
def test_post_push_empty_history(self):
199
325
target = self.make_branch('target')
200
326
source = self.make_branch('source')
201
Branch.hooks.install_named_hook('post_push',
202
self.capture_post_push_hook, None)
327
branch.Branch.hooks.install_named_hook(
328
'post_push', self.capture_post_push_hook, None)
203
329
source.push(target)
204
330
# with nothing there we should still get a notification, and
205
331
# have both branches locked at the notification time.
206
332
self.assertEqual([
207
('post_push', source, None, target.base, 0, NULL_REVISION,
208
0, NULL_REVISION, True, None, True)
333
('post_push', source, None, target.base, 0, revision.NULL_REVISION,
334
0, revision.NULL_REVISION, True, None, True)
212
338
def test_post_push_bound_branch(self):
213
339
# pushing to a bound branch should pass in the master branch to the
214
340
# hook, allowing the correct number of emails to be sent, while still
215
# allowing hooks that want to modify the target to do so to both
341
# allowing hooks that want to modify the target to do so to both
217
343
target = self.make_branch('target')
218
344
local = self.make_branch('local')
224
350
# remotebranches can't be bound. Let's instead make a new local
225
351
# branch of the default type, which does allow binding.
226
352
# See https://bugs.launchpad.net/bzr/+bug/112020
227
local = BzrDir.create_branch_convenience('local2')
353
local = controldir.ControlDir.create_branch_convenience('local2')
228
354
local.bind(target)
229
355
source = self.make_branch('source')
230
Branch.hooks.install_named_hook('post_push',
231
self.capture_post_push_hook, None)
356
branch.Branch.hooks.install_named_hook(
357
'post_push', self.capture_post_push_hook, None)
232
358
source.push(local)
233
359
# with nothing there we should still get a notification, and
234
360
# have both branches locked at the notification time.
235
361
self.assertEqual([
236
('post_push', source, local.base, target.base, 0, NULL_REVISION,
237
0, NULL_REVISION, True, True, True)
362
('post_push', source, local.base, target.base, 0,
363
revision.NULL_REVISION, 0, revision.NULL_REVISION,
257
384
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)