1
# Copyright (C) 2006-2010 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."""
33
from bzrlib.repofmt import knitrepo
34
from bzrlib.tests import (
40
from bzrlib.transport import memory
43
def load_tests(standard_tests, module, loader):
44
"""Multiply tests for the push command."""
45
result = loader.suiteClass()
47
# one for each king of change
48
changes_tests, remaining_tests = tests.split_suite_by_condition(
49
standard_tests, tests.condition_isinstance((
50
TestPushStrictWithChanges,
54
dict(_changes_type= '_uncommitted_changes')),
56
dict(_changes_type= '_pending_merges')),
58
dict(_changes_type= '_out_of_sync_trees')),
60
tests.multiply_tests(changes_tests, changes_scenarios, result)
61
# No parametrization for the remaining tests
62
result.addTests(remaining_tests)
67
class TestPush(tests.TestCaseWithTransport):
69
def test_push_error_on_vfs_http(self):
70
""" pushing a branch to a HTTP server fails cleanly. """
71
# the trunk is published on a web server
72
self.transport_readonly_server = http_server.HttpServer
73
self.make_branch('source')
74
public_url = self.get_readonly_url('target')
75
self.run_bzr_error(['http does not support mkdir'],
79
def test_push_remember(self):
80
"""Push changes from one branch to another and test push location."""
81
transport = self.get_transport()
82
tree_a = self.make_branch_and_tree('branch_a')
83
branch_a = tree_a.branch
84
self.build_tree(['branch_a/a'])
86
tree_a.commit('commit a')
87
tree_b = branch_a.bzrdir.sprout('branch_b').open_workingtree()
88
branch_b = tree_b.branch
89
tree_c = branch_a.bzrdir.sprout('branch_c').open_workingtree()
90
branch_c = tree_c.branch
91
self.build_tree(['branch_a/b'])
93
tree_a.commit('commit b')
94
self.build_tree(['branch_b/c'])
96
tree_b.commit('commit c')
97
# initial push location must be empty
98
self.assertEqual(None, branch_b.get_push_location())
100
# test push for failure without push location set
101
out = self.run_bzr('push', working_dir='branch_a', retcode=3)
102
self.assertEquals(out,
103
('','bzr: ERROR: No push location known or specified.\n'))
105
# test not remembered if cannot actually push
106
self.run_bzr('push path/which/doesnt/exist',
107
working_dir='branch_a', retcode=3)
108
out = self.run_bzr('push', working_dir='branch_a', retcode=3)
110
('', 'bzr: ERROR: No push location known or specified.\n'),
113
# test implicit --remember when no push location set, push fails
114
out = self.run_bzr('push ../branch_b',
115
working_dir='branch_a', retcode=3)
116
self.assertEquals(out,
117
('','bzr: ERROR: These branches have diverged. '
118
'See "bzr help diverged-branches" for more information.\n'))
119
self.assertEquals(osutils.abspath(branch_a.get_push_location()),
120
osutils.abspath(branch_b.bzrdir.root_transport.base))
122
# test implicit --remember after resolving previous failure
123
uncommit.uncommit(branch=branch_b, tree=tree_b)
124
transport.delete('branch_b/c')
125
out, err = self.run_bzr('push', working_dir='branch_a')
126
path = branch_a.get_push_location()
127
self.assertEquals(out,
128
'Using saved push location: %s\n'
129
% urlutils.local_path_from_url(path))
130
self.assertEqual(err,
131
'All changes applied successfully.\n'
132
'Pushed up to revision 2.\n')
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_new_branch_revision_count(self):
150
# bzr push of a branch with revisions to a new location
151
# should print the number of revisions equal to the length of the
153
t = self.make_branch_and_tree('tree')
154
self.build_tree(['tree/file'])
157
out, err = self.run_bzr('push -d tree pushed-to')
158
self.assertEqual('', out)
159
self.assertEqual('Created new branch.\n', err)
161
def test_push_only_pushes_history(self):
162
# Knit branches should only push the history for the current revision.
163
format = bzrdir.BzrDirMetaFormat1()
164
format.repository_format = knitrepo.RepositoryFormatKnit1()
165
shared_repo = self.make_repository('repo', format=format, shared=True)
166
shared_repo.set_make_working_trees(True)
168
def make_shared_tree(path):
169
shared_repo.bzrdir.root_transport.mkdir(path)
170
shared_repo.bzrdir.create_branch_convenience('repo/' + path)
171
return workingtree.WorkingTree.open('repo/' + path)
172
tree_a = make_shared_tree('a')
173
self.build_tree(['repo/a/file'])
175
tree_a.commit('commit a-1', rev_id='a-1')
176
f = open('repo/a/file', 'ab')
177
f.write('more stuff\n')
179
tree_a.commit('commit a-2', rev_id='a-2')
181
tree_b = make_shared_tree('b')
182
self.build_tree(['repo/b/file'])
184
tree_b.commit('commit b-1', rev_id='b-1')
186
self.assertTrue(shared_repo.has_revision('a-1'))
187
self.assertTrue(shared_repo.has_revision('a-2'))
188
self.assertTrue(shared_repo.has_revision('b-1'))
190
# Now that we have a repository with shared files, make sure
191
# that things aren't copied out by a 'push'
192
self.run_bzr('push ../../push-b', working_dir='repo/b')
193
pushed_tree = workingtree.WorkingTree.open('push-b')
194
pushed_repo = pushed_tree.branch.repository
195
self.assertFalse(pushed_repo.has_revision('a-1'))
196
self.assertFalse(pushed_repo.has_revision('a-2'))
197
self.assertTrue(pushed_repo.has_revision('b-1'))
199
def test_push_funky_id(self):
200
t = self.make_branch_and_tree('tree')
201
self.build_tree(['tree/filename'])
202
t.add('filename', 'funky-chars<>%&;"\'')
203
t.commit('commit filename')
204
self.run_bzr('push -d tree new-tree')
206
def test_push_dash_d(self):
207
t = self.make_branch_and_tree('from')
208
t.commit(allow_pointless=True,
209
message='first commit')
210
self.run_bzr('push -d from to-one')
211
self.failUnlessExists('to-one')
212
self.run_bzr('push -d %s %s'
213
% tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
214
self.failUnlessExists('to-two')
216
def test_push_smart_non_stacked_streaming_acceptance(self):
217
self.setup_smart_server_with_call_log()
218
t = self.make_branch_and_tree('from')
219
t.commit(allow_pointless=True, message='first commit')
220
self.reset_smart_call_log()
221
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
222
# This figure represent the amount of work to perform this use case. It
223
# is entirely ok to reduce this number if a test fails due to rpc_count
224
# being too low. If rpc_count increases, more network roundtrips have
225
# become necessary for this use case. Please do not adjust this number
226
# upwards without agreement from bzr's network support maintainers.
227
self.assertLength(9, self.hpss_calls)
229
def test_push_smart_stacked_streaming_acceptance(self):
230
self.setup_smart_server_with_call_log()
231
parent = self.make_branch_and_tree('parent', format='1.9')
232
parent.commit(message='first commit')
233
local = parent.bzrdir.sprout('local').open_workingtree()
234
local.commit(message='local commit')
235
self.reset_smart_call_log()
236
self.run_bzr(['push', '--stacked', '--stacked-on', '../parent',
237
self.get_url('public')], working_dir='local')
238
# This figure represent the amount of work to perform this use case. It
239
# is entirely ok to reduce this number if a test fails due to rpc_count
240
# being too low. If rpc_count increases, more network roundtrips have
241
# become necessary for this use case. Please do not adjust this number
242
# upwards without agreement from bzr's network support maintainers.
243
self.assertLength(14, self.hpss_calls)
244
remote = branch.Branch.open('public')
245
self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
247
def test_push_smart_tags_streaming_acceptance(self):
248
self.setup_smart_server_with_call_log()
249
t = self.make_branch_and_tree('from')
250
rev_id = t.commit(allow_pointless=True, message='first commit')
251
t.branch.tags.set_tag('new-tag', rev_id)
252
self.reset_smart_call_log()
253
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
254
# This figure represent the amount of work to perform this use case. It
255
# is entirely ok to reduce this number if a test fails due to rpc_count
256
# being too low. If rpc_count increases, more network roundtrips have
257
# become necessary for this use case. Please do not adjust this number
258
# upwards without agreement from bzr's network support maintainers.
259
self.assertLength(11, self.hpss_calls)
261
def test_push_smart_incremental_acceptance(self):
262
self.setup_smart_server_with_call_log()
263
t = self.make_branch_and_tree('from')
264
rev_id1 = t.commit(allow_pointless=True, message='first commit')
265
rev_id2 = t.commit(allow_pointless=True, message='second commit')
267
['push', self.get_url('to-one'), '-r1'], working_dir='from')
268
self.reset_smart_call_log()
269
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
270
# This figure represent the amount of work to perform this use case. It
271
# is entirely ok to reduce this number if a test fails due to rpc_count
272
# being too low. If rpc_count increases, more network roundtrips have
273
# become necessary for this use case. Please do not adjust this number
274
# upwards without agreement from bzr's network support maintainers.
275
self.assertLength(11, self.hpss_calls)
277
def test_push_smart_with_default_stacking_url_path_segment(self):
278
# If the default stacked-on location is a path element then branches
279
# we push there over the smart server are stacked and their
280
# stacked_on_url is that exact path segment. Added to nail bug 385132.
281
self.setup_smart_server_with_call_log()
282
self.make_branch('stack-on', format='1.9')
283
self.make_bzrdir('.').get_config().set_default_stack_on(
285
self.make_branch('from', format='1.9')
286
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
287
b = branch.Branch.open(self.get_url('to'))
288
self.assertEqual('/extra/stack-on', b.get_stacked_on_url())
290
def test_push_smart_with_default_stacking_relative_path(self):
291
# If the default stacked-on location is a relative path then branches
292
# we push there over the smart server are stacked and their
293
# stacked_on_url is a relative path. Added to nail bug 385132.
294
self.setup_smart_server_with_call_log()
295
self.make_branch('stack-on', format='1.9')
296
self.make_bzrdir('.').get_config().set_default_stack_on('stack-on')
297
self.make_branch('from', format='1.9')
298
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
299
b = branch.Branch.open(self.get_url('to'))
300
self.assertEqual('../stack-on', b.get_stacked_on_url())
302
def create_simple_tree(self):
303
tree = self.make_branch_and_tree('tree')
304
self.build_tree(['tree/a'])
305
tree.add(['a'], ['a-id'])
306
tree.commit('one', rev_id='r1')
309
def test_push_create_prefix(self):
310
"""'bzr push --create-prefix' will create leading directories."""
311
tree = self.create_simple_tree()
313
self.run_bzr_error(['Parent directory of ../new/tree does not exist'],
316
self.run_bzr('push ../new/tree --create-prefix',
318
new_tree = workingtree.WorkingTree.open('new/tree')
319
self.assertEqual(tree.last_revision(), new_tree.last_revision())
320
self.failUnlessExists('new/tree/a')
322
def test_push_use_existing(self):
323
"""'bzr push --use-existing-dir' can push into an existing dir.
325
By default, 'bzr push' will not use an existing, non-versioned dir.
327
tree = self.create_simple_tree()
328
self.build_tree(['target/'])
330
self.run_bzr_error(['Target directory ../target already exists',
331
'Supply --use-existing-dir',
333
'push ../target', working_dir='tree')
335
self.run_bzr('push --use-existing-dir ../target',
338
new_tree = workingtree.WorkingTree.open('target')
339
self.assertEqual(tree.last_revision(), new_tree.last_revision())
340
# The push should have created target/a
341
self.failUnlessExists('target/a')
343
def test_push_use_existing_into_empty_bzrdir(self):
344
"""'bzr push --use-existing-dir' into a dir with an empty .bzr dir
347
tree = self.create_simple_tree()
348
self.build_tree(['target/', 'target/.bzr/'])
350
['Target directory ../target already contains a .bzr directory, '
351
'but it is not valid.'],
352
'push ../target --use-existing-dir', working_dir='tree')
354
def test_push_onto_repo(self):
355
"""We should be able to 'bzr push' into an existing bzrdir."""
356
tree = self.create_simple_tree()
357
repo = self.make_repository('repo', shared=True)
359
self.run_bzr('push ../repo',
362
# Pushing onto an existing bzrdir will create a repository and
363
# branch as needed, but will only create a working tree if there was
365
self.assertRaises(errors.NoWorkingTree,
366
workingtree.WorkingTree.open, 'repo')
367
new_branch = branch.Branch.open('repo')
368
self.assertEqual(tree.last_revision(), new_branch.last_revision())
370
def test_push_onto_just_bzrdir(self):
371
"""We don't handle when the target is just a bzrdir.
373
Because you shouldn't be able to create *just* a bzrdir in the wild.
375
# TODO: jam 20070109 Maybe it would be better to create the repository
377
tree = self.create_simple_tree()
378
a_bzrdir = self.make_bzrdir('dir')
380
self.run_bzr_error(['At ../dir you have a valid .bzr control'],
384
def test_push_with_revisionspec(self):
385
"""We should be able to push a revision older than the tip."""
386
tree_from = self.make_branch_and_tree('from')
387
tree_from.commit("One.", rev_id="from-1")
388
tree_from.commit("Two.", rev_id="from-2")
390
self.run_bzr('push -r1 ../to', working_dir='from')
392
tree_to = workingtree.WorkingTree.open('to')
393
repo_to = tree_to.branch.repository
394
self.assertTrue(repo_to.has_revision('from-1'))
395
self.assertFalse(repo_to.has_revision('from-2'))
396
self.assertEqual(tree_to.branch.last_revision_info()[1], 'from-1')
399
['bzr: ERROR: bzr push --revision '
400
'takes exactly one revision identifier\n'],
401
'push -r0..2 ../to', working_dir='from')
403
def create_trunk_and_feature_branch(self):
405
trunk_tree = self.make_branch_and_tree('target',
407
trunk_tree.commit('mainline')
408
# and a branch from it
409
branch_tree = self.make_branch_and_tree('branch',
411
branch_tree.pull(trunk_tree.branch)
412
branch_tree.branch.set_parent(trunk_tree.branch.base)
413
# with some work on it
414
branch_tree.commit('moar work plz')
415
return trunk_tree, branch_tree
417
def assertPublished(self, branch_revid, stacked_on):
418
"""Assert that the branch 'published' has been published correctly."""
419
published_branch = branch.Branch.open('published')
420
# The published branch refers to the mainline
421
self.assertEqual(stacked_on, published_branch.get_stacked_on_url())
422
# and the branch's work was pushed
423
self.assertTrue(published_branch.repository.has_revision(branch_revid))
425
def test_push_new_branch_stacked_on(self):
426
"""Pushing a new branch with --stacked-on creates a stacked branch."""
427
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
428
# we publish branch_tree with a reference to the mainline.
429
out, err = self.run_bzr(['push', '--stacked-on', trunk_tree.branch.base,
430
self.get_url('published')], working_dir='branch')
431
self.assertEqual('', out)
432
self.assertEqual('Created new stacked branch referring to %s.\n' %
433
trunk_tree.branch.base, err)
434
self.assertPublished(branch_tree.last_revision(),
435
trunk_tree.branch.base)
437
def test_push_new_branch_stacked_uses_parent_when_no_public_url(self):
438
"""When the parent has no public url the parent is used as-is."""
439
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
440
# now we do a stacked push, which should determine the public location
442
out, err = self.run_bzr(['push', '--stacked',
443
self.get_url('published')], working_dir='branch')
444
self.assertEqual('', out)
445
self.assertEqual('Created new stacked branch referring to %s.\n' %
446
trunk_tree.branch.base, err)
447
self.assertPublished(branch_tree.last_revision(),
448
trunk_tree.branch.base)
450
def test_push_new_branch_stacked_uses_parent_public(self):
451
"""Pushing a new branch with --stacked creates a stacked branch."""
452
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
453
# the trunk is published on a web server
454
self.transport_readonly_server = http_server.HttpServer
455
trunk_public = self.make_branch('public_trunk', format='1.9')
456
trunk_public.pull(trunk_tree.branch)
457
trunk_public_url = self.get_readonly_url('public_trunk')
458
trunk_tree.branch.set_public_branch(trunk_public_url)
459
# now we do a stacked push, which should determine the public location
461
out, err = self.run_bzr(['push', '--stacked',
462
self.get_url('published')], working_dir='branch')
463
self.assertEqual('', out)
464
self.assertEqual('Created new stacked branch referring to %s.\n' %
465
trunk_public_url, err)
466
self.assertPublished(branch_tree.last_revision(), trunk_public_url)
468
def test_push_new_branch_stacked_no_parent(self):
469
"""Pushing with --stacked and no parent branch errors."""
470
branch = self.make_branch_and_tree('branch', format='1.9')
471
# now we do a stacked push, which should fail as the place to refer too
472
# cannot be determined.
473
out, err = self.run_bzr_error(
474
['Could not determine branch to refer to\\.'], ['push', '--stacked',
475
self.get_url('published')], working_dir='branch')
476
self.assertEqual('', out)
477
self.assertFalse(self.get_transport('published').has('.'))
479
def test_push_notifies_default_stacking(self):
480
self.make_branch('stack_on', format='1.6')
481
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
482
self.make_branch('from', format='1.6')
483
out, err = self.run_bzr('push -d from to')
484
self.assertContainsRe(err,
485
'Using default stacking branch stack_on at .*')
487
def test_push_stacks_with_default_stacking_if_target_is_stackable(self):
488
self.make_branch('stack_on', format='1.6')
489
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
490
self.make_branch('from', format='pack-0.92')
491
out, err = self.run_bzr('push -d from to')
492
b = branch.Branch.open('to')
493
self.assertEqual('../stack_on', b.get_stacked_on_url())
495
def test_push_does_not_change_format_with_default_if_target_cannot(self):
496
self.make_branch('stack_on', format='pack-0.92')
497
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
498
self.make_branch('from', format='pack-0.92')
499
out, err = self.run_bzr('push -d from to')
500
b = branch.Branch.open('to')
501
self.assertRaises(errors.UnstackableBranchFormat, b.get_stacked_on_url)
503
def test_push_doesnt_create_broken_branch(self):
504
"""Pushing a new standalone branch works even when there's a default
505
stacking policy at the destination.
507
The new branch will preserve the repo format (even if it isn't the
508
default for the branch), and will be stacked when the repo format
509
allows (which means that the branch format isn't necessarly preserved).
511
self.make_repository('repo', shared=True, format='1.6')
512
builder = self.make_branch_builder('repo/local', format='pack-0.92')
513
builder.start_series()
514
builder.build_snapshot('rev-1', None, [
515
('add', ('', 'root-id', 'directory', '')),
516
('add', ('filename', 'f-id', 'file', 'content\n'))])
517
builder.build_snapshot('rev-2', ['rev-1'], [])
518
builder.build_snapshot('rev-3', ['rev-2'],
519
[('modify', ('f-id', 'new-content\n'))])
520
builder.finish_series()
521
branch = builder.get_branch()
522
# Push rev-1 to "trunk", so that we can stack on it.
523
self.run_bzr('push -d repo/local trunk -r 1')
524
# Set a default stacking policy so that new branches will automatically
526
self.make_bzrdir('.').get_config().set_default_stack_on('trunk')
527
# Push rev-2 to a new branch "remote". It will be stacked on "trunk".
528
out, err = self.run_bzr('push -d repo/local remote -r 2')
529
self.assertContainsRe(
530
err, 'Using default stacking branch trunk at .*')
531
# Push rev-3 onto "remote". If "remote" not stacked and is missing the
532
# fulltext record for f-id @ rev-1, then this will fail.
533
out, err = self.run_bzr('push -d repo/local remote -r 3')
535
def test_push_verbose_shows_log(self):
536
tree = self.make_branch_and_tree('source')
538
out, err = self.run_bzr('push -v -d source target')
539
# initial push contains log
540
self.assertContainsRe(out, 'rev1')
542
out, err = self.run_bzr('push -v -d source target')
543
# subsequent push contains log
544
self.assertContainsRe(out, 'rev2')
545
# subsequent log is accurate
546
self.assertNotContainsRe(out, 'rev1')
548
def test_push_from_subdir(self):
549
t = self.make_branch_and_tree('tree')
550
self.build_tree(['tree/dir/', 'tree/dir/file'])
551
t.add('dir', 'dir/file')
553
out, err = self.run_bzr('push ../../pushloc', working_dir='tree/dir')
554
self.assertEqual('', out)
555
self.assertEqual('Created new branch.\n', err)
558
class RedirectingMemoryTransport(memory.MemoryTransport):
560
def mkdir(self, relpath, mode=None):
561
if self._cwd == '/source/':
562
raise errors.RedirectRequested(self.abspath(relpath),
563
self.abspath('../target'),
565
elif self._cwd == '/infinite-loop/':
566
raise errors.RedirectRequested(self.abspath(relpath),
567
self.abspath('../infinite-loop'),
570
return super(RedirectingMemoryTransport, self).mkdir(
573
def get(self, relpath):
574
if self.clone(relpath)._cwd == '/infinite-loop/':
575
raise errors.RedirectRequested(self.abspath(relpath),
576
self.abspath('../infinite-loop'),
579
return super(RedirectingMemoryTransport, self).get(relpath)
581
def _redirected_to(self, source, target):
582
# We do accept redirections
583
return transport.get_transport(target)
586
class RedirectingMemoryServer(memory.MemoryServer):
588
def start_server(self):
589
self._dirs = {'/': None}
592
self._scheme = 'redirecting-memory+%s:///' % id(self)
593
transport.register_transport(self._scheme, self._memory_factory)
595
def _memory_factory(self, url):
596
result = RedirectingMemoryTransport(url)
597
result._dirs = self._dirs
598
result._files = self._files
599
result._locks = self._locks
602
def stop_server(self):
603
transport.unregister_transport(self._scheme, self._memory_factory)
606
class TestPushRedirect(tests.TestCaseWithTransport):
609
tests.TestCaseWithTransport.setUp(self)
610
self.memory_server = RedirectingMemoryServer()
611
self.start_server(self.memory_server)
612
# Make the branch and tree that we'll be pushing.
613
t = self.make_branch_and_tree('tree')
614
self.build_tree(['tree/file'])
618
def test_push_redirects_on_mkdir(self):
619
"""If the push requires a mkdir, push respects redirect requests.
621
This is added primarily to handle lp:/ URI support, so that users can
622
push to new branches by specifying lp:/ URIs.
624
destination_url = self.memory_server.get_url() + 'source'
625
self.run_bzr(['push', '-d', 'tree', destination_url])
627
local_revision = branch.Branch.open('tree').last_revision()
628
remote_revision = branch.Branch.open(
629
self.memory_server.get_url() + 'target').last_revision()
630
self.assertEqual(remote_revision, local_revision)
632
def test_push_gracefully_handles_too_many_redirects(self):
633
"""Push fails gracefully if the mkdir generates a large number of
636
destination_url = self.memory_server.get_url() + 'infinite-loop'
637
out, err = self.run_bzr_error(
638
['Too many redirections trying to make %s\\.\n'
639
% re.escape(destination_url)],
640
['push', '-d', 'tree', destination_url], retcode=3)
641
self.assertEqual('', out)
644
class TestPushStrictMixin(object):
646
def make_local_branch_and_tree(self):
647
self.tree = self.make_branch_and_tree('local')
648
self.build_tree_contents([('local/file', 'initial')])
649
self.tree.add('file')
650
self.tree.commit('adding file', rev_id='added')
651
self.build_tree_contents([('local/file', 'modified')])
652
self.tree.commit('modify file', rev_id='modified')
654
def set_config_push_strict(self, value):
655
# set config var (any of bazaar.conf, locations.conf, branch.conf
657
conf = self.tree.branch.get_config()
658
conf.set_user_option('push_strict', value)
660
_default_command = ['push', '../to']
661
_default_wd = 'local'
662
_default_errors = ['Working tree ".*/local/" has uncommitted '
663
'changes \(See bzr status\)\.',]
664
_default_additional_error = 'Use --no-strict to force the push.\n'
665
_default_additional_warning = 'Uncommitted changes will not be pushed.'
668
def assertPushFails(self, args):
669
out, err = self.run_bzr_error(self._default_errors,
670
self._default_command + args,
671
working_dir=self._default_wd, retcode=3)
672
self.assertContainsRe(err, self._default_additional_error)
674
def assertPushSucceeds(self, args, with_warning=False, revid_to_push=None):
676
error_regexes = self._default_errors
679
out, err = self.run_bzr(self._default_command + args,
680
working_dir=self._default_wd,
681
error_regexes=error_regexes)
683
self.assertContainsRe(err, self._default_additional_warning)
685
self.assertNotContainsRe(err, self._default_additional_warning)
686
branch_from = branch.Branch.open(self._default_wd)
687
if revid_to_push is None:
688
revid_to_push = branch_from.last_revision()
689
branch_to = branch.Branch.open('to')
690
repo_to = branch_to.repository
691
self.assertTrue(repo_to.has_revision(revid_to_push))
692
self.assertEqual(revid_to_push, branch_to.last_revision())
696
class TestPushStrictWithoutChanges(tests.TestCaseWithTransport,
697
TestPushStrictMixin):
700
super(TestPushStrictWithoutChanges, self).setUp()
701
self.make_local_branch_and_tree()
703
def test_push_default(self):
704
self.assertPushSucceeds([])
706
def test_push_strict(self):
707
self.assertPushSucceeds(['--strict'])
709
def test_push_no_strict(self):
710
self.assertPushSucceeds(['--no-strict'])
712
def test_push_config_var_strict(self):
713
self.set_config_push_strict('true')
714
self.assertPushSucceeds([])
716
def test_push_config_var_no_strict(self):
717
self.set_config_push_strict('false')
718
self.assertPushSucceeds([])
721
class TestPushStrictWithChanges(tests.TestCaseWithTransport,
722
TestPushStrictMixin):
724
_changes_type = None # Set by load_tests
727
super(TestPushStrictWithChanges, self).setUp()
728
# Apply the changes defined in load_tests: one of _uncommitted_changes,
729
# _pending_merges or _out_of_sync_trees
730
getattr(self, self._changes_type)()
732
def _uncommitted_changes(self):
733
self.make_local_branch_and_tree()
734
# Make a change without committing it
735
self.build_tree_contents([('local/file', 'in progress')])
737
def _pending_merges(self):
738
self.make_local_branch_and_tree()
739
# Create 'other' branch containing a new file
740
other_bzrdir = self.tree.bzrdir.sprout('other')
741
other_tree = other_bzrdir.open_workingtree()
742
self.build_tree_contents([('other/other-file', 'other')])
743
other_tree.add('other-file')
744
other_tree.commit('other commit', rev_id='other')
745
# Merge and revert, leaving a pending merge
746
self.tree.merge_from_branch(other_tree.branch)
747
self.tree.revert(filenames=['other-file'], backups=False)
749
def _out_of_sync_trees(self):
750
self.make_local_branch_and_tree()
751
self.run_bzr(['checkout', '--lightweight', 'local', 'checkout'])
752
# Make a change and commit it
753
self.build_tree_contents([('local/file', 'modified in local')])
754
self.tree.commit('modify file', rev_id='modified-in-local')
755
# Exercise commands from the checkout directory
756
self._default_wd = 'checkout'
757
self._default_errors = ["Working tree is out of date, please run"
760
def test_push_default(self):
761
self.assertPushSucceeds([], with_warning=True)
763
def test_push_with_revision(self):
764
self.assertPushSucceeds(['-r', 'revid:added'], revid_to_push='added')
766
def test_push_no_strict(self):
767
self.assertPushSucceeds(['--no-strict'])
769
def test_push_strict_with_changes(self):
770
self.assertPushFails(['--strict'])
772
def test_push_respect_config_var_strict(self):
773
self.set_config_push_strict('true')
774
self.assertPushFails([])
776
def test_push_bogus_config_var_ignored(self):
777
self.set_config_push_strict("I don't want you to be strict")
778
self.assertPushSucceeds([], with_warning=True)
780
def test_push_no_strict_command_line_override_config(self):
781
self.set_config_push_strict('yES')
782
self.assertPushFails([])
783
self.assertPushSucceeds(['--no-strict'])
785
def test_push_strict_command_line_override_config(self):
786
self.set_config_push_strict('oFF')
787
self.assertPushFails(['--strict'])
788
self.assertPushSucceeds([])
791
class TestPushForeign(tests.TestCaseWithTransport):
794
super(TestPushForeign, self).setUp()
795
test_foreign.register_dummy_foreign_for_test(self)
797
def make_dummy_builder(self, relpath):
798
builder = self.make_branch_builder(
799
relpath, format=test_foreign.DummyForeignVcsDirFormat())
800
builder.build_snapshot('revid', None,
801
[('add', ('', 'TREE_ROOT', 'directory', None)),
802
('add', ('foo', 'fooid', 'file', 'bar'))])
805
def test_no_roundtripping(self):
806
target_branch = self.make_dummy_builder('dp').get_branch()
807
source_tree = self.make_branch_and_tree("dc")
808
output, error = self.run_bzr("push -d dc dp", retcode=3)
809
self.assertEquals("", output)
810
self.assertEquals(error, "bzr: ERROR: It is not possible to losslessly"
811
" push to dummy. You may want to use dpush instead.\n")