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=b'a-1')
212
f = open('repo/a/file', 'ab')
213
f.write(b'more stuff\n')
215
tree_a.commit('commit a-2', rev_id=b'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'b-1')
222
self.assertTrue(shared_repo.has_revision(b'a-1'))
223
self.assertTrue(shared_repo.has_revision(b'a-2'))
224
self.assertTrue(shared_repo.has_revision(b'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(b'a-1'))
232
self.assertFalse(pushed_repo.has_revision(b'a-2'))
233
self.assertTrue(pushed_repo.has_revision(b'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', b'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(None, [
258
('add', ('', b'root-id', 'directory', None))],
260
source.build_snapshot([b'A'], [], revision_id=b'B')
261
source.build_snapshot([b'A'], [], revision_id=b'C')
262
source.finish_series()
263
self.run_bzr('push target -d source')
264
self.addCleanup(target_repo.lock_read().unlock)
265
# We should have pushed 'C', but not 'B', since it isn't in the
267
self.assertEqual([(b'A',), (b'C',)], sorted(
268
target_repo.revisions.keys()))
270
def test_push_smart_non_stacked_streaming_acceptance(self):
271
self.setup_smart_server_with_call_log()
272
t = self.make_branch_and_tree('from')
273
t.commit(allow_pointless=True, message='first commit')
274
self.reset_smart_call_log()
275
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
276
# This figure represent the amount of work to perform this use case. It
277
# is entirely ok to reduce this number if a test fails due to rpc_count
278
# being too low. If rpc_count increases, more network roundtrips have
279
# become necessary for this use case. Please do not adjust this number
280
# upwards without agreement from bzr's network support maintainers.
281
self.assertLength(9, self.hpss_calls)
282
self.assertLength(1, self.hpss_connections)
283
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
285
def test_push_smart_stacked_streaming_acceptance(self):
286
self.setup_smart_server_with_call_log()
287
parent = self.make_branch_and_tree('parent', format='1.9')
288
parent.commit(message='first commit')
289
local = parent.controldir.sprout('local').open_workingtree()
290
local.commit(message='local commit')
291
self.reset_smart_call_log()
292
self.run_bzr(['push', '--stacked', '--stacked-on', '../parent',
293
self.get_url('public')], working_dir='local')
294
# This figure represent the amount of work to perform this use case. It
295
# is entirely ok to reduce this number if a test fails due to rpc_count
296
# being too low. If rpc_count increases, more network roundtrips have
297
# become necessary for this use case. Please do not adjust this number
298
# upwards without agreement from bzr's network support maintainers.
299
self.assertLength(15, self.hpss_calls)
300
self.assertLength(1, self.hpss_connections)
301
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
302
remote = branch.Branch.open('public')
303
self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
305
def test_push_smart_tags_streaming_acceptance(self):
306
self.setup_smart_server_with_call_log()
307
t = self.make_branch_and_tree('from')
308
rev_id = t.commit(allow_pointless=True, message='first commit')
309
t.branch.tags.set_tag('new-tag', rev_id)
310
self.reset_smart_call_log()
311
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
312
# This figure represent the amount of work to perform this use case. It
313
# is entirely ok to reduce this number if a test fails due to rpc_count
314
# being too low. If rpc_count increases, more network roundtrips have
315
# become necessary for this use case. Please do not adjust this number
316
# upwards without agreement from bzr's network support maintainers.
317
self.assertLength(11, self.hpss_calls)
318
self.assertLength(1, self.hpss_connections)
319
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
321
def test_push_smart_incremental_acceptance(self):
322
self.setup_smart_server_with_call_log()
323
t = self.make_branch_and_tree('from')
324
rev_id1 = t.commit(allow_pointless=True, message='first commit')
325
rev_id2 = t.commit(allow_pointless=True, message='second commit')
327
['push', self.get_url('to-one'), '-r1'], working_dir='from')
328
self.reset_smart_call_log()
329
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
330
# This figure represent the amount of work to perform this use case. It
331
# is entirely ok to reduce this number if a test fails due to rpc_count
332
# being too low. If rpc_count increases, more network roundtrips have
333
# become necessary for this use case. Please do not adjust this number
334
# upwards without agreement from bzr's network support maintainers.
335
self.assertLength(11, self.hpss_calls)
336
self.assertLength(1, self.hpss_connections)
337
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
339
def test_push_smart_with_default_stacking_url_path_segment(self):
340
# If the default stacked-on location is a path element then branches
341
# we push there over the smart server are stacked and their
342
# stacked_on_url is that exact path segment. Added to nail bug 385132.
343
self.setup_smart_server_with_call_log()
344
self.make_branch('stack-on', format='1.9')
345
self.make_controldir('.').get_config().set_default_stack_on(
347
self.make_branch('from', format='1.9')
348
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
349
b = branch.Branch.open(self.get_url('to'))
350
self.assertEqual('/extra/stack-on', b.get_stacked_on_url())
352
def test_push_smart_with_default_stacking_relative_path(self):
353
# If the default stacked-on location is a relative path then branches
354
# we push there over the smart server are stacked and their
355
# stacked_on_url is a relative path. Added to nail bug 385132.
356
self.setup_smart_server_with_call_log()
357
self.make_branch('stack-on', format='1.9')
358
self.make_controldir('.').get_config().set_default_stack_on('stack-on')
359
self.make_branch('from', format='1.9')
360
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
361
b = branch.Branch.open(self.get_url('to'))
362
self.assertEqual('../stack-on', b.get_stacked_on_url())
364
def create_simple_tree(self):
365
tree = self.make_branch_and_tree('tree')
366
self.build_tree(['tree/a'])
367
tree.add(['a'], [b'a-id'])
368
tree.commit('one', rev_id=b'r1')
371
def test_push_create_prefix(self):
372
"""'brz push --create-prefix' will create leading directories."""
373
tree = self.create_simple_tree()
375
self.run_bzr_error(['Parent directory of ../new/tree does not exist'],
378
self.run_bzr('push ../new/tree --create-prefix',
380
new_tree = workingtree.WorkingTree.open('new/tree')
381
self.assertEqual(tree.last_revision(), new_tree.last_revision())
382
self.assertPathExists('new/tree/a')
384
def test_push_use_existing(self):
385
"""'brz push --use-existing-dir' can push into an existing dir.
387
By default, 'brz push' will not use an existing, non-versioned dir.
389
tree = self.create_simple_tree()
390
self.build_tree(['target/'])
392
self.run_bzr_error(['Target directory ../target already exists',
393
'Supply --use-existing-dir',
395
'push ../target', working_dir='tree')
397
self.run_bzr('push --use-existing-dir ../target',
400
new_tree = workingtree.WorkingTree.open('target')
401
self.assertEqual(tree.last_revision(), new_tree.last_revision())
402
# The push should have created target/a
403
self.assertPathExists('target/a')
405
def test_push_use_existing_into_empty_bzrdir(self):
406
"""'brz push --use-existing-dir' into a dir with an empty .bzr dir
409
tree = self.create_simple_tree()
410
self.build_tree(['target/', 'target/.bzr/'])
412
['Target directory ../target already contains a .bzr directory, '
413
'but it is not valid.'],
414
'push ../target --use-existing-dir', working_dir='tree')
416
def test_push_onto_repo(self):
417
"""We should be able to 'brz push' into an existing bzrdir."""
418
tree = self.create_simple_tree()
419
repo = self.make_repository('repo', shared=True)
421
self.run_bzr('push ../repo',
424
# Pushing onto an existing bzrdir will create a repository and
425
# branch as needed, but will only create a working tree if there was
427
self.assertRaises(errors.NoWorkingTree,
428
workingtree.WorkingTree.open, 'repo')
429
new_branch = branch.Branch.open('repo')
430
self.assertEqual(tree.last_revision(), new_branch.last_revision())
432
def test_push_onto_just_bzrdir(self):
433
"""We don't handle when the target is just a bzrdir.
435
Because you shouldn't be able to create *just* a bzrdir in the wild.
437
# TODO: jam 20070109 Maybe it would be better to create the repository
439
tree = self.create_simple_tree()
440
a_controldir = self.make_controldir('dir')
442
self.run_bzr_error(['At ../dir you have a valid .bzr control'],
446
def test_push_with_revisionspec(self):
447
"""We should be able to push a revision older than the tip."""
448
tree_from = self.make_branch_and_tree('from')
449
tree_from.commit("One.", rev_id=b"from-1")
450
tree_from.commit("Two.", rev_id=b"from-2")
452
self.run_bzr('push -r1 ../to', working_dir='from')
454
tree_to = workingtree.WorkingTree.open('to')
455
repo_to = tree_to.branch.repository
456
self.assertTrue(repo_to.has_revision(b'from-1'))
457
self.assertFalse(repo_to.has_revision(b'from-2'))
458
self.assertEqual(tree_to.branch.last_revision_info()[1], b'from-1')
460
tree_to.changes_from(tree_to.basis_tree()).has_changed())
463
['brz: ERROR: brz push --revision '
464
'takes exactly one revision identifier\n'],
465
'push -r0..2 ../to', working_dir='from')
467
def create_trunk_and_feature_branch(self):
469
trunk_tree = self.make_branch_and_tree('target',
471
trunk_tree.commit('mainline')
472
# and a branch from it
473
branch_tree = self.make_branch_and_tree('branch',
475
branch_tree.pull(trunk_tree.branch)
476
branch_tree.branch.set_parent(trunk_tree.branch.base)
477
# with some work on it
478
branch_tree.commit('moar work plz')
479
return trunk_tree, branch_tree
481
def assertPublished(self, branch_revid, stacked_on):
482
"""Assert that the branch 'published' has been published correctly."""
483
published_branch = branch.Branch.open('published')
484
# The published branch refers to the mainline
485
self.assertEqual(stacked_on, published_branch.get_stacked_on_url())
486
# and the branch's work was pushed
487
self.assertTrue(published_branch.repository.has_revision(branch_revid))
489
def test_push_new_branch_stacked_on(self):
490
"""Pushing a new branch with --stacked-on creates a stacked branch."""
491
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
492
# we publish branch_tree with a reference to the mainline.
493
out, err = self.run_bzr(['push', '--stacked-on', trunk_tree.branch.base,
494
self.get_url('published')], working_dir='branch')
495
self.assertEqual('', out)
496
self.assertEqual('Created new stacked branch referring to %s.\n' %
497
trunk_tree.branch.base, err)
498
self.assertPublished(branch_tree.last_revision(),
499
trunk_tree.branch.base)
501
def test_push_new_branch_stacked_uses_parent_when_no_public_url(self):
502
"""When the parent has no public url the parent is used as-is."""
503
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
504
# now we do a stacked push, which should determine the public location
506
out, err = self.run_bzr(['push', '--stacked',
507
self.get_url('published')], working_dir='branch')
508
self.assertEqual('', out)
509
self.assertEqual('Created new stacked branch referring to %s.\n' %
510
trunk_tree.branch.base, err)
511
self.assertPublished(branch_tree.last_revision(),
512
trunk_tree.branch.base)
514
def test_push_new_branch_stacked_uses_parent_public(self):
515
"""Pushing a new branch with --stacked creates a stacked branch."""
516
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
517
# the trunk is published on a web server
518
self.transport_readonly_server = http_server.HttpServer
519
trunk_public = self.make_branch('public_trunk', format='1.9')
520
trunk_public.pull(trunk_tree.branch)
521
trunk_public_url = self.get_readonly_url('public_trunk')
522
br = trunk_tree.branch
523
br.set_public_branch(trunk_public_url)
524
# now we do a stacked push, which should determine the public location
526
out, err = self.run_bzr(['push', '--stacked',
527
self.get_url('published')], working_dir='branch')
528
self.assertEqual('', out)
529
self.assertEqual('Created new stacked branch referring to %s.\n' %
530
trunk_public_url, err)
531
self.assertPublished(branch_tree.last_revision(), trunk_public_url)
533
def test_push_new_branch_stacked_no_parent(self):
534
"""Pushing with --stacked and no parent branch errors."""
535
branch = self.make_branch_and_tree('branch', format='1.9')
536
# now we do a stacked push, which should fail as the place to refer too
537
# cannot be determined.
538
out, err = self.run_bzr_error(
539
['Could not determine branch to refer to\\.'], ['push', '--stacked',
540
self.get_url('published')], working_dir='branch')
541
self.assertEqual('', out)
542
self.assertFalse(self.get_transport('published').has('.'))
544
def test_push_notifies_default_stacking(self):
545
self.make_branch('stack_on', format='1.6')
546
self.make_controldir('.').get_config().set_default_stack_on('stack_on')
547
self.make_branch('from', format='1.6')
548
out, err = self.run_bzr('push -d from to')
549
self.assertContainsRe(err,
550
'Using default stacking branch stack_on at .*')
552
def test_push_stacks_with_default_stacking_if_target_is_stackable(self):
553
self.make_branch('stack_on', format='1.6')
554
self.make_controldir('.').get_config().set_default_stack_on('stack_on')
555
self.make_branch('from', format='pack-0.92')
556
out, err = self.run_bzr('push -d from to')
557
b = branch.Branch.open('to')
558
self.assertEqual('../stack_on', b.get_stacked_on_url())
560
def test_push_does_not_change_format_with_default_if_target_cannot(self):
561
self.make_branch('stack_on', format='pack-0.92')
562
self.make_controldir('.').get_config().set_default_stack_on('stack_on')
563
self.make_branch('from', format='pack-0.92')
564
out, err = self.run_bzr('push -d from to')
565
b = branch.Branch.open('to')
566
self.assertRaises(branch.UnstackableBranchFormat, b.get_stacked_on_url)
568
def test_push_doesnt_create_broken_branch(self):
569
"""Pushing a new standalone branch works even when there's a default
570
stacking policy at the destination.
572
The new branch will preserve the repo format (even if it isn't the
573
default for the branch), and will be stacked when the repo format
574
allows (which means that the branch format isn't necessarly preserved).
576
self.make_repository('repo', shared=True, format='1.6')
577
builder = self.make_branch_builder('repo/local', format='pack-0.92')
578
builder.start_series()
579
builder.build_snapshot(None, [
580
('add', ('', b'root-id', 'directory', '')),
581
('add', ('filename', b'f-id', 'file', b'content\n'))],
582
revision_id=b'rev-1')
583
builder.build_snapshot([b'rev-1'], [], revision_id=b'rev-2')
584
builder.build_snapshot([b'rev-2'],
585
[('modify', ('filename', b'new-content\n'))],
586
revision_id=b'rev-3')
587
builder.finish_series()
588
branch = builder.get_branch()
589
# Push rev-1 to "trunk", so that we can stack on it.
590
self.run_bzr('push -d repo/local trunk -r 1')
591
# Set a default stacking policy so that new branches will automatically
593
self.make_controldir('.').get_config().set_default_stack_on('trunk')
594
# Push rev-2 to a new branch "remote". It will be stacked on "trunk".
595
out, err = self.run_bzr('push -d repo/local remote -r 2')
596
self.assertContainsRe(
597
err, 'Using default stacking branch trunk at .*')
598
# Push rev-3 onto "remote". If "remote" not stacked and is missing the
599
# fulltext record for f-id @ rev-1, then this will fail.
600
out, err = self.run_bzr('push -d repo/local remote -r 3')
602
def test_push_verbose_shows_log(self):
603
tree = self.make_branch_and_tree('source')
605
out, err = self.run_bzr('push -v -d source target')
606
# initial push contains log
607
self.assertContainsRe(out, 'rev1')
609
out, err = self.run_bzr('push -v -d source target')
610
# subsequent push contains log
611
self.assertContainsRe(out, 'rev2')
612
# subsequent log is accurate
613
self.assertNotContainsRe(out, 'rev1')
615
def test_push_from_subdir(self):
616
t = self.make_branch_and_tree('tree')
617
self.build_tree(['tree/dir/', 'tree/dir/file'])
618
t.add(['dir', 'dir/file'])
620
out, err = self.run_bzr('push ../../pushloc', working_dir='tree/dir')
621
self.assertEqual('', out)
622
self.assertEqual('Created new branch.\n', err)
624
def test_overwrite_tags(self):
625
"""--overwrite-tags only overwrites tags, not revisions."""
626
from_tree = self.make_branch_and_tree('from')
627
from_tree.branch.tags.set_tag("mytag", b"somerevid")
628
to_tree = self.make_branch_and_tree('to')
629
to_tree.branch.tags.set_tag("mytag", b"anotherrevid")
630
revid1 = to_tree.commit('my commit')
631
out = self.run_bzr(['push', '-d', 'from', 'to'])
632
self.assertEqual(out,
633
('Conflicting tags:\n mytag\n', 'No new revisions to push.\n'))
634
out = self.run_bzr(['push', '-d', 'from', '--overwrite-tags', 'to'])
635
self.assertEqual(out, ('', '1 tag updated.\n'))
636
self.assertEqual(to_tree.branch.tags.lookup_tag('mytag'),
638
self.assertEqual(to_tree.branch.last_revision(), revid1)
641
class RedirectingMemoryTransport(memory.MemoryTransport):
643
def mkdir(self, relpath, mode=None):
644
if self._cwd == '/source/':
645
raise errors.RedirectRequested(self.abspath(relpath),
646
self.abspath('../target'),
648
elif self._cwd == '/infinite-loop/':
649
raise errors.RedirectRequested(self.abspath(relpath),
650
self.abspath('../infinite-loop'),
653
return super(RedirectingMemoryTransport, self).mkdir(
656
def get(self, relpath):
657
if self.clone(relpath)._cwd == '/infinite-loop/':
658
raise errors.RedirectRequested(self.abspath(relpath),
659
self.abspath('../infinite-loop'),
662
return super(RedirectingMemoryTransport, self).get(relpath)
664
def _redirected_to(self, source, target):
665
# We do accept redirections
666
return transport.get_transport(target)
669
class RedirectingMemoryServer(memory.MemoryServer):
671
def start_server(self):
672
self._dirs = {'/': None}
675
self._scheme = 'redirecting-memory+%s:///' % id(self)
676
transport.register_transport(self._scheme, self._memory_factory)
678
def _memory_factory(self, url):
679
result = RedirectingMemoryTransport(url)
680
result._dirs = self._dirs
681
result._files = self._files
682
result._locks = self._locks
685
def stop_server(self):
686
transport.unregister_transport(self._scheme, self._memory_factory)
689
class TestPushRedirect(tests.TestCaseWithTransport):
692
super(TestPushRedirect, self).setUp()
693
self.memory_server = RedirectingMemoryServer()
694
self.start_server(self.memory_server)
695
# Make the branch and tree that we'll be pushing.
696
t = self.make_branch_and_tree('tree')
697
self.build_tree(['tree/file'])
701
def test_push_redirects_on_mkdir(self):
702
"""If the push requires a mkdir, push respects redirect requests.
704
This is added primarily to handle lp:/ URI support, so that users can
705
push to new branches by specifying lp:/ URIs.
707
destination_url = self.memory_server.get_url() + 'source'
708
self.run_bzr(['push', '-d', 'tree', destination_url])
710
local_revision = branch.Branch.open('tree').last_revision()
711
remote_revision = branch.Branch.open(
712
self.memory_server.get_url() + 'target').last_revision()
713
self.assertEqual(remote_revision, local_revision)
715
def test_push_gracefully_handles_too_many_redirects(self):
716
"""Push fails gracefully if the mkdir generates a large number of
719
destination_url = self.memory_server.get_url() + 'infinite-loop'
720
out, err = self.run_bzr_error(
721
['Too many redirections trying to make %s\\.\n'
722
% re.escape(destination_url)],
723
['push', '-d', 'tree', destination_url], retcode=3)
724
self.assertEqual('', out)
727
class TestPushStrictMixin(object):
729
def make_local_branch_and_tree(self):
730
self.tree = self.make_branch_and_tree('local')
731
self.build_tree_contents([('local/file', b'initial')])
732
self.tree.add('file')
733
self.tree.commit('adding file', rev_id=b'added')
734
self.build_tree_contents([('local/file', b'modified')])
735
self.tree.commit('modify file', rev_id=b'modified')
737
def set_config_push_strict(self, value):
738
br = branch.Branch.open('local')
739
br.get_config_stack().set('push_strict', value)
741
_default_command = ['push', '../to']
742
_default_wd = 'local'
743
_default_errors = ['Working tree ".*/local/" has uncommitted '
744
'changes \\(See brz status\\)\\.', ]
745
_default_additional_error = 'Use --no-strict to force the push.\n'
746
_default_additional_warning = 'Uncommitted changes will not be pushed.'
748
def assertPushFails(self, args):
749
out, err = self.run_bzr_error(self._default_errors,
750
self._default_command + args,
751
working_dir=self._default_wd, retcode=3)
752
self.assertContainsRe(err, self._default_additional_error)
754
def assertPushSucceeds(self, args, with_warning=False, revid_to_push=None):
756
error_regexes = self._default_errors
759
out, err = self.run_bzr(self._default_command + args,
760
working_dir=self._default_wd,
761
error_regexes=error_regexes)
763
self.assertContainsRe(err, self._default_additional_warning)
765
self.assertNotContainsRe(err, self._default_additional_warning)
766
branch_from = branch.Branch.open(self._default_wd)
767
if revid_to_push is None:
768
revid_to_push = branch_from.last_revision()
769
branch_to = branch.Branch.open('to')
770
repo_to = branch_to.repository
771
self.assertTrue(repo_to.has_revision(revid_to_push))
772
self.assertEqual(revid_to_push, branch_to.last_revision())
775
class TestPushStrictWithoutChanges(tests.TestCaseWithTransport,
776
TestPushStrictMixin):
779
super(TestPushStrictWithoutChanges, self).setUp()
780
self.make_local_branch_and_tree()
782
def test_push_default(self):
783
self.assertPushSucceeds([])
785
def test_push_strict(self):
786
self.assertPushSucceeds(['--strict'])
788
def test_push_no_strict(self):
789
self.assertPushSucceeds(['--no-strict'])
791
def test_push_config_var_strict(self):
792
self.set_config_push_strict('true')
793
self.assertPushSucceeds([])
795
def test_push_config_var_no_strict(self):
796
self.set_config_push_strict('false')
797
self.assertPushSucceeds([])
800
strict_push_change_scenarios = [
802
dict(_changes_type='_uncommitted_changes')),
804
dict(_changes_type='_pending_merges')),
805
('out-of-sync-trees',
806
dict(_changes_type='_out_of_sync_trees')),
810
class TestPushStrictWithChanges(tests.TestCaseWithTransport,
811
TestPushStrictMixin):
813
scenarios = strict_push_change_scenarios
814
_changes_type = None # Set by load_tests
817
super(TestPushStrictWithChanges, self).setUp()
818
# Apply the changes defined in load_tests: one of _uncommitted_changes,
819
# _pending_merges or _out_of_sync_trees
820
getattr(self, self._changes_type)()
822
def _uncommitted_changes(self):
823
self.make_local_branch_and_tree()
824
# Make a change without committing it
825
self.build_tree_contents([('local/file', b'in progress')])
827
def _pending_merges(self):
828
self.make_local_branch_and_tree()
829
# Create 'other' branch containing a new file
830
other_bzrdir = self.tree.controldir.sprout('other')
831
other_tree = other_bzrdir.open_workingtree()
832
self.build_tree_contents([('other/other-file', b'other')])
833
other_tree.add('other-file')
834
other_tree.commit('other commit', rev_id=b'other')
835
# Merge and revert, leaving a pending merge
836
self.tree.merge_from_branch(other_tree.branch)
837
self.tree.revert(filenames=['other-file'], backups=False)
839
def _out_of_sync_trees(self):
840
self.make_local_branch_and_tree()
841
self.run_bzr(['checkout', '--lightweight', 'local', 'checkout'])
842
# Make a change and commit it
843
self.build_tree_contents([('local/file', b'modified in local')])
844
self.tree.commit('modify file', rev_id=b'modified-in-local')
845
# Exercise commands from the checkout directory
846
self._default_wd = 'checkout'
847
self._default_errors = ["Working tree is out of date, please run"
848
" 'brz update'\\.", ]
850
def test_push_default(self):
851
self.assertPushSucceeds([], with_warning=True)
853
def test_push_with_revision(self):
854
self.assertPushSucceeds(['-r', 'revid:added'], revid_to_push=b'added')
856
def test_push_no_strict(self):
857
self.assertPushSucceeds(['--no-strict'])
859
def test_push_strict_with_changes(self):
860
self.assertPushFails(['--strict'])
862
def test_push_respect_config_var_strict(self):
863
self.set_config_push_strict('true')
864
self.assertPushFails([])
866
def test_push_bogus_config_var_ignored(self):
867
self.set_config_push_strict("I don't want you to be strict")
868
self.assertPushSucceeds([], with_warning=True)
870
def test_push_no_strict_command_line_override_config(self):
871
self.set_config_push_strict('yES')
872
self.assertPushFails([])
873
self.assertPushSucceeds(['--no-strict'])
875
def test_push_strict_command_line_override_config(self):
876
self.set_config_push_strict('oFF')
877
self.assertPushFails(['--strict'])
878
self.assertPushSucceeds([])
881
class TestPushForeign(tests.TestCaseWithTransport):
884
super(TestPushForeign, self).setUp()
885
test_foreign.register_dummy_foreign_for_test(self)
887
def make_dummy_builder(self, relpath):
888
builder = self.make_branch_builder(
889
relpath, format=test_foreign.DummyForeignVcsDirFormat())
890
builder.build_snapshot(None,
891
[('add', ('', b'TREE_ROOT', 'directory', None)),
892
('add', ('foo', b'fooid', 'file', b'bar'))],
893
revision_id=b'revid')
896
def test_no_roundtripping(self):
897
target_branch = self.make_dummy_builder('dp').get_branch()
898
source_tree = self.make_branch_and_tree("dc")
899
output, error = self.run_bzr("push -d dc dp", retcode=3)
900
self.assertEqual("", output)
903
"brz: ERROR: It is not possible to losslessly"
904
" push to dummy. You may want to use --lossy.\n")
907
class TestPushOutput(script.TestCaseWithTransportAndScript):
909
def test_push_log_format(self):
912
Created a standalone tree (format: 2a)
917
$ brz commit -m 'we need some foo'
918
2>Committing to:...trunk/
920
2>Committed revision 1.
921
$ brz init ../feature
922
Created a standalone tree (format: 2a)
923
$ brz push -v ../feature -Olog_format=line
925
1: jrandom@example.com ...we need some foo
926
2>All changes applied successfully.
927
2>Pushed up to revision 1.
930
def test_push_with_revspec(self):
933
Shared repository with trees (format: 2a)
937
Created a repository tree (format: 2a)
938
Using shared repository...
940
$ brz commit -m 'first rev' --unchanged
941
2>Committing to:...trunk/
942
2>Committed revision 1.
946
$ brz commit -m 'we need some foo'
947
2>Committing to:...trunk/
949
2>Committed revision 2.
950
$ brz push -r 1 ../other
951
2>Created new branch.
952
$ brz st ../other # checking that file is not created (#484516)