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