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."""
34
from breezy.bzr import (
37
from breezy.bzr import knitrepo
38
from breezy.tests import (
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_with_default_stacking_url_path_segment(self):
271
# If the default stacked-on location is a path element then branches
272
# we push there over the smart server are stacked and their
273
# stacked_on_url is that exact path segment. Added to nail bug 385132.
274
self.setup_smart_server_with_call_log()
275
self.make_branch('stack-on', format='1.9')
276
self.make_controldir('.').get_config().set_default_stack_on(
278
self.make_branch('from', format='1.9')
279
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
280
b = branch.Branch.open(self.get_url('to'))
281
self.assertEqual('/extra/stack-on', b.get_stacked_on_url())
283
def test_push_smart_with_default_stacking_relative_path(self):
284
# If the default stacked-on location is a relative path then branches
285
# we push there over the smart server are stacked and their
286
# stacked_on_url is a relative path. Added to nail bug 385132.
287
self.setup_smart_server_with_call_log()
288
self.make_branch('stack-on', format='1.9')
289
self.make_controldir('.').get_config().set_default_stack_on('stack-on')
290
self.make_branch('from', format='1.9')
291
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
292
b = branch.Branch.open(self.get_url('to'))
293
self.assertEqual('../stack-on', b.get_stacked_on_url())
295
def create_simple_tree(self):
296
tree = self.make_branch_and_tree('tree')
297
self.build_tree(['tree/a'])
298
tree.add(['a'], [b'a-id'])
299
tree.commit('one', rev_id=b'r1')
302
def test_push_create_prefix(self):
303
"""'brz push --create-prefix' will create leading directories."""
304
tree = self.create_simple_tree()
306
self.run_bzr_error(['Parent directory of ../new/tree does not exist'],
309
self.run_bzr('push ../new/tree --create-prefix',
311
new_tree = workingtree.WorkingTree.open('new/tree')
312
self.assertEqual(tree.last_revision(), new_tree.last_revision())
313
self.assertPathExists('new/tree/a')
315
def test_push_use_existing(self):
316
"""'brz push --use-existing-dir' can push into an existing dir.
318
By default, 'brz push' will not use an existing, non-versioned dir.
320
tree = self.create_simple_tree()
321
self.build_tree(['target/'])
323
self.run_bzr_error(['Target directory ../target already exists',
324
'Supply --use-existing-dir',
326
'push ../target', working_dir='tree')
328
self.run_bzr('push --use-existing-dir ../target',
331
new_tree = workingtree.WorkingTree.open('target')
332
self.assertEqual(tree.last_revision(), new_tree.last_revision())
333
# The push should have created target/a
334
self.assertPathExists('target/a')
336
def test_push_use_existing_into_empty_bzrdir(self):
337
"""'brz push --use-existing-dir' into a dir with an empty .bzr dir
340
tree = self.create_simple_tree()
341
self.build_tree(['target/', 'target/.bzr/'])
343
['Target directory ../target already contains a .bzr directory, '
344
'but it is not valid.'],
345
'push ../target --use-existing-dir', working_dir='tree')
347
def test_push_onto_repo(self):
348
"""We should be able to 'brz push' into an existing bzrdir."""
349
tree = self.create_simple_tree()
350
repo = self.make_repository('repo', shared=True)
352
self.run_bzr('push ../repo',
355
# Pushing onto an existing bzrdir will create a repository and
356
# branch as needed, but will only create a working tree if there was
358
self.assertRaises(errors.NoWorkingTree,
359
workingtree.WorkingTree.open, 'repo')
360
new_branch = branch.Branch.open('repo')
361
self.assertEqual(tree.last_revision(), new_branch.last_revision())
363
def test_push_onto_just_bzrdir(self):
364
"""We don't handle when the target is just a bzrdir.
366
Because you shouldn't be able to create *just* a bzrdir in the wild.
368
# TODO: jam 20070109 Maybe it would be better to create the repository
370
tree = self.create_simple_tree()
371
a_controldir = self.make_controldir('dir')
373
self.run_bzr_error(['At ../dir you have a valid .bzr control'],
377
def test_push_with_revisionspec(self):
378
"""We should be able to push a revision older than the tip."""
379
tree_from = self.make_branch_and_tree('from')
380
tree_from.commit("One.", rev_id=b"from-1")
381
tree_from.commit("Two.", rev_id=b"from-2")
383
self.run_bzr('push -r1 ../to', working_dir='from')
385
tree_to = workingtree.WorkingTree.open('to')
386
repo_to = tree_to.branch.repository
387
self.assertTrue(repo_to.has_revision(b'from-1'))
388
self.assertFalse(repo_to.has_revision(b'from-2'))
389
self.assertEqual(tree_to.branch.last_revision_info()[1], b'from-1')
391
tree_to.changes_from(tree_to.basis_tree()).has_changed())
394
['brz: ERROR: brz push --revision '
395
'takes exactly one revision identifier\n'],
396
'push -r0..2 ../to', working_dir='from')
398
def create_trunk_and_feature_branch(self):
400
trunk_tree = self.make_branch_and_tree('target',
402
trunk_tree.commit('mainline')
403
# and a branch from it
404
branch_tree = self.make_branch_and_tree('branch',
406
branch_tree.pull(trunk_tree.branch)
407
branch_tree.branch.set_parent(trunk_tree.branch.base)
408
# with some work on it
409
branch_tree.commit('moar work plz')
410
return trunk_tree, branch_tree
412
def assertPublished(self, branch_revid, stacked_on):
413
"""Assert that the branch 'published' has been published correctly."""
414
published_branch = branch.Branch.open('published')
415
# The published branch refers to the mainline
416
self.assertEqual(stacked_on, published_branch.get_stacked_on_url())
417
# and the branch's work was pushed
418
self.assertTrue(published_branch.repository.has_revision(branch_revid))
420
def test_push_new_branch_stacked_on(self):
421
"""Pushing a new branch with --stacked-on creates a stacked branch."""
422
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
423
# we publish branch_tree with a reference to the mainline.
424
out, err = self.run_bzr(['push', '--stacked-on', trunk_tree.branch.base,
425
self.get_url('published')], working_dir='branch')
426
self.assertEqual('', out)
427
self.assertEqual('Created new stacked branch referring to %s.\n' %
428
trunk_tree.branch.base, err)
429
self.assertPublished(branch_tree.last_revision(),
430
trunk_tree.branch.base)
432
def test_push_new_branch_stacked_on(self):
433
"""Pushing a new branch with --stacked-on can use directory URLs."""
434
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
435
class FooDirectory(object):
436
def look_up(self, name, url, purpose=None):
438
return trunk_tree.branch.base
440
directory_service.directories.register('foo:', FooDirectory, 'Foo directory')
441
self.addCleanup(directory_service.directories.remove, 'foo:')
442
# we publish branch_tree with a reference to the mainline.
443
out, err = self.run_bzr(['push', '--stacked-on', 'foo:',
444
self.get_url('published')], working_dir='branch')
445
self.assertEqual('', out)
446
self.assertEqual('Created new stacked branch referring to %s.\n' %
447
trunk_tree.branch.base, err)
448
self.assertPublished(branch_tree.last_revision(),
449
trunk_tree.branch.base)
451
def test_push_new_branch_stacked_uses_parent_when_no_public_url(self):
452
"""When the parent has no public url the parent is used as-is."""
453
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
454
# now we do a stacked push, which should determine the public location
456
out, err = self.run_bzr(['push', '--stacked',
457
self.get_url('published')], working_dir='branch')
458
self.assertEqual('', out)
459
self.assertEqual('Created new stacked branch referring to %s.\n' %
460
trunk_tree.branch.base, err)
461
self.assertPublished(branch_tree.last_revision(),
462
trunk_tree.branch.base)
464
def test_push_new_branch_stacked_uses_parent_public(self):
465
"""Pushing a new branch with --stacked creates a stacked branch."""
466
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
467
# the trunk is published on a web server
468
self.transport_readonly_server = http_server.HttpServer
469
trunk_public = self.make_branch('public_trunk', format='1.9')
470
trunk_public.pull(trunk_tree.branch)
471
trunk_public_url = self.get_readonly_url('public_trunk')
472
br = trunk_tree.branch
473
br.set_public_branch(trunk_public_url)
474
# now we do a stacked push, which should determine the public location
476
out, err = self.run_bzr(['push', '--stacked',
477
self.get_url('published')], working_dir='branch')
478
self.assertEqual('', out)
479
self.assertEqual('Created new stacked branch referring to %s.\n' %
480
trunk_public_url, err)
481
self.assertPublished(branch_tree.last_revision(), trunk_public_url)
483
def test_push_new_branch_stacked_no_parent(self):
484
"""Pushing with --stacked and no parent branch errors."""
485
branch = self.make_branch_and_tree('branch', format='1.9')
486
# now we do a stacked push, which should fail as the place to refer too
487
# cannot be determined.
488
out, err = self.run_bzr_error(
489
['Could not determine branch to refer to\\.'], ['push', '--stacked',
490
self.get_url('published')], working_dir='branch')
491
self.assertEqual('', out)
492
self.assertFalse(self.get_transport('published').has('.'))
494
def test_push_notifies_default_stacking(self):
495
self.make_branch('stack_on', format='1.6')
496
self.make_controldir('.').get_config().set_default_stack_on('stack_on')
497
self.make_branch('from', format='1.6')
498
out, err = self.run_bzr('push -d from to')
499
self.assertContainsRe(err,
500
'Using default stacking branch stack_on at .*')
502
def test_push_stacks_with_default_stacking_if_target_is_stackable(self):
503
self.make_branch('stack_on', format='1.6')
504
self.make_controldir('.').get_config().set_default_stack_on('stack_on')
505
self.make_branch('from', format='pack-0.92')
506
out, err = self.run_bzr('push -d from to')
507
b = branch.Branch.open('to')
508
self.assertEqual('../stack_on', b.get_stacked_on_url())
510
def test_push_does_not_change_format_with_default_if_target_cannot(self):
511
self.make_branch('stack_on', format='pack-0.92')
512
self.make_controldir('.').get_config().set_default_stack_on('stack_on')
513
self.make_branch('from', format='pack-0.92')
514
out, err = self.run_bzr('push -d from to')
515
b = branch.Branch.open('to')
516
self.assertRaises(branch.UnstackableBranchFormat, b.get_stacked_on_url)
518
def test_push_doesnt_create_broken_branch(self):
519
"""Pushing a new standalone branch works even when there's a default
520
stacking policy at the destination.
522
The new branch will preserve the repo format (even if it isn't the
523
default for the branch), and will be stacked when the repo format
524
allows (which means that the branch format isn't necessarly preserved).
526
self.make_repository('repo', shared=True, format='1.6')
527
builder = self.make_branch_builder('repo/local', format='pack-0.92')
528
builder.start_series()
529
builder.build_snapshot(None, [
530
('add', ('', b'root-id', 'directory', '')),
531
('add', ('filename', b'f-id', 'file', b'content\n'))],
532
revision_id=b'rev-1')
533
builder.build_snapshot([b'rev-1'], [], revision_id=b'rev-2')
534
builder.build_snapshot([b'rev-2'],
535
[('modify', ('filename', b'new-content\n'))],
536
revision_id=b'rev-3')
537
builder.finish_series()
538
branch = builder.get_branch()
539
# Push rev-1 to "trunk", so that we can stack on it.
540
self.run_bzr('push -d repo/local trunk -r 1')
541
# Set a default stacking policy so that new branches will automatically
543
self.make_controldir('.').get_config().set_default_stack_on('trunk')
544
# Push rev-2 to a new branch "remote". It will be stacked on "trunk".
545
out, err = self.run_bzr('push -d repo/local remote -r 2')
546
self.assertContainsRe(
547
err, 'Using default stacking branch trunk at .*')
548
# Push rev-3 onto "remote". If "remote" not stacked and is missing the
549
# fulltext record for f-id @ rev-1, then this will fail.
550
out, err = self.run_bzr('push -d repo/local remote -r 3')
552
def test_push_verbose_shows_log(self):
553
tree = self.make_branch_and_tree('source')
555
out, err = self.run_bzr('push -v -d source target')
556
# initial push contains log
557
self.assertContainsRe(out, 'rev1')
559
out, err = self.run_bzr('push -v -d source target')
560
# subsequent push contains log
561
self.assertContainsRe(out, 'rev2')
562
# subsequent log is accurate
563
self.assertNotContainsRe(out, 'rev1')
565
def test_push_from_subdir(self):
566
t = self.make_branch_and_tree('tree')
567
self.build_tree(['tree/dir/', 'tree/dir/file'])
568
t.add(['dir', 'dir/file'])
570
out, err = self.run_bzr('push ../../pushloc', working_dir='tree/dir')
571
self.assertEqual('', out)
572
self.assertEqual('Created new branch.\n', err)
574
def test_overwrite_tags(self):
575
"""--overwrite-tags only overwrites tags, not revisions."""
576
from_tree = self.make_branch_and_tree('from')
577
from_tree.branch.tags.set_tag("mytag", b"somerevid")
578
to_tree = self.make_branch_and_tree('to')
579
to_tree.branch.tags.set_tag("mytag", b"anotherrevid")
580
revid1 = to_tree.commit('my commit')
581
out = self.run_bzr(['push', '-d', 'from', 'to'])
582
self.assertEqual(out,
583
('Conflicting tags:\n mytag\n', 'No new revisions to push.\n'))
584
out = self.run_bzr(['push', '-d', 'from', '--overwrite-tags', 'to'])
585
self.assertEqual(out, ('', '1 tag updated.\n'))
586
self.assertEqual(to_tree.branch.tags.lookup_tag('mytag'),
588
self.assertEqual(to_tree.branch.last_revision(), revid1)
591
class RedirectingMemoryTransport(memory.MemoryTransport):
593
def mkdir(self, relpath, mode=None):
594
if self._cwd == '/source/':
595
raise errors.RedirectRequested(self.abspath(relpath),
596
self.abspath('../target'),
598
elif self._cwd == '/infinite-loop/':
599
raise errors.RedirectRequested(self.abspath(relpath),
600
self.abspath('../infinite-loop'),
603
return super(RedirectingMemoryTransport, self).mkdir(
606
def get(self, relpath):
607
if self.clone(relpath)._cwd == '/infinite-loop/':
608
raise errors.RedirectRequested(self.abspath(relpath),
609
self.abspath('../infinite-loop'),
612
return super(RedirectingMemoryTransport, self).get(relpath)
614
def _redirected_to(self, source, target):
615
# We do accept redirections
616
return transport.get_transport(target)
619
class RedirectingMemoryServer(memory.MemoryServer):
621
def start_server(self):
622
self._dirs = {'/': None}
625
self._scheme = 'redirecting-memory+%s:///' % id(self)
626
transport.register_transport(self._scheme, self._memory_factory)
628
def _memory_factory(self, url):
629
result = RedirectingMemoryTransport(url)
630
result._dirs = self._dirs
631
result._files = self._files
632
result._locks = self._locks
635
def stop_server(self):
636
transport.unregister_transport(self._scheme, self._memory_factory)
639
class TestPushRedirect(tests.TestCaseWithTransport):
642
super(TestPushRedirect, self).setUp()
643
self.memory_server = RedirectingMemoryServer()
644
self.start_server(self.memory_server)
645
# Make the branch and tree that we'll be pushing.
646
t = self.make_branch_and_tree('tree')
647
self.build_tree(['tree/file'])
651
def test_push_redirects_on_mkdir(self):
652
"""If the push requires a mkdir, push respects redirect requests.
654
This is added primarily to handle lp:/ URI support, so that users can
655
push to new branches by specifying lp:/ URIs.
657
destination_url = self.memory_server.get_url() + 'source'
658
self.run_bzr(['push', '-d', 'tree', destination_url])
660
local_revision = branch.Branch.open('tree').last_revision()
661
remote_revision = branch.Branch.open(
662
self.memory_server.get_url() + 'target').last_revision()
663
self.assertEqual(remote_revision, local_revision)
665
def test_push_gracefully_handles_too_many_redirects(self):
666
"""Push fails gracefully if the mkdir generates a large number of
669
destination_url = self.memory_server.get_url() + 'infinite-loop'
670
out, err = self.run_bzr_error(
671
['Too many redirections trying to make %s\\.\n'
672
% re.escape(destination_url)],
673
['push', '-d', 'tree', destination_url], retcode=3)
674
self.assertEqual('', out)
677
class TestPushStrictMixin(object):
679
def make_local_branch_and_tree(self):
680
self.tree = self.make_branch_and_tree('local')
681
self.build_tree_contents([('local/file', b'initial')])
682
self.tree.add('file')
683
self.tree.commit('adding file', rev_id=b'added')
684
self.build_tree_contents([('local/file', b'modified')])
685
self.tree.commit('modify file', rev_id=b'modified')
687
def set_config_push_strict(self, value):
688
br = branch.Branch.open('local')
689
br.get_config_stack().set('push_strict', value)
691
_default_command = ['push', '../to']
692
_default_wd = 'local'
693
_default_errors = ['Working tree ".*/local/" has uncommitted '
694
'changes \\(See brz status\\)\\.', ]
695
_default_additional_error = 'Use --no-strict to force the push.\n'
696
_default_additional_warning = 'Uncommitted changes will not be pushed.'
698
def assertPushFails(self, args):
699
out, err = self.run_bzr_error(self._default_errors,
700
self._default_command + args,
701
working_dir=self._default_wd, retcode=3)
702
self.assertContainsRe(err, self._default_additional_error)
704
def assertPushSucceeds(self, args, with_warning=False, revid_to_push=None):
706
error_regexes = self._default_errors
709
out, err = self.run_bzr(self._default_command + args,
710
working_dir=self._default_wd,
711
error_regexes=error_regexes)
713
self.assertContainsRe(err, self._default_additional_warning)
715
self.assertNotContainsRe(err, self._default_additional_warning)
716
branch_from = branch.Branch.open(self._default_wd)
717
if revid_to_push is None:
718
revid_to_push = branch_from.last_revision()
719
branch_to = branch.Branch.open('to')
720
repo_to = branch_to.repository
721
self.assertTrue(repo_to.has_revision(revid_to_push))
722
self.assertEqual(revid_to_push, branch_to.last_revision())
725
class TestPushStrictWithoutChanges(tests.TestCaseWithTransport,
726
TestPushStrictMixin):
729
super(TestPushStrictWithoutChanges, self).setUp()
730
self.make_local_branch_and_tree()
732
def test_push_default(self):
733
self.assertPushSucceeds([])
735
def test_push_strict(self):
736
self.assertPushSucceeds(['--strict'])
738
def test_push_no_strict(self):
739
self.assertPushSucceeds(['--no-strict'])
741
def test_push_config_var_strict(self):
742
self.set_config_push_strict('true')
743
self.assertPushSucceeds([])
745
def test_push_config_var_no_strict(self):
746
self.set_config_push_strict('false')
747
self.assertPushSucceeds([])
750
strict_push_change_scenarios = [
752
dict(_changes_type='_uncommitted_changes')),
754
dict(_changes_type='_pending_merges')),
755
('out-of-sync-trees',
756
dict(_changes_type='_out_of_sync_trees')),
760
class TestPushStrictWithChanges(tests.TestCaseWithTransport,
761
TestPushStrictMixin):
763
scenarios = strict_push_change_scenarios
764
_changes_type = None # Set by load_tests
767
super(TestPushStrictWithChanges, self).setUp()
768
# Apply the changes defined in load_tests: one of _uncommitted_changes,
769
# _pending_merges or _out_of_sync_trees
770
getattr(self, self._changes_type)()
772
def _uncommitted_changes(self):
773
self.make_local_branch_and_tree()
774
# Make a change without committing it
775
self.build_tree_contents([('local/file', b'in progress')])
777
def _pending_merges(self):
778
self.make_local_branch_and_tree()
779
# Create 'other' branch containing a new file
780
other_bzrdir = self.tree.controldir.sprout('other')
781
other_tree = other_bzrdir.open_workingtree()
782
self.build_tree_contents([('other/other-file', b'other')])
783
other_tree.add('other-file')
784
other_tree.commit('other commit', rev_id=b'other')
785
# Merge and revert, leaving a pending merge
786
self.tree.merge_from_branch(other_tree.branch)
787
self.tree.revert(filenames=['other-file'], backups=False)
789
def _out_of_sync_trees(self):
790
self.make_local_branch_and_tree()
791
self.run_bzr(['checkout', '--lightweight', 'local', 'checkout'])
792
# Make a change and commit it
793
self.build_tree_contents([('local/file', b'modified in local')])
794
self.tree.commit('modify file', rev_id=b'modified-in-local')
795
# Exercise commands from the checkout directory
796
self._default_wd = 'checkout'
797
self._default_errors = ["Working tree is out of date, please run"
798
" 'brz update'\\.", ]
800
def test_push_default(self):
801
self.assertPushSucceeds([], with_warning=True)
803
def test_push_with_revision(self):
804
self.assertPushSucceeds(['-r', 'revid:added'], revid_to_push=b'added')
806
def test_push_no_strict(self):
807
self.assertPushSucceeds(['--no-strict'])
809
def test_push_strict_with_changes(self):
810
self.assertPushFails(['--strict'])
812
def test_push_respect_config_var_strict(self):
813
self.set_config_push_strict('true')
814
self.assertPushFails([])
816
def test_push_bogus_config_var_ignored(self):
817
self.set_config_push_strict("I don't want you to be strict")
818
self.assertPushSucceeds([], with_warning=True)
820
def test_push_no_strict_command_line_override_config(self):
821
self.set_config_push_strict('yES')
822
self.assertPushFails([])
823
self.assertPushSucceeds(['--no-strict'])
825
def test_push_strict_command_line_override_config(self):
826
self.set_config_push_strict('oFF')
827
self.assertPushFails(['--strict'])
828
self.assertPushSucceeds([])
831
class TestPushForeign(tests.TestCaseWithTransport):
834
super(TestPushForeign, self).setUp()
835
test_foreign.register_dummy_foreign_for_test(self)
837
def make_dummy_builder(self, relpath):
838
builder = self.make_branch_builder(
839
relpath, format=test_foreign.DummyForeignVcsDirFormat())
840
builder.build_snapshot(None,
841
[('add', ('', b'TREE_ROOT', 'directory', None)),
842
('add', ('foo', b'fooid', 'file', b'bar'))],
843
revision_id=b'revid')
846
def test_no_roundtripping(self):
847
target_branch = self.make_dummy_builder('dp').get_branch()
848
source_tree = self.make_branch_and_tree("dc")
849
output, error = self.run_bzr("push -d dc dp", retcode=3)
850
self.assertEqual("", output)
853
"brz: ERROR: It is not possible to losslessly"
854
" push to dummy. You may want to use --lossy.\n")
857
class TestPushOutput(script.TestCaseWithTransportAndScript):
859
def test_push_log_format(self):
862
Created a standalone tree (format: 2a)
867
$ brz commit -m 'we need some foo'
868
2>Committing to:...trunk/
870
2>Committed revision 1.
871
$ brz init ../feature
872
Created a standalone tree (format: 2a)
873
$ brz push -v ../feature -Olog_format=line
875
1: jrandom@example.com ...we need some foo
876
2>All changes applied successfully.
877
2>Pushed up to revision 1.
880
def test_push_with_revspec(self):
882
$ brz init-shared-repo .
883
Shared repository with trees (format: 2a)
887
Created a repository tree (format: 2a)
888
Using shared repository...
890
$ brz commit -m 'first rev' --unchanged
891
2>Committing to:...trunk/
892
2>Committed revision 1.
896
$ brz commit -m 'we need some foo'
897
2>Committing to:...trunk/
899
2>Committed revision 2.
900
$ brz push -r 1 ../other
901
2>Created new branch.
902
$ brz st ../other # checking that file is not created (#484516)