1
# Copyright (C) 2006-2012 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
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
18
"""Black-box tests for bzr push."""
34
from bzrlib.repofmt import knitrepo
35
from bzrlib.tests import (
41
from bzrlib.tests.matchers import ContainsNoVfsCalls
42
from bzrlib.transport import memory
45
load_tests = scenarios.load_tests_apply_scenarios
48
class TestPush(tests.TestCaseWithTransport):
50
def test_push_error_on_vfs_http(self):
51
""" pushing a branch to a HTTP server fails cleanly. """
52
# the trunk is published on a web server
53
self.transport_readonly_server = http_server.HttpServer
54
self.make_branch('source')
55
public_url = self.get_readonly_url('target')
56
self.run_bzr_error(['http does not support mkdir'],
60
def test_push_suggests_parent_alias(self):
61
"""Push suggests using :parent if there is a known parent branch."""
62
tree_a = self.make_branch_and_tree('a')
63
tree_a.commit('this is a commit')
64
tree_b = self.make_branch_and_tree('b')
66
# If there is no parent location set, :parent isn't mentioned.
67
out = self.run_bzr('push', working_dir='a', retcode=3)
68
self.assertEquals(out,
69
('','bzr: ERROR: No push location known or specified.\n'))
71
# If there is a parent location set, the error suggests :parent.
72
tree_a.branch.set_parent(tree_b.branch.base)
73
out = self.run_bzr('push', working_dir='a', retcode=3)
74
self.assertEquals(out,
75
('','bzr: ERROR: No push location known or specified. '
76
'To push to the parent branch '
77
'(at %s), use \'bzr push :parent\'.\n' %
78
urlutils.unescape_for_display(tree_b.branch.base, 'utf-8')))
80
def test_push_remember(self):
81
"""Push changes from one branch to another and test push location."""
82
transport = self.get_transport()
83
tree_a = self.make_branch_and_tree('branch_a')
84
branch_a = tree_a.branch
85
self.build_tree(['branch_a/a'])
87
tree_a.commit('commit a')
88
tree_b = branch_a.bzrdir.sprout('branch_b').open_workingtree()
89
branch_b = tree_b.branch
90
tree_c = branch_a.bzrdir.sprout('branch_c').open_workingtree()
91
branch_c = tree_c.branch
92
self.build_tree(['branch_a/b'])
94
tree_a.commit('commit b')
95
self.build_tree(['branch_b/c'])
97
tree_b.commit('commit c')
98
# initial push location must be empty
99
self.assertEqual(None, branch_b.get_push_location())
101
# test push for failure without push location set
102
out = self.run_bzr('push', working_dir='branch_a', retcode=3)
103
self.assertEquals(out,
104
('','bzr: ERROR: No push location known or specified.\n'))
106
# test not remembered if cannot actually push
107
self.run_bzr('push path/which/doesnt/exist',
108
working_dir='branch_a', retcode=3)
109
out = self.run_bzr('push', working_dir='branch_a', retcode=3)
111
('', 'bzr: ERROR: No push location known or specified.\n'),
114
# test implicit --remember when no push location set, push fails
115
out = self.run_bzr('push ../branch_b',
116
working_dir='branch_a', retcode=3)
117
self.assertEquals(out,
118
('','bzr: ERROR: These branches have diverged. '
119
'See "bzr help diverged-branches" for more information.\n'))
120
# Refresh the branch as 'push' modified it
121
branch_a = branch_a.bzrdir.open_branch()
122
self.assertEquals(osutils.abspath(branch_a.get_push_location()),
123
osutils.abspath(branch_b.bzrdir.root_transport.base))
125
# test implicit --remember after resolving previous failure
126
uncommit.uncommit(branch=branch_b, tree=tree_b)
127
transport.delete('branch_b/c')
128
out, err = self.run_bzr('push', working_dir='branch_a')
129
# Refresh the branch as 'push' modified it
130
branch_a = branch_a.bzrdir.open_branch()
131
path = branch_a.get_push_location()
132
self.assertEqual(err,
133
'Using saved push location: %s\n'
134
'All changes applied successfully.\n'
135
'Pushed up to revision 2.\n'
136
% urlutils.local_path_from_url(path))
137
self.assertEqual(path,
138
branch_b.bzrdir.root_transport.base)
139
# test explicit --remember
140
self.run_bzr('push ../branch_c --remember', working_dir='branch_a')
141
# Refresh the branch as 'push' modified it
142
branch_a = branch_a.bzrdir.open_branch()
143
self.assertEquals(branch_a.get_push_location(),
144
branch_c.bzrdir.root_transport.base)
146
def test_push_without_tree(self):
147
# bzr push from a branch that does not have a checkout should work.
148
b = self.make_branch('.')
149
out, err = self.run_bzr('push pushed-location')
150
self.assertEqual('', out)
151
self.assertEqual('Created new branch.\n', err)
152
b2 = branch.Branch.open('pushed-location')
153
self.assertEndsWith(b2.base, 'pushed-location/')
155
def test_push_no_tree(self):
156
# bzr push --no-tree of a branch with working trees
157
b = self.make_branch_and_tree('push-from')
158
self.build_tree(['push-from/file'])
161
out, err = self.run_bzr('push --no-tree -d push-from push-to')
162
self.assertEqual('', out)
163
self.assertEqual('Created new branch.\n', err)
164
self.assertPathDoesNotExist('push-to/file')
166
def test_push_new_branch_revision_count(self):
167
# bzr push of a branch with revisions to a new location
168
# should print the number of revisions equal to the length of the
170
t = self.make_branch_and_tree('tree')
171
self.build_tree(['tree/file'])
174
out, err = self.run_bzr('push -d tree pushed-to')
175
self.assertEqual('', out)
176
self.assertEqual('Created new branch.\n', err)
178
def test_push_quiet(self):
179
# test that using -q makes output quiet
180
t = self.make_branch_and_tree('tree')
181
self.build_tree(['tree/file'])
184
self.run_bzr('push -d tree pushed-to')
185
# Refresh the branch as 'push' modified it and get the push location
186
push_loc = t.branch.bzrdir.open_branch().get_push_location()
187
out, err = self.run_bzr('push', working_dir="tree")
188
self.assertEqual('Using saved push location: %s\n'
189
'No new revisions or tags to push.\n' %
190
urlutils.local_path_from_url(push_loc), err)
191
out, err = self.run_bzr('push -q', working_dir="tree")
192
self.assertEqual('', out)
193
self.assertEqual('', err)
195
def test_push_only_pushes_history(self):
196
# Knit branches should only push the history for the current revision.
197
format = bzrdir.BzrDirMetaFormat1()
198
format.repository_format = knitrepo.RepositoryFormatKnit1()
199
shared_repo = self.make_repository('repo', format=format, shared=True)
200
shared_repo.set_make_working_trees(True)
202
def make_shared_tree(path):
203
shared_repo.bzrdir.root_transport.mkdir(path)
204
controldir.ControlDir.create_branch_convenience('repo/' + path)
205
return workingtree.WorkingTree.open('repo/' + path)
206
tree_a = make_shared_tree('a')
207
self.build_tree(['repo/a/file'])
209
tree_a.commit('commit a-1', rev_id='a-1')
210
f = open('repo/a/file', 'ab')
211
f.write('more stuff\n')
213
tree_a.commit('commit a-2', rev_id='a-2')
215
tree_b = make_shared_tree('b')
216
self.build_tree(['repo/b/file'])
218
tree_b.commit('commit b-1', rev_id='b-1')
220
self.assertTrue(shared_repo.has_revision('a-1'))
221
self.assertTrue(shared_repo.has_revision('a-2'))
222
self.assertTrue(shared_repo.has_revision('b-1'))
224
# Now that we have a repository with shared files, make sure
225
# that things aren't copied out by a 'push'
226
self.run_bzr('push ../../push-b', working_dir='repo/b')
227
pushed_tree = workingtree.WorkingTree.open('push-b')
228
pushed_repo = pushed_tree.branch.repository
229
self.assertFalse(pushed_repo.has_revision('a-1'))
230
self.assertFalse(pushed_repo.has_revision('a-2'))
231
self.assertTrue(pushed_repo.has_revision('b-1'))
233
def test_push_funky_id(self):
234
t = self.make_branch_and_tree('tree')
235
self.build_tree(['tree/filename'])
236
t.add('filename', 'funky-chars<>%&;"\'')
237
t.commit('commit filename')
238
self.run_bzr('push -d tree new-tree')
240
def test_push_dash_d(self):
241
t = self.make_branch_and_tree('from')
242
t.commit(allow_pointless=True,
243
message='first commit')
244
self.run_bzr('push -d from to-one')
245
self.assertPathExists('to-one')
246
self.run_bzr('push -d %s %s'
247
% tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
248
self.assertPathExists('to-two')
250
def test_push_repository_no_branch_doesnt_fetch_all_revs(self):
251
# See https://bugs.launchpad.net/bzr/+bug/465517
252
target_repo = self.make_repository('target')
253
source = self.make_branch_builder('source')
254
source.start_series()
255
source.build_snapshot('A', None, [
256
('add', ('', 'root-id', 'directory', None))])
257
source.build_snapshot('B', ['A'], [])
258
source.build_snapshot('C', ['A'], [])
259
source.finish_series()
260
self.run_bzr('push target -d source')
261
self.addCleanup(target_repo.lock_read().unlock)
262
# We should have pushed 'C', but not 'B', since it isn't in the
264
self.assertEqual([('A',), ('C',)], sorted(target_repo.revisions.keys()))
266
def test_push_smart_non_stacked_streaming_acceptance(self):
267
self.setup_smart_server_with_call_log()
268
t = self.make_branch_and_tree('from')
269
t.commit(allow_pointless=True, message='first commit')
270
self.reset_smart_call_log()
271
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
272
# This figure represent the amount of work to perform this use case. It
273
# is entirely ok to reduce this number if a test fails due to rpc_count
274
# being too low. If rpc_count increases, more network roundtrips have
275
# become necessary for this use case. Please do not adjust this number
276
# upwards without agreement from bzr's network support maintainers.
277
self.assertLength(9, self.hpss_calls)
278
self.assertLength(1, self.hpss_connections)
279
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
281
def test_push_smart_stacked_streaming_acceptance(self):
282
self.setup_smart_server_with_call_log()
283
parent = self.make_branch_and_tree('parent', format='1.9')
284
parent.commit(message='first commit')
285
local = parent.bzrdir.sprout('local').open_workingtree()
286
local.commit(message='local commit')
287
self.reset_smart_call_log()
288
self.run_bzr(['push', '--stacked', '--stacked-on', '../parent',
289
self.get_url('public')], working_dir='local')
290
# This figure represent the amount of work to perform this use case. It
291
# is entirely ok to reduce this number if a test fails due to rpc_count
292
# being too low. If rpc_count increases, more network roundtrips have
293
# become necessary for this use case. Please do not adjust this number
294
# upwards without agreement from bzr's network support maintainers.
295
self.assertLength(15, self.hpss_calls)
296
self.assertLength(1, self.hpss_connections)
297
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
298
remote = branch.Branch.open('public')
299
self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
301
def test_push_smart_tags_streaming_acceptance(self):
302
self.setup_smart_server_with_call_log()
303
t = self.make_branch_and_tree('from')
304
rev_id = t.commit(allow_pointless=True, message='first commit')
305
t.branch.tags.set_tag('new-tag', rev_id)
306
self.reset_smart_call_log()
307
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
308
# This figure represent the amount of work to perform this use case. It
309
# is entirely ok to reduce this number if a test fails due to rpc_count
310
# being too low. If rpc_count increases, more network roundtrips have
311
# become necessary for this use case. Please do not adjust this number
312
# upwards without agreement from bzr's network support maintainers.
313
self.assertLength(11, self.hpss_calls)
314
self.assertLength(1, self.hpss_connections)
315
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
317
def test_push_smart_incremental_acceptance(self):
318
self.setup_smart_server_with_call_log()
319
t = self.make_branch_and_tree('from')
320
rev_id1 = t.commit(allow_pointless=True, message='first commit')
321
rev_id2 = t.commit(allow_pointless=True, message='second commit')
323
['push', self.get_url('to-one'), '-r1'], working_dir='from')
324
self.reset_smart_call_log()
325
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
326
# This figure represent the amount of work to perform this use case. It
327
# is entirely ok to reduce this number if a test fails due to rpc_count
328
# being too low. If rpc_count increases, more network roundtrips have
329
# become necessary for this use case. Please do not adjust this number
330
# upwards without agreement from bzr's network support maintainers.
331
self.assertLength(11, self.hpss_calls)
332
self.assertLength(1, self.hpss_connections)
333
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
335
def test_push_smart_with_default_stacking_url_path_segment(self):
336
# If the default stacked-on location is a path element then branches
337
# we push there over the smart server are stacked and their
338
# stacked_on_url is that exact path segment. Added to nail bug 385132.
339
self.setup_smart_server_with_call_log()
340
self.make_branch('stack-on', format='1.9')
341
self.make_bzrdir('.').get_config().set_default_stack_on(
343
self.make_branch('from', format='1.9')
344
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
345
b = branch.Branch.open(self.get_url('to'))
346
self.assertEqual('/extra/stack-on', b.get_stacked_on_url())
348
def test_push_smart_with_default_stacking_relative_path(self):
349
# If the default stacked-on location is a relative path then branches
350
# we push there over the smart server are stacked and their
351
# stacked_on_url is a relative path. Added to nail bug 385132.
352
self.setup_smart_server_with_call_log()
353
self.make_branch('stack-on', format='1.9')
354
self.make_bzrdir('.').get_config().set_default_stack_on('stack-on')
355
self.make_branch('from', format='1.9')
356
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
357
b = branch.Branch.open(self.get_url('to'))
358
self.assertEqual('../stack-on', b.get_stacked_on_url())
360
def create_simple_tree(self):
361
tree = self.make_branch_and_tree('tree')
362
self.build_tree(['tree/a'])
363
tree.add(['a'], ['a-id'])
364
tree.commit('one', rev_id='r1')
367
def test_push_create_prefix(self):
368
"""'bzr push --create-prefix' will create leading directories."""
369
tree = self.create_simple_tree()
371
self.run_bzr_error(['Parent directory of ../new/tree does not exist'],
374
self.run_bzr('push ../new/tree --create-prefix',
376
new_tree = workingtree.WorkingTree.open('new/tree')
377
self.assertEqual(tree.last_revision(), new_tree.last_revision())
378
self.assertPathExists('new/tree/a')
380
def test_push_use_existing(self):
381
"""'bzr push --use-existing-dir' can push into an existing dir.
383
By default, 'bzr push' will not use an existing, non-versioned dir.
385
tree = self.create_simple_tree()
386
self.build_tree(['target/'])
388
self.run_bzr_error(['Target directory ../target already exists',
389
'Supply --use-existing-dir',
391
'push ../target', working_dir='tree')
393
self.run_bzr('push --use-existing-dir ../target',
396
new_tree = workingtree.WorkingTree.open('target')
397
self.assertEqual(tree.last_revision(), new_tree.last_revision())
398
# The push should have created target/a
399
self.assertPathExists('target/a')
401
def test_push_use_existing_into_empty_bzrdir(self):
402
"""'bzr push --use-existing-dir' into a dir with an empty .bzr dir
405
tree = self.create_simple_tree()
406
self.build_tree(['target/', 'target/.bzr/'])
408
['Target directory ../target already contains a .bzr directory, '
409
'but it is not valid.'],
410
'push ../target --use-existing-dir', working_dir='tree')
412
def test_push_onto_repo(self):
413
"""We should be able to 'bzr push' into an existing bzrdir."""
414
tree = self.create_simple_tree()
415
repo = self.make_repository('repo', shared=True)
417
self.run_bzr('push ../repo',
420
# Pushing onto an existing bzrdir will create a repository and
421
# branch as needed, but will only create a working tree if there was
423
self.assertRaises(errors.NoWorkingTree,
424
workingtree.WorkingTree.open, 'repo')
425
new_branch = branch.Branch.open('repo')
426
self.assertEqual(tree.last_revision(), new_branch.last_revision())
428
def test_push_onto_just_bzrdir(self):
429
"""We don't handle when the target is just a bzrdir.
431
Because you shouldn't be able to create *just* a bzrdir in the wild.
433
# TODO: jam 20070109 Maybe it would be better to create the repository
435
tree = self.create_simple_tree()
436
a_bzrdir = self.make_bzrdir('dir')
438
self.run_bzr_error(['At ../dir you have a valid .bzr control'],
442
def test_push_with_revisionspec(self):
443
"""We should be able to push a revision older than the tip."""
444
tree_from = self.make_branch_and_tree('from')
445
tree_from.commit("One.", rev_id="from-1")
446
tree_from.commit("Two.", rev_id="from-2")
448
self.run_bzr('push -r1 ../to', working_dir='from')
450
tree_to = workingtree.WorkingTree.open('to')
451
repo_to = tree_to.branch.repository
452
self.assertTrue(repo_to.has_revision('from-1'))
453
self.assertFalse(repo_to.has_revision('from-2'))
454
self.assertEqual(tree_to.branch.last_revision_info()[1], 'from-1')
457
['bzr: ERROR: bzr push --revision '
458
'takes exactly one revision identifier\n'],
459
'push -r0..2 ../to', working_dir='from')
461
def create_trunk_and_feature_branch(self):
463
trunk_tree = self.make_branch_and_tree('target',
465
trunk_tree.commit('mainline')
466
# and a branch from it
467
branch_tree = self.make_branch_and_tree('branch',
469
branch_tree.pull(trunk_tree.branch)
470
branch_tree.branch.set_parent(trunk_tree.branch.base)
471
# with some work on it
472
branch_tree.commit('moar work plz')
473
return trunk_tree, branch_tree
475
def assertPublished(self, branch_revid, stacked_on):
476
"""Assert that the branch 'published' has been published correctly."""
477
published_branch = branch.Branch.open('published')
478
# The published branch refers to the mainline
479
self.assertEqual(stacked_on, published_branch.get_stacked_on_url())
480
# and the branch's work was pushed
481
self.assertTrue(published_branch.repository.has_revision(branch_revid))
483
def test_push_new_branch_stacked_on(self):
484
"""Pushing a new branch with --stacked-on creates a stacked branch."""
485
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
486
# we publish branch_tree with a reference to the mainline.
487
out, err = self.run_bzr(['push', '--stacked-on', trunk_tree.branch.base,
488
self.get_url('published')], working_dir='branch')
489
self.assertEqual('', out)
490
self.assertEqual('Created new stacked branch referring to %s.\n' %
491
trunk_tree.branch.base, err)
492
self.assertPublished(branch_tree.last_revision(),
493
trunk_tree.branch.base)
495
def test_push_new_branch_stacked_uses_parent_when_no_public_url(self):
496
"""When the parent has no public url the parent is used as-is."""
497
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
498
# now we do a stacked push, which should determine the public location
500
out, err = self.run_bzr(['push', '--stacked',
501
self.get_url('published')], working_dir='branch')
502
self.assertEqual('', out)
503
self.assertEqual('Created new stacked branch referring to %s.\n' %
504
trunk_tree.branch.base, err)
505
self.assertPublished(branch_tree.last_revision(),
506
trunk_tree.branch.base)
508
def test_push_new_branch_stacked_uses_parent_public(self):
509
"""Pushing a new branch with --stacked creates a stacked branch."""
510
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
511
# the trunk is published on a web server
512
self.transport_readonly_server = http_server.HttpServer
513
trunk_public = self.make_branch('public_trunk', format='1.9')
514
trunk_public.pull(trunk_tree.branch)
515
trunk_public_url = self.get_readonly_url('public_trunk')
516
br = trunk_tree.branch
517
br.set_public_branch(trunk_public_url)
518
# now we do a stacked push, which should determine the public location
520
out, err = self.run_bzr(['push', '--stacked',
521
self.get_url('published')], working_dir='branch')
522
self.assertEqual('', out)
523
self.assertEqual('Created new stacked branch referring to %s.\n' %
524
trunk_public_url, err)
525
self.assertPublished(branch_tree.last_revision(), trunk_public_url)
527
def test_push_new_branch_stacked_no_parent(self):
528
"""Pushing with --stacked and no parent branch errors."""
529
branch = self.make_branch_and_tree('branch', format='1.9')
530
# now we do a stacked push, which should fail as the place to refer too
531
# cannot be determined.
532
out, err = self.run_bzr_error(
533
['Could not determine branch to refer to\\.'], ['push', '--stacked',
534
self.get_url('published')], working_dir='branch')
535
self.assertEqual('', out)
536
self.assertFalse(self.get_transport('published').has('.'))
538
def test_push_notifies_default_stacking(self):
539
self.make_branch('stack_on', format='1.6')
540
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
541
self.make_branch('from', format='1.6')
542
out, err = self.run_bzr('push -d from to')
543
self.assertContainsRe(err,
544
'Using default stacking branch stack_on at .*')
546
def test_push_stacks_with_default_stacking_if_target_is_stackable(self):
547
self.make_branch('stack_on', format='1.6')
548
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
549
self.make_branch('from', format='pack-0.92')
550
out, err = self.run_bzr('push -d from to')
551
b = branch.Branch.open('to')
552
self.assertEqual('../stack_on', b.get_stacked_on_url())
554
def test_push_does_not_change_format_with_default_if_target_cannot(self):
555
self.make_branch('stack_on', format='pack-0.92')
556
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
557
self.make_branch('from', format='pack-0.92')
558
out, err = self.run_bzr('push -d from to')
559
b = branch.Branch.open('to')
560
self.assertRaises(errors.UnstackableBranchFormat, b.get_stacked_on_url)
562
def test_push_doesnt_create_broken_branch(self):
563
"""Pushing a new standalone branch works even when there's a default
564
stacking policy at the destination.
566
The new branch will preserve the repo format (even if it isn't the
567
default for the branch), and will be stacked when the repo format
568
allows (which means that the branch format isn't necessarly preserved).
570
self.make_repository('repo', shared=True, format='1.6')
571
builder = self.make_branch_builder('repo/local', format='pack-0.92')
572
builder.start_series()
573
builder.build_snapshot('rev-1', None, [
574
('add', ('', 'root-id', 'directory', '')),
575
('add', ('filename', 'f-id', 'file', 'content\n'))])
576
builder.build_snapshot('rev-2', ['rev-1'], [])
577
builder.build_snapshot('rev-3', ['rev-2'],
578
[('modify', ('f-id', 'new-content\n'))])
579
builder.finish_series()
580
branch = builder.get_branch()
581
# Push rev-1 to "trunk", so that we can stack on it.
582
self.run_bzr('push -d repo/local trunk -r 1')
583
# Set a default stacking policy so that new branches will automatically
585
self.make_bzrdir('.').get_config().set_default_stack_on('trunk')
586
# Push rev-2 to a new branch "remote". It will be stacked on "trunk".
587
out, err = self.run_bzr('push -d repo/local remote -r 2')
588
self.assertContainsRe(
589
err, 'Using default stacking branch trunk at .*')
590
# Push rev-3 onto "remote". If "remote" not stacked and is missing the
591
# fulltext record for f-id @ rev-1, then this will fail.
592
out, err = self.run_bzr('push -d repo/local remote -r 3')
594
def test_push_verbose_shows_log(self):
595
tree = self.make_branch_and_tree('source')
597
out, err = self.run_bzr('push -v -d source target')
598
# initial push contains log
599
self.assertContainsRe(out, 'rev1')
601
out, err = self.run_bzr('push -v -d source target')
602
# subsequent push contains log
603
self.assertContainsRe(out, 'rev2')
604
# subsequent log is accurate
605
self.assertNotContainsRe(out, 'rev1')
607
def test_push_from_subdir(self):
608
t = self.make_branch_and_tree('tree')
609
self.build_tree(['tree/dir/', 'tree/dir/file'])
610
t.add('dir', 'dir/file')
612
out, err = self.run_bzr('push ../../pushloc', working_dir='tree/dir')
613
self.assertEqual('', out)
614
self.assertEqual('Created new branch.\n', err)
617
class RedirectingMemoryTransport(memory.MemoryTransport):
619
def mkdir(self, relpath, mode=None):
620
if self._cwd == '/source/':
621
raise errors.RedirectRequested(self.abspath(relpath),
622
self.abspath('../target'),
624
elif self._cwd == '/infinite-loop/':
625
raise errors.RedirectRequested(self.abspath(relpath),
626
self.abspath('../infinite-loop'),
629
return super(RedirectingMemoryTransport, self).mkdir(
632
def get(self, relpath):
633
if self.clone(relpath)._cwd == '/infinite-loop/':
634
raise errors.RedirectRequested(self.abspath(relpath),
635
self.abspath('../infinite-loop'),
638
return super(RedirectingMemoryTransport, self).get(relpath)
640
def _redirected_to(self, source, target):
641
# We do accept redirections
642
return transport.get_transport(target)
645
class RedirectingMemoryServer(memory.MemoryServer):
647
def start_server(self):
648
self._dirs = {'/': None}
651
self._scheme = 'redirecting-memory+%s:///' % id(self)
652
transport.register_transport(self._scheme, self._memory_factory)
654
def _memory_factory(self, url):
655
result = RedirectingMemoryTransport(url)
656
result._dirs = self._dirs
657
result._files = self._files
658
result._locks = self._locks
661
def stop_server(self):
662
transport.unregister_transport(self._scheme, self._memory_factory)
665
class TestPushRedirect(tests.TestCaseWithTransport):
668
tests.TestCaseWithTransport.setUp(self)
669
self.memory_server = RedirectingMemoryServer()
670
self.start_server(self.memory_server)
671
# Make the branch and tree that we'll be pushing.
672
t = self.make_branch_and_tree('tree')
673
self.build_tree(['tree/file'])
677
def test_push_redirects_on_mkdir(self):
678
"""If the push requires a mkdir, push respects redirect requests.
680
This is added primarily to handle lp:/ URI support, so that users can
681
push to new branches by specifying lp:/ URIs.
683
destination_url = self.memory_server.get_url() + 'source'
684
self.run_bzr(['push', '-d', 'tree', destination_url])
686
local_revision = branch.Branch.open('tree').last_revision()
687
remote_revision = branch.Branch.open(
688
self.memory_server.get_url() + 'target').last_revision()
689
self.assertEqual(remote_revision, local_revision)
691
def test_push_gracefully_handles_too_many_redirects(self):
692
"""Push fails gracefully if the mkdir generates a large number of
695
destination_url = self.memory_server.get_url() + 'infinite-loop'
696
out, err = self.run_bzr_error(
697
['Too many redirections trying to make %s\\.\n'
698
% re.escape(destination_url)],
699
['push', '-d', 'tree', destination_url], retcode=3)
700
self.assertEqual('', out)
703
class TestPushStrictMixin(object):
705
def make_local_branch_and_tree(self):
706
self.tree = self.make_branch_and_tree('local')
707
self.build_tree_contents([('local/file', 'initial')])
708
self.tree.add('file')
709
self.tree.commit('adding file', rev_id='added')
710
self.build_tree_contents([('local/file', 'modified')])
711
self.tree.commit('modify file', rev_id='modified')
713
def set_config_push_strict(self, value):
714
br = branch.Branch.open('local')
715
br.get_config_stack().set('push_strict', value)
717
_default_command = ['push', '../to']
718
_default_wd = 'local'
719
_default_errors = ['Working tree ".*/local/" has uncommitted '
720
'changes \(See bzr status\)\.',]
721
_default_additional_error = 'Use --no-strict to force the push.\n'
722
_default_additional_warning = 'Uncommitted changes will not be pushed.'
725
def assertPushFails(self, args):
726
out, err = self.run_bzr_error(self._default_errors,
727
self._default_command + args,
728
working_dir=self._default_wd, retcode=3)
729
self.assertContainsRe(err, self._default_additional_error)
731
def assertPushSucceeds(self, args, with_warning=False, revid_to_push=None):
733
error_regexes = self._default_errors
736
out, err = self.run_bzr(self._default_command + args,
737
working_dir=self._default_wd,
738
error_regexes=error_regexes)
740
self.assertContainsRe(err, self._default_additional_warning)
742
self.assertNotContainsRe(err, self._default_additional_warning)
743
branch_from = branch.Branch.open(self._default_wd)
744
if revid_to_push is None:
745
revid_to_push = branch_from.last_revision()
746
branch_to = branch.Branch.open('to')
747
repo_to = branch_to.repository
748
self.assertTrue(repo_to.has_revision(revid_to_push))
749
self.assertEqual(revid_to_push, branch_to.last_revision())
753
class TestPushStrictWithoutChanges(tests.TestCaseWithTransport,
754
TestPushStrictMixin):
757
super(TestPushStrictWithoutChanges, self).setUp()
758
self.make_local_branch_and_tree()
760
def test_push_default(self):
761
self.assertPushSucceeds([])
763
def test_push_strict(self):
764
self.assertPushSucceeds(['--strict'])
766
def test_push_no_strict(self):
767
self.assertPushSucceeds(['--no-strict'])
769
def test_push_config_var_strict(self):
770
self.set_config_push_strict('true')
771
self.assertPushSucceeds([])
773
def test_push_config_var_no_strict(self):
774
self.set_config_push_strict('false')
775
self.assertPushSucceeds([])
778
strict_push_change_scenarios = [
780
dict(_changes_type= '_uncommitted_changes')),
782
dict(_changes_type= '_pending_merges')),
783
('out-of-sync-trees',
784
dict(_changes_type= '_out_of_sync_trees')),
788
class TestPushStrictWithChanges(tests.TestCaseWithTransport,
789
TestPushStrictMixin):
791
scenarios = strict_push_change_scenarios
792
_changes_type = None # Set by load_tests
795
super(TestPushStrictWithChanges, self).setUp()
796
# Apply the changes defined in load_tests: one of _uncommitted_changes,
797
# _pending_merges or _out_of_sync_trees
798
getattr(self, self._changes_type)()
800
def _uncommitted_changes(self):
801
self.make_local_branch_and_tree()
802
# Make a change without committing it
803
self.build_tree_contents([('local/file', 'in progress')])
805
def _pending_merges(self):
806
self.make_local_branch_and_tree()
807
# Create 'other' branch containing a new file
808
other_bzrdir = self.tree.bzrdir.sprout('other')
809
other_tree = other_bzrdir.open_workingtree()
810
self.build_tree_contents([('other/other-file', 'other')])
811
other_tree.add('other-file')
812
other_tree.commit('other commit', rev_id='other')
813
# Merge and revert, leaving a pending merge
814
self.tree.merge_from_branch(other_tree.branch)
815
self.tree.revert(filenames=['other-file'], backups=False)
817
def _out_of_sync_trees(self):
818
self.make_local_branch_and_tree()
819
self.run_bzr(['checkout', '--lightweight', 'local', 'checkout'])
820
# Make a change and commit it
821
self.build_tree_contents([('local/file', 'modified in local')])
822
self.tree.commit('modify file', rev_id='modified-in-local')
823
# Exercise commands from the checkout directory
824
self._default_wd = 'checkout'
825
self._default_errors = ["Working tree is out of date, please run"
828
def test_push_default(self):
829
self.assertPushSucceeds([], with_warning=True)
831
def test_push_with_revision(self):
832
self.assertPushSucceeds(['-r', 'revid:added'], revid_to_push='added')
834
def test_push_no_strict(self):
835
self.assertPushSucceeds(['--no-strict'])
837
def test_push_strict_with_changes(self):
838
self.assertPushFails(['--strict'])
840
def test_push_respect_config_var_strict(self):
841
self.set_config_push_strict('true')
842
self.assertPushFails([])
844
def test_push_bogus_config_var_ignored(self):
845
self.set_config_push_strict("I don't want you to be strict")
846
self.assertPushSucceeds([], with_warning=True)
848
def test_push_no_strict_command_line_override_config(self):
849
self.set_config_push_strict('yES')
850
self.assertPushFails([])
851
self.assertPushSucceeds(['--no-strict'])
853
def test_push_strict_command_line_override_config(self):
854
self.set_config_push_strict('oFF')
855
self.assertPushFails(['--strict'])
856
self.assertPushSucceeds([])
859
class TestPushForeign(tests.TestCaseWithTransport):
862
super(TestPushForeign, self).setUp()
863
test_foreign.register_dummy_foreign_for_test(self)
865
def make_dummy_builder(self, relpath):
866
builder = self.make_branch_builder(
867
relpath, format=test_foreign.DummyForeignVcsDirFormat())
868
builder.build_snapshot('revid', None,
869
[('add', ('', 'TREE_ROOT', 'directory', None)),
870
('add', ('foo', 'fooid', 'file', 'bar'))])
873
def test_no_roundtripping(self):
874
target_branch = self.make_dummy_builder('dp').get_branch()
875
source_tree = self.make_branch_and_tree("dc")
876
output, error = self.run_bzr("push -d dc dp", retcode=3)
877
self.assertEquals("", output)
878
self.assertEquals(error, "bzr: ERROR: It is not possible to losslessly"
879
" push to dummy. You may want to use dpush instead.\n")
882
class TestPushOutput(script.TestCaseWithTransportAndScript):
884
def test_push_log_format(self):
887
Created a standalone tree (format: 2a)
892
$ bzr commit -m 'we need some foo'
893
2>Committing to:...trunk/
895
2>Committed revision 1.
896
$ bzr init ../feature
897
Created a standalone tree (format: 2a)
898
$ bzr push -v ../feature -Olog_format=line
900
1: jrandom@example.com ...we need some foo
901
2>All changes applied successfully.
902
2>Pushed up to revision 1.