1
# Copyright (C) 2006-2012, 2016 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 brz push."""
33
from breezy.bzr import (
36
from breezy.bzr import knitrepo
37
from breezy.tests import (
43
from breezy.tests.matchers import ContainsNoVfsCalls
44
from breezy.transport import memory
47
load_tests = scenarios.load_tests_apply_scenarios
50
class TestPush(tests.TestCaseWithTransport):
52
def test_push_error_on_vfs_http(self):
53
""" pushing a branch to a HTTP server fails cleanly. """
54
# the trunk is published on a web server
55
self.transport_readonly_server = http_server.HttpServer
56
self.make_branch('source')
57
public_url = self.get_readonly_url('target')
58
self.run_bzr_error(['http does not support mkdir'],
62
def test_push_suggests_parent_alias(self):
63
"""Push suggests using :parent if there is a known parent branch."""
64
tree_a = self.make_branch_and_tree('a')
65
tree_a.commit('this is a commit')
66
tree_b = self.make_branch_and_tree('b')
68
# If there is no parent location set, :parent isn't mentioned.
69
out = self.run_bzr('push', working_dir='a', retcode=3)
71
('','brz: ERROR: No push location known or specified.\n'))
73
# If there is a parent location set, the error suggests :parent.
74
tree_a.branch.set_parent(tree_b.branch.base)
75
out = self.run_bzr('push', working_dir='a', retcode=3)
77
('','brz: ERROR: No push location known or specified. '
78
'To push to the parent branch '
79
'(at %s), use \'brz push :parent\'.\n' %
80
urlutils.unescape_for_display(tree_b.branch.base, 'utf-8')))
82
def test_push_remember(self):
83
"""Push changes from one branch to another and test push location."""
84
transport = self.get_transport()
85
tree_a = self.make_branch_and_tree('branch_a')
86
branch_a = tree_a.branch
87
self.build_tree(['branch_a/a'])
89
tree_a.commit('commit a')
90
tree_b = branch_a.controldir.sprout('branch_b').open_workingtree()
91
branch_b = tree_b.branch
92
tree_c = branch_a.controldir.sprout('branch_c').open_workingtree()
93
branch_c = tree_c.branch
94
self.build_tree(['branch_a/b'])
96
tree_a.commit('commit b')
97
self.build_tree(['branch_b/c'])
99
tree_b.commit('commit c')
100
# initial push location must be empty
101
self.assertEqual(None, branch_b.get_push_location())
103
# test push for failure without push location set
104
out = self.run_bzr('push', working_dir='branch_a', retcode=3)
105
self.assertEqual(out,
106
('','brz: ERROR: No push location known or specified.\n'))
108
# test not remembered if cannot actually push
109
self.run_bzr('push path/which/doesnt/exist',
110
working_dir='branch_a', retcode=3)
111
out = self.run_bzr('push', working_dir='branch_a', retcode=3)
113
('', 'brz: ERROR: No push location known or specified.\n'),
116
# test implicit --remember when no push location set, push fails
117
out = self.run_bzr('push ../branch_b',
118
working_dir='branch_a', retcode=3)
119
self.assertEqual(out,
120
('','brz: ERROR: These branches have diverged. '
121
'See "brz help diverged-branches" for more information.\n'))
122
# Refresh the branch as 'push' modified it
123
branch_a = branch_a.controldir.open_branch()
124
self.assertEqual(osutils.abspath(branch_a.get_push_location()),
125
osutils.abspath(branch_b.controldir.root_transport.base))
127
# test implicit --remember after resolving previous failure
128
uncommit.uncommit(branch=branch_b, tree=tree_b)
129
transport.delete('branch_b/c')
130
out, err = self.run_bzr('push', working_dir='branch_a')
131
# Refresh the branch as 'push' modified it
132
branch_a = branch_a.controldir.open_branch()
133
path = branch_a.get_push_location()
134
self.assertEqual(err,
135
'Using saved push location: %s\n'
136
'All changes applied successfully.\n'
137
'Pushed up to revision 2.\n'
138
% urlutils.local_path_from_url(path))
139
self.assertEqual(path,
140
branch_b.controldir.root_transport.base)
141
# test explicit --remember
142
self.run_bzr('push ../branch_c --remember', working_dir='branch_a')
143
# Refresh the branch as 'push' modified it
144
branch_a = branch_a.controldir.open_branch()
145
self.assertEqual(branch_a.get_push_location(),
146
branch_c.controldir.root_transport.base)
148
def test_push_without_tree(self):
149
# brz push from a branch that does not have a checkout should work.
150
b = self.make_branch('.')
151
out, err = self.run_bzr('push pushed-location')
152
self.assertEqual('', out)
153
self.assertEqual('Created new branch.\n', err)
154
b2 = branch.Branch.open('pushed-location')
155
self.assertEndsWith(b2.base, 'pushed-location/')
157
def test_push_no_tree(self):
158
# brz push --no-tree of a branch with working trees
159
b = self.make_branch_and_tree('push-from')
160
self.build_tree(['push-from/file'])
163
out, err = self.run_bzr('push --no-tree -d push-from push-to')
164
self.assertEqual('', out)
165
self.assertEqual('Created new branch.\n', err)
166
self.assertPathDoesNotExist('push-to/file')
168
def test_push_new_branch_revision_count(self):
169
# brz push of a branch with revisions to a new location
170
# should print the number of revisions equal to the length of the
172
t = self.make_branch_and_tree('tree')
173
self.build_tree(['tree/file'])
176
out, err = self.run_bzr('push -d tree pushed-to')
177
self.assertEqual('', out)
178
self.assertEqual('Created new branch.\n', err)
180
def test_push_quiet(self):
181
# test that using -q makes output quiet
182
t = self.make_branch_and_tree('tree')
183
self.build_tree(['tree/file'])
186
self.run_bzr('push -d tree pushed-to')
187
# Refresh the branch as 'push' modified it and get the push location
188
push_loc = t.branch.controldir.open_branch().get_push_location()
189
out, err = self.run_bzr('push', working_dir="tree")
190
self.assertEqual('Using saved push location: %s\n'
191
'No new revisions or tags to push.\n' %
192
urlutils.local_path_from_url(push_loc), err)
193
out, err = self.run_bzr('push -q', working_dir="tree")
194
self.assertEqual('', out)
195
self.assertEqual('', err)
197
def test_push_only_pushes_history(self):
198
# Knit branches should only push the history for the current revision.
199
format = bzrdir.BzrDirMetaFormat1()
200
format.repository_format = knitrepo.RepositoryFormatKnit1()
201
shared_repo = self.make_repository('repo', format=format, shared=True)
202
shared_repo.set_make_working_trees(True)
204
def make_shared_tree(path):
205
shared_repo.controldir.root_transport.mkdir(path)
206
controldir.ControlDir.create_branch_convenience('repo/' + path)
207
return workingtree.WorkingTree.open('repo/' + path)
208
tree_a = make_shared_tree('a')
209
self.build_tree(['repo/a/file'])
211
tree_a.commit('commit a-1', rev_id='a-1')
212
f = open('repo/a/file', 'ab')
213
f.write('more stuff\n')
215
tree_a.commit('commit a-2', rev_id='a-2')
217
tree_b = make_shared_tree('b')
218
self.build_tree(['repo/b/file'])
220
tree_b.commit('commit b-1', rev_id='b-1')
222
self.assertTrue(shared_repo.has_revision('a-1'))
223
self.assertTrue(shared_repo.has_revision('a-2'))
224
self.assertTrue(shared_repo.has_revision('b-1'))
226
# Now that we have a repository with shared files, make sure
227
# that things aren't copied out by a 'push'
228
self.run_bzr('push ../../push-b', working_dir='repo/b')
229
pushed_tree = workingtree.WorkingTree.open('push-b')
230
pushed_repo = pushed_tree.branch.repository
231
self.assertFalse(pushed_repo.has_revision('a-1'))
232
self.assertFalse(pushed_repo.has_revision('a-2'))
233
self.assertTrue(pushed_repo.has_revision('b-1'))
235
def test_push_funky_id(self):
236
t = self.make_branch_and_tree('tree')
237
self.build_tree(['tree/filename'])
238
t.add('filename', 'funky-chars<>%&;"\'')
239
t.commit('commit filename')
240
self.run_bzr('push -d tree new-tree')
242
def test_push_dash_d(self):
243
t = self.make_branch_and_tree('from')
244
t.commit(allow_pointless=True,
245
message='first commit')
246
self.run_bzr('push -d from to-one')
247
self.assertPathExists('to-one')
248
self.run_bzr('push -d %s %s'
249
% tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
250
self.assertPathExists('to-two')
252
def test_push_repository_no_branch_doesnt_fetch_all_revs(self):
253
# See https://bugs.launchpad.net/bzr/+bug/465517
254
target_repo = self.make_repository('target')
255
source = self.make_branch_builder('source')
256
source.start_series()
257
source.build_snapshot('A', None, [
258
('add', ('', 'root-id', 'directory', None))])
259
source.build_snapshot('B', ['A'], [])
260
source.build_snapshot('C', ['A'], [])
261
source.finish_series()
262
self.run_bzr('push target -d source')
263
self.addCleanup(target_repo.lock_read().unlock)
264
# We should have pushed 'C', but not 'B', since it isn't in the
266
self.assertEqual([('A',), ('C',)], sorted(target_repo.revisions.keys()))
268
def test_push_smart_non_stacked_streaming_acceptance(self):
269
self.setup_smart_server_with_call_log()
270
t = self.make_branch_and_tree('from')
271
t.commit(allow_pointless=True, message='first commit')
272
self.reset_smart_call_log()
273
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
274
# This figure represent the amount of work to perform this use case. It
275
# is entirely ok to reduce this number if a test fails due to rpc_count
276
# being too low. If rpc_count increases, more network roundtrips have
277
# become necessary for this use case. Please do not adjust this number
278
# upwards without agreement from bzr's network support maintainers.
279
self.assertLength(9, self.hpss_calls)
280
self.assertLength(1, self.hpss_connections)
281
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
283
def test_push_smart_stacked_streaming_acceptance(self):
284
self.setup_smart_server_with_call_log()
285
parent = self.make_branch_and_tree('parent', format='1.9')
286
parent.commit(message='first commit')
287
local = parent.controldir.sprout('local').open_workingtree()
288
local.commit(message='local commit')
289
self.reset_smart_call_log()
290
self.run_bzr(['push', '--stacked', '--stacked-on', '../parent',
291
self.get_url('public')], working_dir='local')
292
# This figure represent the amount of work to perform this use case. It
293
# is entirely ok to reduce this number if a test fails due to rpc_count
294
# being too low. If rpc_count increases, more network roundtrips have
295
# become necessary for this use case. Please do not adjust this number
296
# upwards without agreement from bzr's network support maintainers.
297
self.assertLength(15, self.hpss_calls)
298
self.assertLength(1, self.hpss_connections)
299
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
300
remote = branch.Branch.open('public')
301
self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
303
def test_push_smart_tags_streaming_acceptance(self):
304
self.setup_smart_server_with_call_log()
305
t = self.make_branch_and_tree('from')
306
rev_id = t.commit(allow_pointless=True, message='first commit')
307
t.branch.tags.set_tag('new-tag', rev_id)
308
self.reset_smart_call_log()
309
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
310
# This figure represent the amount of work to perform this use case. It
311
# is entirely ok to reduce this number if a test fails due to rpc_count
312
# being too low. If rpc_count increases, more network roundtrips have
313
# become necessary for this use case. Please do not adjust this number
314
# upwards without agreement from bzr's network support maintainers.
315
self.assertLength(11, self.hpss_calls)
316
self.assertLength(1, self.hpss_connections)
317
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
319
def test_push_smart_incremental_acceptance(self):
320
self.setup_smart_server_with_call_log()
321
t = self.make_branch_and_tree('from')
322
rev_id1 = t.commit(allow_pointless=True, message='first commit')
323
rev_id2 = t.commit(allow_pointless=True, message='second commit')
325
['push', self.get_url('to-one'), '-r1'], working_dir='from')
326
self.reset_smart_call_log()
327
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
328
# This figure represent the amount of work to perform this use case. It
329
# is entirely ok to reduce this number if a test fails due to rpc_count
330
# being too low. If rpc_count increases, more network roundtrips have
331
# become necessary for this use case. Please do not adjust this number
332
# upwards without agreement from bzr's network support maintainers.
333
self.assertLength(11, self.hpss_calls)
334
self.assertLength(1, self.hpss_connections)
335
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
337
def test_push_smart_with_default_stacking_url_path_segment(self):
338
# If the default stacked-on location is a path element then branches
339
# we push there over the smart server are stacked and their
340
# stacked_on_url is that exact path segment. Added to nail bug 385132.
341
self.setup_smart_server_with_call_log()
342
self.make_branch('stack-on', format='1.9')
343
self.make_controldir('.').get_config().set_default_stack_on(
345
self.make_branch('from', format='1.9')
346
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
347
b = branch.Branch.open(self.get_url('to'))
348
self.assertEqual('/extra/stack-on', b.get_stacked_on_url())
350
def test_push_smart_with_default_stacking_relative_path(self):
351
# If the default stacked-on location is a relative path then branches
352
# we push there over the smart server are stacked and their
353
# stacked_on_url is a relative path. Added to nail bug 385132.
354
self.setup_smart_server_with_call_log()
355
self.make_branch('stack-on', format='1.9')
356
self.make_controldir('.').get_config().set_default_stack_on('stack-on')
357
self.make_branch('from', format='1.9')
358
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
359
b = branch.Branch.open(self.get_url('to'))
360
self.assertEqual('../stack-on', b.get_stacked_on_url())
362
def create_simple_tree(self):
363
tree = self.make_branch_and_tree('tree')
364
self.build_tree(['tree/a'])
365
tree.add(['a'], ['a-id'])
366
tree.commit('one', rev_id='r1')
369
def test_push_create_prefix(self):
370
"""'brz push --create-prefix' will create leading directories."""
371
tree = self.create_simple_tree()
373
self.run_bzr_error(['Parent directory of ../new/tree does not exist'],
376
self.run_bzr('push ../new/tree --create-prefix',
378
new_tree = workingtree.WorkingTree.open('new/tree')
379
self.assertEqual(tree.last_revision(), new_tree.last_revision())
380
self.assertPathExists('new/tree/a')
382
def test_push_use_existing(self):
383
"""'brz push --use-existing-dir' can push into an existing dir.
385
By default, 'brz push' will not use an existing, non-versioned dir.
387
tree = self.create_simple_tree()
388
self.build_tree(['target/'])
390
self.run_bzr_error(['Target directory ../target already exists',
391
'Supply --use-existing-dir',
393
'push ../target', working_dir='tree')
395
self.run_bzr('push --use-existing-dir ../target',
398
new_tree = workingtree.WorkingTree.open('target')
399
self.assertEqual(tree.last_revision(), new_tree.last_revision())
400
# The push should have created target/a
401
self.assertPathExists('target/a')
403
def test_push_use_existing_into_empty_bzrdir(self):
404
"""'brz push --use-existing-dir' into a dir with an empty .bzr dir
407
tree = self.create_simple_tree()
408
self.build_tree(['target/', 'target/.bzr/'])
410
['Target directory ../target already contains a .bzr directory, '
411
'but it is not valid.'],
412
'push ../target --use-existing-dir', working_dir='tree')
414
def test_push_onto_repo(self):
415
"""We should be able to 'brz push' into an existing bzrdir."""
416
tree = self.create_simple_tree()
417
repo = self.make_repository('repo', shared=True)
419
self.run_bzr('push ../repo',
422
# Pushing onto an existing bzrdir will create a repository and
423
# branch as needed, but will only create a working tree if there was
425
self.assertRaises(errors.NoWorkingTree,
426
workingtree.WorkingTree.open, 'repo')
427
new_branch = branch.Branch.open('repo')
428
self.assertEqual(tree.last_revision(), new_branch.last_revision())
430
def test_push_onto_just_bzrdir(self):
431
"""We don't handle when the target is just a bzrdir.
433
Because you shouldn't be able to create *just* a bzrdir in the wild.
435
# TODO: jam 20070109 Maybe it would be better to create the repository
437
tree = self.create_simple_tree()
438
a_controldir = self.make_controldir('dir')
440
self.run_bzr_error(['At ../dir you have a valid .bzr control'],
444
def test_push_with_revisionspec(self):
445
"""We should be able to push a revision older than the tip."""
446
tree_from = self.make_branch_and_tree('from')
447
tree_from.commit("One.", rev_id="from-1")
448
tree_from.commit("Two.", rev_id="from-2")
450
self.run_bzr('push -r1 ../to', working_dir='from')
452
tree_to = workingtree.WorkingTree.open('to')
453
repo_to = tree_to.branch.repository
454
self.assertTrue(repo_to.has_revision('from-1'))
455
self.assertFalse(repo_to.has_revision('from-2'))
456
self.assertEqual(tree_to.branch.last_revision_info()[1], 'from-1')
458
tree_to.changes_from(tree_to.basis_tree()).has_changed())
461
['brz: ERROR: brz push --revision '
462
'takes exactly one revision identifier\n'],
463
'push -r0..2 ../to', working_dir='from')
465
def create_trunk_and_feature_branch(self):
467
trunk_tree = self.make_branch_and_tree('target',
469
trunk_tree.commit('mainline')
470
# and a branch from it
471
branch_tree = self.make_branch_and_tree('branch',
473
branch_tree.pull(trunk_tree.branch)
474
branch_tree.branch.set_parent(trunk_tree.branch.base)
475
# with some work on it
476
branch_tree.commit('moar work plz')
477
return trunk_tree, branch_tree
479
def assertPublished(self, branch_revid, stacked_on):
480
"""Assert that the branch 'published' has been published correctly."""
481
published_branch = branch.Branch.open('published')
482
# The published branch refers to the mainline
483
self.assertEqual(stacked_on, published_branch.get_stacked_on_url())
484
# and the branch's work was pushed
485
self.assertTrue(published_branch.repository.has_revision(branch_revid))
487
def test_push_new_branch_stacked_on(self):
488
"""Pushing a new branch with --stacked-on creates a stacked branch."""
489
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
490
# we publish branch_tree with a reference to the mainline.
491
out, err = self.run_bzr(['push', '--stacked-on', trunk_tree.branch.base,
492
self.get_url('published')], working_dir='branch')
493
self.assertEqual('', out)
494
self.assertEqual('Created new stacked branch referring to %s.\n' %
495
trunk_tree.branch.base, err)
496
self.assertPublished(branch_tree.last_revision(),
497
trunk_tree.branch.base)
499
def test_push_new_branch_stacked_uses_parent_when_no_public_url(self):
500
"""When the parent has no public url the parent is used as-is."""
501
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
502
# now we do a stacked push, which should determine the public location
504
out, err = self.run_bzr(['push', '--stacked',
505
self.get_url('published')], working_dir='branch')
506
self.assertEqual('', out)
507
self.assertEqual('Created new stacked branch referring to %s.\n' %
508
trunk_tree.branch.base, err)
509
self.assertPublished(branch_tree.last_revision(),
510
trunk_tree.branch.base)
512
def test_push_new_branch_stacked_uses_parent_public(self):
513
"""Pushing a new branch with --stacked creates a stacked branch."""
514
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
515
# the trunk is published on a web server
516
self.transport_readonly_server = http_server.HttpServer
517
trunk_public = self.make_branch('public_trunk', format='1.9')
518
trunk_public.pull(trunk_tree.branch)
519
trunk_public_url = self.get_readonly_url('public_trunk')
520
br = trunk_tree.branch
521
br.set_public_branch(trunk_public_url)
522
# now we do a stacked push, which should determine the public location
524
out, err = self.run_bzr(['push', '--stacked',
525
self.get_url('published')], working_dir='branch')
526
self.assertEqual('', out)
527
self.assertEqual('Created new stacked branch referring to %s.\n' %
528
trunk_public_url, err)
529
self.assertPublished(branch_tree.last_revision(), trunk_public_url)
531
def test_push_new_branch_stacked_no_parent(self):
532
"""Pushing with --stacked and no parent branch errors."""
533
branch = self.make_branch_and_tree('branch', format='1.9')
534
# now we do a stacked push, which should fail as the place to refer too
535
# cannot be determined.
536
out, err = self.run_bzr_error(
537
['Could not determine branch to refer to\\.'], ['push', '--stacked',
538
self.get_url('published')], working_dir='branch')
539
self.assertEqual('', out)
540
self.assertFalse(self.get_transport('published').has('.'))
542
def test_push_notifies_default_stacking(self):
543
self.make_branch('stack_on', format='1.6')
544
self.make_controldir('.').get_config().set_default_stack_on('stack_on')
545
self.make_branch('from', format='1.6')
546
out, err = self.run_bzr('push -d from to')
547
self.assertContainsRe(err,
548
'Using default stacking branch stack_on at .*')
550
def test_push_stacks_with_default_stacking_if_target_is_stackable(self):
551
self.make_branch('stack_on', format='1.6')
552
self.make_controldir('.').get_config().set_default_stack_on('stack_on')
553
self.make_branch('from', format='pack-0.92')
554
out, err = self.run_bzr('push -d from to')
555
b = branch.Branch.open('to')
556
self.assertEqual('../stack_on', b.get_stacked_on_url())
558
def test_push_does_not_change_format_with_default_if_target_cannot(self):
559
self.make_branch('stack_on', format='pack-0.92')
560
self.make_controldir('.').get_config().set_default_stack_on('stack_on')
561
self.make_branch('from', format='pack-0.92')
562
out, err = self.run_bzr('push -d from to')
563
b = branch.Branch.open('to')
564
self.assertRaises(errors.UnstackableBranchFormat, b.get_stacked_on_url)
566
def test_push_doesnt_create_broken_branch(self):
567
"""Pushing a new standalone branch works even when there's a default
568
stacking policy at the destination.
570
The new branch will preserve the repo format (even if it isn't the
571
default for the branch), and will be stacked when the repo format
572
allows (which means that the branch format isn't necessarly preserved).
574
self.make_repository('repo', shared=True, format='1.6')
575
builder = self.make_branch_builder('repo/local', format='pack-0.92')
576
builder.start_series()
577
builder.build_snapshot('rev-1', None, [
578
('add', ('', 'root-id', 'directory', '')),
579
('add', ('filename', 'f-id', 'file', 'content\n'))])
580
builder.build_snapshot('rev-2', ['rev-1'], [])
581
builder.build_snapshot('rev-3', ['rev-2'],
582
[('modify', ('f-id', 'new-content\n'))])
583
builder.finish_series()
584
branch = builder.get_branch()
585
# Push rev-1 to "trunk", so that we can stack on it.
586
self.run_bzr('push -d repo/local trunk -r 1')
587
# Set a default stacking policy so that new branches will automatically
589
self.make_controldir('.').get_config().set_default_stack_on('trunk')
590
# Push rev-2 to a new branch "remote". It will be stacked on "trunk".
591
out, err = self.run_bzr('push -d repo/local remote -r 2')
592
self.assertContainsRe(
593
err, 'Using default stacking branch trunk at .*')
594
# Push rev-3 onto "remote". If "remote" not stacked and is missing the
595
# fulltext record for f-id @ rev-1, then this will fail.
596
out, err = self.run_bzr('push -d repo/local remote -r 3')
598
def test_push_verbose_shows_log(self):
599
tree = self.make_branch_and_tree('source')
601
out, err = self.run_bzr('push -v -d source target')
602
# initial push contains log
603
self.assertContainsRe(out, 'rev1')
605
out, err = self.run_bzr('push -v -d source target')
606
# subsequent push contains log
607
self.assertContainsRe(out, 'rev2')
608
# subsequent log is accurate
609
self.assertNotContainsRe(out, 'rev1')
611
def test_push_from_subdir(self):
612
t = self.make_branch_and_tree('tree')
613
self.build_tree(['tree/dir/', 'tree/dir/file'])
614
t.add('dir', 'dir/file')
616
out, err = self.run_bzr('push ../../pushloc', working_dir='tree/dir')
617
self.assertEqual('', out)
618
self.assertEqual('Created new branch.\n', err)
620
def test_overwrite_tags(self):
621
"""--overwrite-tags only overwrites tags, not revisions."""
622
from_tree = self.make_branch_and_tree('from')
623
from_tree.branch.tags.set_tag("mytag", "somerevid")
624
to_tree = self.make_branch_and_tree('to')
625
to_tree.branch.tags.set_tag("mytag", "anotherrevid")
626
revid1 = to_tree.commit('my commit')
627
out = self.run_bzr(['push', '-d', 'from', 'to'])
628
self.assertEqual(out,
629
('Conflicting tags:\n mytag\n', 'No new revisions to push.\n'))
630
out = self.run_bzr(['push', '-d', 'from', '--overwrite-tags', 'to'])
631
self.assertEqual(out, ('', '1 tag updated.\n'))
632
self.assertEqual(to_tree.branch.tags.lookup_tag('mytag'),
634
self.assertEqual(to_tree.branch.last_revision(), revid1)
637
class RedirectingMemoryTransport(memory.MemoryTransport):
639
def mkdir(self, relpath, mode=None):
640
if self._cwd == '/source/':
641
raise errors.RedirectRequested(self.abspath(relpath),
642
self.abspath('../target'),
644
elif self._cwd == '/infinite-loop/':
645
raise errors.RedirectRequested(self.abspath(relpath),
646
self.abspath('../infinite-loop'),
649
return super(RedirectingMemoryTransport, self).mkdir(
652
def get(self, relpath):
653
if self.clone(relpath)._cwd == '/infinite-loop/':
654
raise errors.RedirectRequested(self.abspath(relpath),
655
self.abspath('../infinite-loop'),
658
return super(RedirectingMemoryTransport, self).get(relpath)
660
def _redirected_to(self, source, target):
661
# We do accept redirections
662
return transport.get_transport(target)
665
class RedirectingMemoryServer(memory.MemoryServer):
667
def start_server(self):
668
self._dirs = {'/': None}
671
self._scheme = 'redirecting-memory+%s:///' % id(self)
672
transport.register_transport(self._scheme, self._memory_factory)
674
def _memory_factory(self, url):
675
result = RedirectingMemoryTransport(url)
676
result._dirs = self._dirs
677
result._files = self._files
678
result._locks = self._locks
681
def stop_server(self):
682
transport.unregister_transport(self._scheme, self._memory_factory)
685
class TestPushRedirect(tests.TestCaseWithTransport):
688
super(TestPushRedirect, self).setUp()
689
self.memory_server = RedirectingMemoryServer()
690
self.start_server(self.memory_server)
691
# Make the branch and tree that we'll be pushing.
692
t = self.make_branch_and_tree('tree')
693
self.build_tree(['tree/file'])
697
def test_push_redirects_on_mkdir(self):
698
"""If the push requires a mkdir, push respects redirect requests.
700
This is added primarily to handle lp:/ URI support, so that users can
701
push to new branches by specifying lp:/ URIs.
703
destination_url = self.memory_server.get_url() + 'source'
704
self.run_bzr(['push', '-d', 'tree', destination_url])
706
local_revision = branch.Branch.open('tree').last_revision()
707
remote_revision = branch.Branch.open(
708
self.memory_server.get_url() + 'target').last_revision()
709
self.assertEqual(remote_revision, local_revision)
711
def test_push_gracefully_handles_too_many_redirects(self):
712
"""Push fails gracefully if the mkdir generates a large number of
715
destination_url = self.memory_server.get_url() + 'infinite-loop'
716
out, err = self.run_bzr_error(
717
['Too many redirections trying to make %s\\.\n'
718
% re.escape(destination_url)],
719
['push', '-d', 'tree', destination_url], retcode=3)
720
self.assertEqual('', out)
723
class TestPushStrictMixin(object):
725
def make_local_branch_and_tree(self):
726
self.tree = self.make_branch_and_tree('local')
727
self.build_tree_contents([('local/file', 'initial')])
728
self.tree.add('file')
729
self.tree.commit('adding file', rev_id='added')
730
self.build_tree_contents([('local/file', 'modified')])
731
self.tree.commit('modify file', rev_id='modified')
733
def set_config_push_strict(self, value):
734
br = branch.Branch.open('local')
735
br.get_config_stack().set('push_strict', value)
737
_default_command = ['push', '../to']
738
_default_wd = 'local'
739
_default_errors = ['Working tree ".*/local/" has uncommitted '
740
'changes \(See brz status\)\.',]
741
_default_additional_error = 'Use --no-strict to force the push.\n'
742
_default_additional_warning = 'Uncommitted changes will not be pushed.'
745
def assertPushFails(self, args):
746
out, err = self.run_bzr_error(self._default_errors,
747
self._default_command + args,
748
working_dir=self._default_wd, retcode=3)
749
self.assertContainsRe(err, self._default_additional_error)
751
def assertPushSucceeds(self, args, with_warning=False, revid_to_push=None):
753
error_regexes = self._default_errors
756
out, err = self.run_bzr(self._default_command + args,
757
working_dir=self._default_wd,
758
error_regexes=error_regexes)
760
self.assertContainsRe(err, self._default_additional_warning)
762
self.assertNotContainsRe(err, self._default_additional_warning)
763
branch_from = branch.Branch.open(self._default_wd)
764
if revid_to_push is None:
765
revid_to_push = branch_from.last_revision()
766
branch_to = branch.Branch.open('to')
767
repo_to = branch_to.repository
768
self.assertTrue(repo_to.has_revision(revid_to_push))
769
self.assertEqual(revid_to_push, branch_to.last_revision())
773
class TestPushStrictWithoutChanges(tests.TestCaseWithTransport,
774
TestPushStrictMixin):
777
super(TestPushStrictWithoutChanges, self).setUp()
778
self.make_local_branch_and_tree()
780
def test_push_default(self):
781
self.assertPushSucceeds([])
783
def test_push_strict(self):
784
self.assertPushSucceeds(['--strict'])
786
def test_push_no_strict(self):
787
self.assertPushSucceeds(['--no-strict'])
789
def test_push_config_var_strict(self):
790
self.set_config_push_strict('true')
791
self.assertPushSucceeds([])
793
def test_push_config_var_no_strict(self):
794
self.set_config_push_strict('false')
795
self.assertPushSucceeds([])
798
strict_push_change_scenarios = [
800
dict(_changes_type= '_uncommitted_changes')),
802
dict(_changes_type= '_pending_merges')),
803
('out-of-sync-trees',
804
dict(_changes_type= '_out_of_sync_trees')),
808
class TestPushStrictWithChanges(tests.TestCaseWithTransport,
809
TestPushStrictMixin):
811
scenarios = strict_push_change_scenarios
812
_changes_type = None # Set by load_tests
815
super(TestPushStrictWithChanges, self).setUp()
816
# Apply the changes defined in load_tests: one of _uncommitted_changes,
817
# _pending_merges or _out_of_sync_trees
818
getattr(self, self._changes_type)()
820
def _uncommitted_changes(self):
821
self.make_local_branch_and_tree()
822
# Make a change without committing it
823
self.build_tree_contents([('local/file', 'in progress')])
825
def _pending_merges(self):
826
self.make_local_branch_and_tree()
827
# Create 'other' branch containing a new file
828
other_bzrdir = self.tree.controldir.sprout('other')
829
other_tree = other_bzrdir.open_workingtree()
830
self.build_tree_contents([('other/other-file', 'other')])
831
other_tree.add('other-file')
832
other_tree.commit('other commit', rev_id='other')
833
# Merge and revert, leaving a pending merge
834
self.tree.merge_from_branch(other_tree.branch)
835
self.tree.revert(filenames=['other-file'], backups=False)
837
def _out_of_sync_trees(self):
838
self.make_local_branch_and_tree()
839
self.run_bzr(['checkout', '--lightweight', 'local', 'checkout'])
840
# Make a change and commit it
841
self.build_tree_contents([('local/file', 'modified in local')])
842
self.tree.commit('modify file', rev_id='modified-in-local')
843
# Exercise commands from the checkout directory
844
self._default_wd = 'checkout'
845
self._default_errors = ["Working tree is out of date, please run"
848
def test_push_default(self):
849
self.assertPushSucceeds([], with_warning=True)
851
def test_push_with_revision(self):
852
self.assertPushSucceeds(['-r', 'revid:added'], revid_to_push='added')
854
def test_push_no_strict(self):
855
self.assertPushSucceeds(['--no-strict'])
857
def test_push_strict_with_changes(self):
858
self.assertPushFails(['--strict'])
860
def test_push_respect_config_var_strict(self):
861
self.set_config_push_strict('true')
862
self.assertPushFails([])
864
def test_push_bogus_config_var_ignored(self):
865
self.set_config_push_strict("I don't want you to be strict")
866
self.assertPushSucceeds([], with_warning=True)
868
def test_push_no_strict_command_line_override_config(self):
869
self.set_config_push_strict('yES')
870
self.assertPushFails([])
871
self.assertPushSucceeds(['--no-strict'])
873
def test_push_strict_command_line_override_config(self):
874
self.set_config_push_strict('oFF')
875
self.assertPushFails(['--strict'])
876
self.assertPushSucceeds([])
879
class TestPushForeign(tests.TestCaseWithTransport):
882
super(TestPushForeign, self).setUp()
883
test_foreign.register_dummy_foreign_for_test(self)
885
def make_dummy_builder(self, relpath):
886
builder = self.make_branch_builder(
887
relpath, format=test_foreign.DummyForeignVcsDirFormat())
888
builder.build_snapshot('revid', None,
889
[('add', ('', 'TREE_ROOT', 'directory', None)),
890
('add', ('foo', 'fooid', 'file', 'bar'))])
893
def test_no_roundtripping(self):
894
target_branch = self.make_dummy_builder('dp').get_branch()
895
source_tree = self.make_branch_and_tree("dc")
896
output, error = self.run_bzr("push -d dc dp", retcode=3)
897
self.assertEqual("", output)
898
self.assertEqual(error, "brz: ERROR: It is not possible to losslessly"
899
" push to dummy. You may want to use dpush instead.\n")
902
class TestPushOutput(script.TestCaseWithTransportAndScript):
904
def test_push_log_format(self):
907
Created a standalone tree (format: 2a)
912
$ brz commit -m 'we need some foo'
913
2>Committing to:...trunk/
915
2>Committed revision 1.
916
$ brz init ../feature
917
Created a standalone tree (format: 2a)
918
$ brz push -v ../feature -Olog_format=line
920
1: jrandom@example.com ...we need some foo
921
2>All changes applied successfully.
922
2>Pushed up to revision 1.
925
def test_push_with_revspec(self):
928
Shared repository with trees (format: 2a)
932
Created a repository tree (format: 2a)
933
Using shared repository...
935
$ brz commit -m 'first rev' --unchanged
936
2>Committing to:...trunk/
937
2>Committed revision 1.
941
$ brz commit -m 'we need some foo'
942
2>Committing to:...trunk/
944
2>Committed revision 2.
945
$ brz push -r 1 ../other
946
2>Created new branch.
947
$ brz st ../other # checking that file is not created (#484516)