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 (
43
from bzrlib.transport import memory
46
load_tests = scenarios.load_tests_apply_scenarios
49
class TestPush(tests.TestCaseWithTransport):
51
def test_push_error_on_vfs_http(self):
52
""" pushing a branch to a HTTP server fails cleanly. """
53
# the trunk is published on a web server
54
self.transport_readonly_server = http_server.HttpServer
55
self.make_branch('source')
56
public_url = self.get_readonly_url('target')
57
self.run_bzr_error(['http does not support mkdir'],
61
def test_push_remember(self):
62
"""Push changes from one branch to another and test push location."""
63
transport = self.get_transport()
64
tree_a = self.make_branch_and_tree('branch_a')
65
branch_a = tree_a.branch
66
self.build_tree(['branch_a/a'])
68
tree_a.commit('commit a')
69
tree_b = branch_a.bzrdir.sprout('branch_b').open_workingtree()
70
branch_b = tree_b.branch
71
tree_c = branch_a.bzrdir.sprout('branch_c').open_workingtree()
72
branch_c = tree_c.branch
73
self.build_tree(['branch_a/b'])
75
tree_a.commit('commit b')
76
self.build_tree(['branch_b/c'])
78
tree_b.commit('commit c')
79
# initial push location must be empty
80
self.assertEqual(None, branch_b.get_push_location())
82
# test push for failure without push location set
83
out = self.run_bzr('push', working_dir='branch_a', retcode=3)
84
self.assertEquals(out,
85
('','bzr: ERROR: No push location known or specified.\n'))
87
# test not remembered if cannot actually push
88
self.run_bzr('push path/which/doesnt/exist',
89
working_dir='branch_a', retcode=3)
90
out = self.run_bzr('push', working_dir='branch_a', retcode=3)
92
('', 'bzr: ERROR: No push location known or specified.\n'),
95
# test implicit --remember when no push location set, push fails
96
out = self.run_bzr('push ../branch_b',
97
working_dir='branch_a', retcode=3)
98
self.assertEquals(out,
99
('','bzr: ERROR: These branches have diverged. '
100
'See "bzr help diverged-branches" for more information.\n'))
101
self.assertEquals(osutils.abspath(branch_a.get_push_location()),
102
osutils.abspath(branch_b.bzrdir.root_transport.base))
104
# test implicit --remember after resolving previous failure
105
uncommit.uncommit(branch=branch_b, tree=tree_b)
106
transport.delete('branch_b/c')
107
out, err = self.run_bzr('push', working_dir='branch_a')
108
path = branch_a.get_push_location()
109
self.assertEqual(err,
110
'Using saved push location: %s\n'
111
'All changes applied successfully.\n'
112
'Pushed up to revision 2.\n'
113
% urlutils.local_path_from_url(path))
114
self.assertEqual(path,
115
branch_b.bzrdir.root_transport.base)
116
# test explicit --remember
117
self.run_bzr('push ../branch_c --remember', working_dir='branch_a')
118
self.assertEquals(branch_a.get_push_location(),
119
branch_c.bzrdir.root_transport.base)
121
def test_push_without_tree(self):
122
# bzr push from a branch that does not have a checkout should work.
123
b = self.make_branch('.')
124
out, err = self.run_bzr('push pushed-location')
125
self.assertEqual('', out)
126
self.assertEqual('Created new branch.\n', err)
127
b2 = branch.Branch.open('pushed-location')
128
self.assertEndsWith(b2.base, 'pushed-location/')
130
def test_push_no_tree(self):
131
# bzr push --no-tree of a branch with working trees
132
b = self.make_branch_and_tree('push-from')
133
self.build_tree(['push-from/file'])
136
out, err = self.run_bzr('push --no-tree -d push-from push-to')
137
self.assertEqual('', out)
138
self.assertEqual('Created new branch.\n', err)
139
self.assertPathDoesNotExist('push-to/file')
141
def test_push_new_branch_revision_count(self):
142
# bzr push of a branch with revisions to a new location
143
# should print the number of revisions equal to the length of the
145
t = self.make_branch_and_tree('tree')
146
self.build_tree(['tree/file'])
149
out, err = self.run_bzr('push -d tree pushed-to')
150
self.assertEqual('', out)
151
self.assertEqual('Created new branch.\n', err)
153
def test_push_quiet(self):
154
# test that using -q makes output quiet
155
t = self.make_branch_and_tree('tree')
156
self.build_tree(['tree/file'])
159
self.run_bzr('push -d tree pushed-to')
160
path = t.branch.get_push_location()
161
out, err = self.run_bzr('push', working_dir="tree")
162
self.assertEqual('Using saved push location: %s\n'
163
'No new revisions or tags to push.\n' %
164
urlutils.local_path_from_url(path), err)
165
out, err = self.run_bzr('push -q', working_dir="tree")
166
self.assertEqual('', out)
167
self.assertEqual('', err)
169
def test_push_only_pushes_history(self):
170
# Knit branches should only push the history for the current revision.
171
format = bzrdir.BzrDirMetaFormat1()
172
format.repository_format = knitrepo.RepositoryFormatKnit1()
173
shared_repo = self.make_repository('repo', format=format, shared=True)
174
shared_repo.set_make_working_trees(True)
176
def make_shared_tree(path):
177
shared_repo.bzrdir.root_transport.mkdir(path)
178
controldir.ControlDir.create_branch_convenience('repo/' + path)
179
return workingtree.WorkingTree.open('repo/' + path)
180
tree_a = make_shared_tree('a')
181
self.build_tree(['repo/a/file'])
183
tree_a.commit('commit a-1', rev_id='a-1')
184
f = open('repo/a/file', 'ab')
185
f.write('more stuff\n')
187
tree_a.commit('commit a-2', rev_id='a-2')
189
tree_b = make_shared_tree('b')
190
self.build_tree(['repo/b/file'])
192
tree_b.commit('commit b-1', rev_id='b-1')
194
self.assertTrue(shared_repo.has_revision('a-1'))
195
self.assertTrue(shared_repo.has_revision('a-2'))
196
self.assertTrue(shared_repo.has_revision('b-1'))
198
# Now that we have a repository with shared files, make sure
199
# that things aren't copied out by a 'push'
200
self.run_bzr('push ../../push-b', working_dir='repo/b')
201
pushed_tree = workingtree.WorkingTree.open('push-b')
202
pushed_repo = pushed_tree.branch.repository
203
self.assertFalse(pushed_repo.has_revision('a-1'))
204
self.assertFalse(pushed_repo.has_revision('a-2'))
205
self.assertTrue(pushed_repo.has_revision('b-1'))
207
def test_push_funky_id(self):
208
t = self.make_branch_and_tree('tree')
209
self.build_tree(['tree/filename'])
210
t.add('filename', 'funky-chars<>%&;"\'')
211
t.commit('commit filename')
212
self.run_bzr('push -d tree new-tree')
214
def test_push_dash_d(self):
215
t = self.make_branch_and_tree('from')
216
t.commit(allow_pointless=True,
217
message='first commit')
218
self.run_bzr('push -d from to-one')
219
self.assertPathExists('to-one')
220
self.run_bzr('push -d %s %s'
221
% tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
222
self.assertPathExists('to-two')
224
def test_push_repository_no_branch_doesnt_fetch_all_revs(self):
225
# See https://bugs.launchpad.net/bzr/+bug/465517
226
target_repo = self.make_repository('target')
227
source = self.make_branch_builder('source')
228
source.start_series()
229
source.build_snapshot('A', None, [
230
('add', ('', 'root-id', 'directory', None))])
231
source.build_snapshot('B', ['A'], [])
232
source.build_snapshot('C', ['A'], [])
233
source.finish_series()
234
self.run_bzr('push target -d source')
235
self.addCleanup(target_repo.lock_read().unlock)
236
# We should have pushed 'C', but not 'B', since it isn't in the
238
self.assertEqual([('A',), ('C',)], sorted(target_repo.revisions.keys()))
240
def test_push_smart_non_stacked_streaming_acceptance(self):
241
self.setup_smart_server_with_call_log()
242
t = self.make_branch_and_tree('from')
243
t.commit(allow_pointless=True, message='first commit')
244
self.reset_smart_call_log()
245
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
246
# This figure represent the amount of work to perform this use case. It
247
# is entirely ok to reduce this number if a test fails due to rpc_count
248
# being too low. If rpc_count increases, more network roundtrips have
249
# become necessary for this use case. Please do not adjust this number
250
# upwards without agreement from bzr's network support maintainers.
251
self.assertLength(9, self.hpss_calls)
253
def test_push_smart_stacked_streaming_acceptance(self):
254
self.setup_smart_server_with_call_log()
255
parent = self.make_branch_and_tree('parent', format='1.9')
256
parent.commit(message='first commit')
257
local = parent.bzrdir.sprout('local').open_workingtree()
258
local.commit(message='local commit')
259
self.reset_smart_call_log()
260
self.run_bzr(['push', '--stacked', '--stacked-on', '../parent',
261
self.get_url('public')], working_dir='local')
262
# This figure represent the amount of work to perform this use case. It
263
# is entirely ok to reduce this number if a test fails due to rpc_count
264
# being too low. If rpc_count increases, more network roundtrips have
265
# become necessary for this use case. Please do not adjust this number
266
# upwards without agreement from bzr's network support maintainers.
267
self.assertLength(13, self.hpss_calls)
268
remote = branch.Branch.open('public')
269
self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
271
def test_push_smart_tags_streaming_acceptance(self):
272
self.setup_smart_server_with_call_log()
273
t = self.make_branch_and_tree('from')
274
rev_id = t.commit(allow_pointless=True, message='first commit')
275
t.branch.tags.set_tag('new-tag', rev_id)
276
self.reset_smart_call_log()
277
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
278
# This figure represent the amount of work to perform this use case. It
279
# is entirely ok to reduce this number if a test fails due to rpc_count
280
# being too low. If rpc_count increases, more network roundtrips have
281
# become necessary for this use case. Please do not adjust this number
282
# upwards without agreement from bzr's network support maintainers.
283
self.assertLength(11, self.hpss_calls)
285
def test_push_smart_incremental_acceptance(self):
286
self.setup_smart_server_with_call_log()
287
t = self.make_branch_and_tree('from')
288
rev_id1 = t.commit(allow_pointless=True, message='first commit')
289
rev_id2 = t.commit(allow_pointless=True, message='second commit')
291
['push', self.get_url('to-one'), '-r1'], working_dir='from')
292
self.reset_smart_call_log()
293
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
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(11, self.hpss_calls)
301
def test_push_smart_with_default_stacking_url_path_segment(self):
302
# If the default stacked-on location is a path element then branches
303
# we push there over the smart server are stacked and their
304
# stacked_on_url is that exact path segment. Added to nail bug 385132.
305
self.setup_smart_server_with_call_log()
306
self.make_branch('stack-on', format='1.9')
307
self.make_bzrdir('.').get_config().set_default_stack_on(
309
self.make_branch('from', format='1.9')
310
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
311
b = branch.Branch.open(self.get_url('to'))
312
self.assertEqual('/extra/stack-on', b.get_stacked_on_url())
314
def test_push_smart_with_default_stacking_relative_path(self):
315
# If the default stacked-on location is a relative path then branches
316
# we push there over the smart server are stacked and their
317
# stacked_on_url is a relative path. Added to nail bug 385132.
318
self.setup_smart_server_with_call_log()
319
self.make_branch('stack-on', format='1.9')
320
self.make_bzrdir('.').get_config().set_default_stack_on('stack-on')
321
self.make_branch('from', format='1.9')
322
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
323
b = branch.Branch.open(self.get_url('to'))
324
self.assertEqual('../stack-on', b.get_stacked_on_url())
326
def create_simple_tree(self):
327
tree = self.make_branch_and_tree('tree')
328
self.build_tree(['tree/a'])
329
tree.add(['a'], ['a-id'])
330
tree.commit('one', rev_id='r1')
333
def test_push_create_prefix(self):
334
"""'bzr push --create-prefix' will create leading directories."""
335
tree = self.create_simple_tree()
337
self.run_bzr_error(['Parent directory of ../new/tree does not exist'],
340
self.run_bzr('push ../new/tree --create-prefix',
342
new_tree = workingtree.WorkingTree.open('new/tree')
343
self.assertEqual(tree.last_revision(), new_tree.last_revision())
344
self.assertPathExists('new/tree/a')
346
def test_push_use_existing(self):
347
"""'bzr push --use-existing-dir' can push into an existing dir.
349
By default, 'bzr push' will not use an existing, non-versioned dir.
351
tree = self.create_simple_tree()
352
self.build_tree(['target/'])
354
self.run_bzr_error(['Target directory ../target already exists',
355
'Supply --use-existing-dir',
357
'push ../target', working_dir='tree')
359
self.run_bzr('push --use-existing-dir ../target',
362
new_tree = workingtree.WorkingTree.open('target')
363
self.assertEqual(tree.last_revision(), new_tree.last_revision())
364
# The push should have created target/a
365
self.assertPathExists('target/a')
367
def test_push_use_existing_into_empty_bzrdir(self):
368
"""'bzr push --use-existing-dir' into a dir with an empty .bzr dir
371
tree = self.create_simple_tree()
372
self.build_tree(['target/', 'target/.bzr/'])
374
['Target directory ../target already contains a .bzr directory, '
375
'but it is not valid.'],
376
'push ../target --use-existing-dir', working_dir='tree')
378
def test_push_onto_repo(self):
379
"""We should be able to 'bzr push' into an existing bzrdir."""
380
tree = self.create_simple_tree()
381
repo = self.make_repository('repo', shared=True)
383
self.run_bzr('push ../repo',
386
# Pushing onto an existing bzrdir will create a repository and
387
# branch as needed, but will only create a working tree if there was
389
self.assertRaises(errors.NoWorkingTree,
390
workingtree.WorkingTree.open, 'repo')
391
new_branch = branch.Branch.open('repo')
392
self.assertEqual(tree.last_revision(), new_branch.last_revision())
394
def test_push_onto_just_bzrdir(self):
395
"""We don't handle when the target is just a bzrdir.
397
Because you shouldn't be able to create *just* a bzrdir in the wild.
399
# TODO: jam 20070109 Maybe it would be better to create the repository
401
tree = self.create_simple_tree()
402
a_bzrdir = self.make_bzrdir('dir')
404
self.run_bzr_error(['At ../dir you have a valid .bzr control'],
408
def test_push_with_revisionspec(self):
409
"""We should be able to push a revision older than the tip."""
410
tree_from = self.make_branch_and_tree('from')
411
tree_from.commit("One.", rev_id="from-1")
412
tree_from.commit("Two.", rev_id="from-2")
414
self.run_bzr('push -r1 ../to', working_dir='from')
416
tree_to = workingtree.WorkingTree.open('to')
417
repo_to = tree_to.branch.repository
418
self.assertTrue(repo_to.has_revision('from-1'))
419
self.assertFalse(repo_to.has_revision('from-2'))
420
self.assertEqual(tree_to.branch.last_revision_info()[1], 'from-1')
423
['bzr: ERROR: bzr push --revision '
424
'takes exactly one revision identifier\n'],
425
'push -r0..2 ../to', working_dir='from')
427
def create_trunk_and_feature_branch(self):
429
trunk_tree = self.make_branch_and_tree('target',
431
trunk_tree.commit('mainline')
432
# and a branch from it
433
branch_tree = self.make_branch_and_tree('branch',
435
branch_tree.pull(trunk_tree.branch)
436
branch_tree.branch.set_parent(trunk_tree.branch.base)
437
# with some work on it
438
branch_tree.commit('moar work plz')
439
return trunk_tree, branch_tree
441
def assertPublished(self, branch_revid, stacked_on):
442
"""Assert that the branch 'published' has been published correctly."""
443
published_branch = branch.Branch.open('published')
444
# The published branch refers to the mainline
445
self.assertEqual(stacked_on, published_branch.get_stacked_on_url())
446
# and the branch's work was pushed
447
self.assertTrue(published_branch.repository.has_revision(branch_revid))
449
def test_push_new_branch_stacked_on(self):
450
"""Pushing a new branch with --stacked-on creates a stacked branch."""
451
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
452
# we publish branch_tree with a reference to the mainline.
453
out, err = self.run_bzr(['push', '--stacked-on', trunk_tree.branch.base,
454
self.get_url('published')], working_dir='branch')
455
self.assertEqual('', out)
456
self.assertEqual('Created new stacked branch referring to %s.\n' %
457
trunk_tree.branch.base, err)
458
self.assertPublished(branch_tree.last_revision(),
459
trunk_tree.branch.base)
461
def test_push_new_branch_stacked_uses_parent_when_no_public_url(self):
462
"""When the parent has no public url the parent is used as-is."""
463
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
464
# now we do a stacked push, which should determine the public location
466
out, err = self.run_bzr(['push', '--stacked',
467
self.get_url('published')], working_dir='branch')
468
self.assertEqual('', out)
469
self.assertEqual('Created new stacked branch referring to %s.\n' %
470
trunk_tree.branch.base, err)
471
self.assertPublished(branch_tree.last_revision(),
472
trunk_tree.branch.base)
474
def test_push_new_branch_stacked_uses_parent_public(self):
475
"""Pushing a new branch with --stacked creates a stacked branch."""
476
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
477
# the trunk is published on a web server
478
self.transport_readonly_server = http_server.HttpServer
479
trunk_public = self.make_branch('public_trunk', format='1.9')
480
trunk_public.pull(trunk_tree.branch)
481
trunk_public_url = self.get_readonly_url('public_trunk')
482
trunk_tree.branch.set_public_branch(trunk_public_url)
483
# now we do a stacked push, which should determine the public location
485
out, err = self.run_bzr(['push', '--stacked',
486
self.get_url('published')], working_dir='branch')
487
self.assertEqual('', out)
488
self.assertEqual('Created new stacked branch referring to %s.\n' %
489
trunk_public_url, err)
490
self.assertPublished(branch_tree.last_revision(), trunk_public_url)
492
def test_push_new_branch_stacked_no_parent(self):
493
"""Pushing with --stacked and no parent branch errors."""
494
branch = self.make_branch_and_tree('branch', format='1.9')
495
# now we do a stacked push, which should fail as the place to refer too
496
# cannot be determined.
497
out, err = self.run_bzr_error(
498
['Could not determine branch to refer to\\.'], ['push', '--stacked',
499
self.get_url('published')], working_dir='branch')
500
self.assertEqual('', out)
501
self.assertFalse(self.get_transport('published').has('.'))
503
def test_push_notifies_default_stacking(self):
504
self.make_branch('stack_on', format='1.6')
505
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
506
self.make_branch('from', format='1.6')
507
out, err = self.run_bzr('push -d from to')
508
self.assertContainsRe(err,
509
'Using default stacking branch stack_on at .*')
511
def test_push_stacks_with_default_stacking_if_target_is_stackable(self):
512
self.make_branch('stack_on', format='1.6')
513
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
514
self.make_branch('from', format='pack-0.92')
515
out, err = self.run_bzr('push -d from to')
516
b = branch.Branch.open('to')
517
self.assertEqual('../stack_on', b.get_stacked_on_url())
519
def test_push_does_not_change_format_with_default_if_target_cannot(self):
520
self.make_branch('stack_on', format='pack-0.92')
521
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
522
self.make_branch('from', format='pack-0.92')
523
out, err = self.run_bzr('push -d from to')
524
b = branch.Branch.open('to')
525
self.assertRaises(errors.UnstackableBranchFormat, b.get_stacked_on_url)
527
def test_push_doesnt_create_broken_branch(self):
528
"""Pushing a new standalone branch works even when there's a default
529
stacking policy at the destination.
531
The new branch will preserve the repo format (even if it isn't the
532
default for the branch), and will be stacked when the repo format
533
allows (which means that the branch format isn't necessarly preserved).
535
self.make_repository('repo', shared=True, format='1.6')
536
builder = self.make_branch_builder('repo/local', format='pack-0.92')
537
builder.start_series()
538
builder.build_snapshot('rev-1', None, [
539
('add', ('', 'root-id', 'directory', '')),
540
('add', ('filename', 'f-id', 'file', 'content\n'))])
541
builder.build_snapshot('rev-2', ['rev-1'], [])
542
builder.build_snapshot('rev-3', ['rev-2'],
543
[('modify', ('f-id', 'new-content\n'))])
544
builder.finish_series()
545
branch = builder.get_branch()
546
# Push rev-1 to "trunk", so that we can stack on it.
547
self.run_bzr('push -d repo/local trunk -r 1')
548
# Set a default stacking policy so that new branches will automatically
550
self.make_bzrdir('.').get_config().set_default_stack_on('trunk')
551
# Push rev-2 to a new branch "remote". It will be stacked on "trunk".
552
out, err = self.run_bzr('push -d repo/local remote -r 2')
553
self.assertContainsRe(
554
err, 'Using default stacking branch trunk at .*')
555
# Push rev-3 onto "remote". If "remote" not stacked and is missing the
556
# fulltext record for f-id @ rev-1, then this will fail.
557
out, err = self.run_bzr('push -d repo/local remote -r 3')
559
def test_push_verbose_shows_log(self):
560
tree = self.make_branch_and_tree('source')
562
out, err = self.run_bzr('push -v -d source target')
563
# initial push contains log
564
self.assertContainsRe(out, 'rev1')
566
out, err = self.run_bzr('push -v -d source target')
567
# subsequent push contains log
568
self.assertContainsRe(out, 'rev2')
569
# subsequent log is accurate
570
self.assertNotContainsRe(out, 'rev1')
572
def test_push_from_subdir(self):
573
t = self.make_branch_and_tree('tree')
574
self.build_tree(['tree/dir/', 'tree/dir/file'])
575
t.add('dir', 'dir/file')
577
out, err = self.run_bzr('push ../../pushloc', working_dir='tree/dir')
578
self.assertEqual('', out)
579
self.assertEqual('Created new branch.\n', err)
582
class RedirectingMemoryTransport(memory.MemoryTransport):
584
def mkdir(self, relpath, mode=None):
585
if self._cwd == '/source/':
586
raise errors.RedirectRequested(self.abspath(relpath),
587
self.abspath('../target'),
589
elif self._cwd == '/infinite-loop/':
590
raise errors.RedirectRequested(self.abspath(relpath),
591
self.abspath('../infinite-loop'),
594
return super(RedirectingMemoryTransport, self).mkdir(
597
def get(self, relpath):
598
if self.clone(relpath)._cwd == '/infinite-loop/':
599
raise errors.RedirectRequested(self.abspath(relpath),
600
self.abspath('../infinite-loop'),
603
return super(RedirectingMemoryTransport, self).get(relpath)
605
def _redirected_to(self, source, target):
606
# We do accept redirections
607
return transport.get_transport(target)
610
class RedirectingMemoryServer(memory.MemoryServer):
612
def start_server(self):
613
self._dirs = {'/': None}
616
self._scheme = 'redirecting-memory+%s:///' % id(self)
617
transport.register_transport(self._scheme, self._memory_factory)
619
def _memory_factory(self, url):
620
result = RedirectingMemoryTransport(url)
621
result._dirs = self._dirs
622
result._files = self._files
623
result._locks = self._locks
626
def stop_server(self):
627
transport.unregister_transport(self._scheme, self._memory_factory)
630
class TestPushRedirect(tests.TestCaseWithTransport):
633
tests.TestCaseWithTransport.setUp(self)
634
self.memory_server = RedirectingMemoryServer()
635
self.start_server(self.memory_server)
636
# Make the branch and tree that we'll be pushing.
637
t = self.make_branch_and_tree('tree')
638
self.build_tree(['tree/file'])
642
def test_push_redirects_on_mkdir(self):
643
"""If the push requires a mkdir, push respects redirect requests.
645
This is added primarily to handle lp:/ URI support, so that users can
646
push to new branches by specifying lp:/ URIs.
648
destination_url = self.memory_server.get_url() + 'source'
649
self.run_bzr(['push', '-d', 'tree', destination_url])
651
local_revision = branch.Branch.open('tree').last_revision()
652
remote_revision = branch.Branch.open(
653
self.memory_server.get_url() + 'target').last_revision()
654
self.assertEqual(remote_revision, local_revision)
656
def test_push_gracefully_handles_too_many_redirects(self):
657
"""Push fails gracefully if the mkdir generates a large number of
660
destination_url = self.memory_server.get_url() + 'infinite-loop'
661
out, err = self.run_bzr_error(
662
['Too many redirections trying to make %s\\.\n'
663
% re.escape(destination_url)],
664
['push', '-d', 'tree', destination_url], retcode=3)
665
self.assertEqual('', out)
668
class TestPushStrictMixin(object):
670
def make_local_branch_and_tree(self):
671
self.tree = self.make_branch_and_tree('local')
672
self.build_tree_contents([('local/file', 'initial')])
673
self.tree.add('file')
674
self.tree.commit('adding file', rev_id='added')
675
self.build_tree_contents([('local/file', 'modified')])
676
self.tree.commit('modify file', rev_id='modified')
678
def set_config_push_strict(self, value):
679
# set config var (any of bazaar.conf, locations.conf, branch.conf
681
conf = self.tree.branch.get_config_stack()
682
conf.set('push_strict', value)
684
_default_command = ['push', '../to']
685
_default_wd = 'local'
686
_default_errors = ['Working tree ".*/local/" has uncommitted '
687
'changes \(See bzr status\)\.',]
688
_default_additional_error = 'Use --no-strict to force the push.\n'
689
_default_additional_warning = 'Uncommitted changes will not be pushed.'
692
def assertPushFails(self, args):
693
out, err = self.run_bzr_error(self._default_errors,
694
self._default_command + args,
695
working_dir=self._default_wd, retcode=3)
696
self.assertContainsRe(err, self._default_additional_error)
698
def assertPushSucceeds(self, args, with_warning=False, revid_to_push=None):
700
error_regexes = self._default_errors
703
out, err = self.run_bzr(self._default_command + args,
704
working_dir=self._default_wd,
705
error_regexes=error_regexes)
707
self.assertContainsRe(err, self._default_additional_warning)
709
self.assertNotContainsRe(err, self._default_additional_warning)
710
branch_from = branch.Branch.open(self._default_wd)
711
if revid_to_push is None:
712
revid_to_push = branch_from.last_revision()
713
branch_to = branch.Branch.open('to')
714
repo_to = branch_to.repository
715
self.assertTrue(repo_to.has_revision(revid_to_push))
716
self.assertEqual(revid_to_push, branch_to.last_revision())
720
class TestPushStrictWithoutChanges(tests.TestCaseWithTransport,
721
TestPushStrictMixin):
724
super(TestPushStrictWithoutChanges, self).setUp()
725
self.make_local_branch_and_tree()
727
def test_push_default(self):
728
self.assertPushSucceeds([])
730
def test_push_strict(self):
731
self.assertPushSucceeds(['--strict'])
733
def test_push_no_strict(self):
734
self.assertPushSucceeds(['--no-strict'])
736
def test_push_config_var_strict(self):
737
self.set_config_push_strict('true')
738
self.assertPushSucceeds([])
740
def test_push_config_var_no_strict(self):
741
self.set_config_push_strict('false')
742
self.assertPushSucceeds([])
745
strict_push_change_scenarios = [
747
dict(_changes_type= '_uncommitted_changes')),
749
dict(_changes_type= '_pending_merges')),
750
('out-of-sync-trees',
751
dict(_changes_type= '_out_of_sync_trees')),
755
class TestPushStrictWithChanges(tests.TestCaseWithTransport,
756
TestPushStrictMixin):
758
scenarios = strict_push_change_scenarios
759
_changes_type = None # Set by load_tests
762
super(TestPushStrictWithChanges, self).setUp()
763
# Apply the changes defined in load_tests: one of _uncommitted_changes,
764
# _pending_merges or _out_of_sync_trees
765
getattr(self, self._changes_type)()
767
def _uncommitted_changes(self):
768
self.make_local_branch_and_tree()
769
# Make a change without committing it
770
self.build_tree_contents([('local/file', 'in progress')])
772
def _pending_merges(self):
773
self.make_local_branch_and_tree()
774
# Create 'other' branch containing a new file
775
other_bzrdir = self.tree.bzrdir.sprout('other')
776
other_tree = other_bzrdir.open_workingtree()
777
self.build_tree_contents([('other/other-file', 'other')])
778
other_tree.add('other-file')
779
other_tree.commit('other commit', rev_id='other')
780
# Merge and revert, leaving a pending merge
781
self.tree.merge_from_branch(other_tree.branch)
782
self.tree.revert(filenames=['other-file'], backups=False)
784
def _out_of_sync_trees(self):
785
self.make_local_branch_and_tree()
786
self.run_bzr(['checkout', '--lightweight', 'local', 'checkout'])
787
# Make a change and commit it
788
self.build_tree_contents([('local/file', 'modified in local')])
789
self.tree.commit('modify file', rev_id='modified-in-local')
790
# Exercise commands from the checkout directory
791
self._default_wd = 'checkout'
792
self._default_errors = ["Working tree is out of date, please run"
795
def test_push_default(self):
796
self.assertPushSucceeds([], with_warning=True)
798
def test_push_with_revision(self):
799
self.assertPushSucceeds(['-r', 'revid:added'], revid_to_push='added')
801
def test_push_no_strict(self):
802
self.assertPushSucceeds(['--no-strict'])
804
def test_push_strict_with_changes(self):
805
self.assertPushFails(['--strict'])
807
def test_push_respect_config_var_strict(self):
808
self.set_config_push_strict('true')
809
self.assertPushFails([])
811
def test_push_bogus_config_var_ignored(self):
812
self.set_config_push_strict("I don't want you to be strict")
813
self.assertPushSucceeds([], with_warning=True)
815
def test_push_no_strict_command_line_override_config(self):
816
self.set_config_push_strict('yES')
817
self.assertPushFails([])
818
self.assertPushSucceeds(['--no-strict'])
820
def test_push_strict_command_line_override_config(self):
821
self.set_config_push_strict('oFF')
822
self.assertPushFails(['--strict'])
823
self.assertPushSucceeds([])
826
class TestPushForeign(tests.TestCaseWithTransport):
829
super(TestPushForeign, self).setUp()
830
test_foreign.register_dummy_foreign_for_test(self)
832
def make_dummy_builder(self, relpath):
833
builder = self.make_branch_builder(
834
relpath, format=test_foreign.DummyForeignVcsDirFormat())
835
builder.build_snapshot('revid', None,
836
[('add', ('', 'TREE_ROOT', 'directory', None)),
837
('add', ('foo', 'fooid', 'file', 'bar'))])
840
def test_no_roundtripping(self):
841
target_branch = self.make_dummy_builder('dp').get_branch()
842
source_tree = self.make_branch_and_tree("dc")
843
output, error = self.run_bzr("push -d dc dp", retcode=3)
844
self.assertEquals("", output)
845
self.assertEquals(error, "bzr: ERROR: It is not possible to losslessly"
846
" push to dummy. You may want to use dpush instead.\n")
849
class TestPushOutput(script.TestCaseWithTransportAndScript):
851
def test_push_log_format(self):
854
Created a standalone tree (format: 2a)
859
$ bzr commit -m 'we need some foo'
860
2>Committing to:...trunk/
862
2>Committed revision 1.
863
$ bzr init ../feature
864
Created a standalone tree (format: 2a)
865
$ bzr push -v ../feature -Olog_format=line
867
1: jrandom@example.com ...we need some foo
868
2>All changes applied successfully.
869
2>Pushed up to revision 1.