1
# Copyright (C) 2005, 2007, 2008 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 http_server
35
from bzrlib.transport import memory
38
def load_tests(standard_tests, module, loader):
39
"""Multiply tests for the push command."""
40
result = loader.suiteClass()
42
# one for each king of change
43
changes_tests, remaining_tests = tests.split_suite_by_condition(
44
standard_tests, tests.condition_isinstance((
45
TestPushStrictWithChanges,
49
dict(_changes_type= '_uncommitted_changes')),
51
dict(_changes_type= '_pending_merges')),
53
dict(_changes_type= '_out_of_sync_trees')),
55
tests.multiply_tests(changes_tests, changes_scenarios, result)
56
# No parametrization for the remaining tests
57
result.addTests(remaining_tests)
62
class TestPush(tests.TestCaseWithTransport):
64
def test_push_error_on_vfs_http(self):
65
""" pushing a branch to a HTTP server fails cleanly. """
66
# the trunk is published on a web server
67
self.transport_readonly_server = http_server.HttpServer
68
self.make_branch('source')
69
public_url = self.get_readonly_url('target')
70
self.run_bzr_error(['http does not support mkdir'],
74
def test_push_remember(self):
75
"""Push changes from one branch to another and test push location."""
76
transport = self.get_transport()
77
tree_a = self.make_branch_and_tree('branch_a')
78
branch_a = tree_a.branch
79
self.build_tree(['branch_a/a'])
81
tree_a.commit('commit a')
82
tree_b = branch_a.bzrdir.sprout('branch_b').open_workingtree()
83
branch_b = tree_b.branch
84
tree_c = branch_a.bzrdir.sprout('branch_c').open_workingtree()
85
branch_c = tree_c.branch
86
self.build_tree(['branch_a/b'])
88
tree_a.commit('commit b')
89
self.build_tree(['branch_b/c'])
91
tree_b.commit('commit c')
92
# initial push location must be empty
93
self.assertEqual(None, branch_b.get_push_location())
95
# test push for failure without push location set
96
out = self.run_bzr('push', working_dir='branch_a', retcode=3)
97
self.assertEquals(out,
98
('','bzr: ERROR: No push location known or specified.\n'))
100
# test not remembered if cannot actually push
101
self.run_bzr('push path/which/doesnt/exist',
102
working_dir='branch_a', retcode=3)
103
out = self.run_bzr('push', working_dir='branch_a', retcode=3)
105
('', 'bzr: ERROR: No push location known or specified.\n'),
108
# test implicit --remember when no push location set, push fails
109
out = self.run_bzr('push ../branch_b',
110
working_dir='branch_a', retcode=3)
111
self.assertEquals(out,
112
('','bzr: ERROR: These branches have diverged. '
113
'See "bzr help diverged-branches" for more information.\n'))
114
self.assertEquals(osutils.abspath(branch_a.get_push_location()),
115
osutils.abspath(branch_b.bzrdir.root_transport.base))
117
# test implicit --remember after resolving previous failure
118
uncommit.uncommit(branch=branch_b, tree=tree_b)
119
transport.delete('branch_b/c')
120
out, err = self.run_bzr('push', working_dir='branch_a')
121
path = branch_a.get_push_location()
122
self.assertEquals(out,
123
'Using saved push location: %s\n'
124
% urlutils.local_path_from_url(path))
125
self.assertEqual(err,
126
'All changes applied successfully.\n'
127
'Pushed up to revision 2.\n')
128
self.assertEqual(path,
129
branch_b.bzrdir.root_transport.base)
130
# test explicit --remember
131
self.run_bzr('push ../branch_c --remember', working_dir='branch_a')
132
self.assertEquals(branch_a.get_push_location(),
133
branch_c.bzrdir.root_transport.base)
135
def test_push_without_tree(self):
136
# bzr push from a branch that does not have a checkout should work.
137
b = self.make_branch('.')
138
out, err = self.run_bzr('push pushed-location')
139
self.assertEqual('', out)
140
self.assertEqual('Created new branch.\n', err)
141
b2 = branch.Branch.open('pushed-location')
142
self.assertEndsWith(b2.base, 'pushed-location/')
144
def test_push_new_branch_revision_count(self):
145
# bzr push of a branch with revisions to a new location
146
# should print the number of revisions equal to the length of the
148
t = self.make_branch_and_tree('tree')
149
self.build_tree(['tree/file'])
152
out, err = self.run_bzr('push -d tree pushed-to')
153
self.assertEqual('', out)
154
self.assertEqual('Created new branch.\n', err)
156
def test_push_only_pushes_history(self):
157
# Knit branches should only push the history for the current revision.
158
format = bzrdir.BzrDirMetaFormat1()
159
format.repository_format = knitrepo.RepositoryFormatKnit1()
160
shared_repo = self.make_repository('repo', format=format, shared=True)
161
shared_repo.set_make_working_trees(True)
163
def make_shared_tree(path):
164
shared_repo.bzrdir.root_transport.mkdir(path)
165
shared_repo.bzrdir.create_branch_convenience('repo/' + path)
166
return workingtree.WorkingTree.open('repo/' + path)
167
tree_a = make_shared_tree('a')
168
self.build_tree(['repo/a/file'])
170
tree_a.commit('commit a-1', rev_id='a-1')
171
f = open('repo/a/file', 'ab')
172
f.write('more stuff\n')
174
tree_a.commit('commit a-2', rev_id='a-2')
176
tree_b = make_shared_tree('b')
177
self.build_tree(['repo/b/file'])
179
tree_b.commit('commit b-1', rev_id='b-1')
181
self.assertTrue(shared_repo.has_revision('a-1'))
182
self.assertTrue(shared_repo.has_revision('a-2'))
183
self.assertTrue(shared_repo.has_revision('b-1'))
185
# Now that we have a repository with shared files, make sure
186
# that things aren't copied out by a 'push'
187
self.run_bzr('push ../../push-b', working_dir='repo/b')
188
pushed_tree = workingtree.WorkingTree.open('push-b')
189
pushed_repo = pushed_tree.branch.repository
190
self.assertFalse(pushed_repo.has_revision('a-1'))
191
self.assertFalse(pushed_repo.has_revision('a-2'))
192
self.assertTrue(pushed_repo.has_revision('b-1'))
194
def test_push_funky_id(self):
195
t = self.make_branch_and_tree('tree')
196
self.build_tree(['tree/filename'])
197
t.add('filename', 'funky-chars<>%&;"\'')
198
t.commit('commit filename')
199
self.run_bzr('push -d tree new-tree')
201
def test_push_dash_d(self):
202
t = self.make_branch_and_tree('from')
203
t.commit(allow_pointless=True,
204
message='first commit')
205
self.run_bzr('push -d from to-one')
206
self.failUnlessExists('to-one')
207
self.run_bzr('push -d %s %s'
208
% tuple(map(urlutils.local_path_to_url, ['from', 'to-two'])))
209
self.failUnlessExists('to-two')
211
def test_push_smart_non_stacked_streaming_acceptance(self):
212
self.setup_smart_server_with_call_log()
213
t = self.make_branch_and_tree('from')
214
t.commit(allow_pointless=True, message='first commit')
215
self.reset_smart_call_log()
216
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
217
# This figure represent the amount of work to perform this use case. It
218
# is entirely ok to reduce this number if a test fails due to rpc_count
219
# being too low. If rpc_count increases, more network roundtrips have
220
# become necessary for this use case. Please do not adjust this number
221
# upwards without agreement from bzr's network support maintainers.
222
self.assertLength(9, self.hpss_calls)
224
def test_push_smart_stacked_streaming_acceptance(self):
225
self.setup_smart_server_with_call_log()
226
parent = self.make_branch_and_tree('parent', format='1.9')
227
parent.commit(message='first commit')
228
local = parent.bzrdir.sprout('local').open_workingtree()
229
local.commit(message='local commit')
230
self.reset_smart_call_log()
231
self.run_bzr(['push', '--stacked', '--stacked-on', '../parent',
232
self.get_url('public')], working_dir='local')
233
# This figure represent the amount of work to perform this use case. It
234
# is entirely ok to reduce this number if a test fails due to rpc_count
235
# being too low. If rpc_count increases, more network roundtrips have
236
# become necessary for this use case. Please do not adjust this number
237
# upwards without agreement from bzr's network support maintainers.
238
self.assertLength(14, self.hpss_calls)
239
remote = branch.Branch.open('public')
240
self.assertEndsWith(remote.get_stacked_on_url(), '/parent')
242
def test_push_smart_tags_streaming_acceptance(self):
243
self.setup_smart_server_with_call_log()
244
t = self.make_branch_and_tree('from')
245
rev_id = t.commit(allow_pointless=True, message='first commit')
246
t.branch.tags.set_tag('new-tag', rev_id)
247
self.reset_smart_call_log()
248
self.run_bzr(['push', self.get_url('to-one')], working_dir='from')
249
# This figure represent the amount of work to perform this use case. It
250
# is entirely ok to reduce this number if a test fails due to rpc_count
251
# being too low. If rpc_count increases, more network roundtrips have
252
# become necessary for this use case. Please do not adjust this number
253
# upwards without agreement from bzr's network support maintainers.
254
self.assertLength(11, self.hpss_calls)
256
def test_push_smart_incremental_acceptance(self):
257
self.setup_smart_server_with_call_log()
258
t = self.make_branch_and_tree('from')
259
rev_id1 = t.commit(allow_pointless=True, message='first commit')
260
rev_id2 = t.commit(allow_pointless=True, message='second commit')
262
['push', self.get_url('to-one'), '-r1'], working_dir='from')
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(11, self.hpss_calls)
272
def test_push_smart_with_default_stacking_url_path_segment(self):
273
# If the default stacked-on location is a path element then branches
274
# we push there over the smart server are stacked and their
275
# stacked_on_url is that exact path segment. Added to nail bug 385132.
276
self.setup_smart_server_with_call_log()
277
self.make_branch('stack-on', format='1.9')
278
self.make_bzrdir('.').get_config().set_default_stack_on(
280
self.make_branch('from', format='1.9')
281
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
282
b = branch.Branch.open(self.get_url('to'))
283
self.assertEqual('/extra/stack-on', b.get_stacked_on_url())
285
def test_push_smart_with_default_stacking_relative_path(self):
286
# If the default stacked-on location is a relative path then branches
287
# we push there over the smart server are stacked and their
288
# stacked_on_url is a relative path. Added to nail bug 385132.
289
self.setup_smart_server_with_call_log()
290
self.make_branch('stack-on', format='1.9')
291
self.make_bzrdir('.').get_config().set_default_stack_on('stack-on')
292
self.make_branch('from', format='1.9')
293
out, err = self.run_bzr(['push', '-d', 'from', self.get_url('to')])
294
b = branch.Branch.open(self.get_url('to'))
295
self.assertEqual('../stack-on', b.get_stacked_on_url())
297
def create_simple_tree(self):
298
tree = self.make_branch_and_tree('tree')
299
self.build_tree(['tree/a'])
300
tree.add(['a'], ['a-id'])
301
tree.commit('one', rev_id='r1')
304
def test_push_create_prefix(self):
305
"""'bzr push --create-prefix' will create leading directories."""
306
tree = self.create_simple_tree()
308
self.run_bzr_error(['Parent directory of ../new/tree does not exist'],
311
self.run_bzr('push ../new/tree --create-prefix',
313
new_tree = workingtree.WorkingTree.open('new/tree')
314
self.assertEqual(tree.last_revision(), new_tree.last_revision())
315
self.failUnlessExists('new/tree/a')
317
def test_push_use_existing(self):
318
"""'bzr push --use-existing-dir' can push into an existing dir.
320
By default, 'bzr push' will not use an existing, non-versioned dir.
322
tree = self.create_simple_tree()
323
self.build_tree(['target/'])
325
self.run_bzr_error(['Target directory ../target already exists',
326
'Supply --use-existing-dir',
328
'push ../target', working_dir='tree')
330
self.run_bzr('push --use-existing-dir ../target',
333
new_tree = workingtree.WorkingTree.open('target')
334
self.assertEqual(tree.last_revision(), new_tree.last_revision())
335
# The push should have created target/a
336
self.failUnlessExists('target/a')
338
def test_push_onto_repo(self):
339
"""We should be able to 'bzr push' into an existing bzrdir."""
340
tree = self.create_simple_tree()
341
repo = self.make_repository('repo', shared=True)
343
self.run_bzr('push ../repo',
346
# Pushing onto an existing bzrdir will create a repository and
347
# branch as needed, but will only create a working tree if there was
349
self.assertRaises(errors.NoWorkingTree,
350
workingtree.WorkingTree.open, 'repo')
351
new_branch = branch.Branch.open('repo')
352
self.assertEqual(tree.last_revision(), new_branch.last_revision())
354
def test_push_onto_just_bzrdir(self):
355
"""We don't handle when the target is just a bzrdir.
357
Because you shouldn't be able to create *just* a bzrdir in the wild.
359
# TODO: jam 20070109 Maybe it would be better to create the repository
361
tree = self.create_simple_tree()
362
a_bzrdir = self.make_bzrdir('dir')
364
self.run_bzr_error(['At ../dir you have a valid .bzr control'],
368
def test_push_with_revisionspec(self):
369
"""We should be able to push a revision older than the tip."""
370
tree_from = self.make_branch_and_tree('from')
371
tree_from.commit("One.", rev_id="from-1")
372
tree_from.commit("Two.", rev_id="from-2")
374
self.run_bzr('push -r1 ../to', working_dir='from')
376
tree_to = workingtree.WorkingTree.open('to')
377
repo_to = tree_to.branch.repository
378
self.assertTrue(repo_to.has_revision('from-1'))
379
self.assertFalse(repo_to.has_revision('from-2'))
380
self.assertEqual(tree_to.branch.last_revision_info()[1], 'from-1')
383
['bzr: ERROR: bzr push --revision '
384
'takes exactly one revision identifier\n'],
385
'push -r0..2 ../to', working_dir='from')
387
def create_trunk_and_feature_branch(self):
389
trunk_tree = self.make_branch_and_tree('target',
391
trunk_tree.commit('mainline')
392
# and a branch from it
393
branch_tree = self.make_branch_and_tree('branch',
395
branch_tree.pull(trunk_tree.branch)
396
branch_tree.branch.set_parent(trunk_tree.branch.base)
397
# with some work on it
398
branch_tree.commit('moar work plz')
399
return trunk_tree, branch_tree
401
def assertPublished(self, branch_revid, stacked_on):
402
"""Assert that the branch 'published' has been published correctly."""
403
published_branch = branch.Branch.open('published')
404
# The published branch refers to the mainline
405
self.assertEqual(stacked_on, published_branch.get_stacked_on_url())
406
# and the branch's work was pushed
407
self.assertTrue(published_branch.repository.has_revision(branch_revid))
409
def test_push_new_branch_stacked_on(self):
410
"""Pushing a new branch with --stacked-on creates a stacked branch."""
411
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
412
# we publish branch_tree with a reference to the mainline.
413
out, err = self.run_bzr(['push', '--stacked-on', trunk_tree.branch.base,
414
self.get_url('published')], working_dir='branch')
415
self.assertEqual('', out)
416
self.assertEqual('Created new stacked branch referring to %s.\n' %
417
trunk_tree.branch.base, err)
418
self.assertPublished(branch_tree.last_revision(),
419
trunk_tree.branch.base)
421
def test_push_new_branch_stacked_uses_parent_when_no_public_url(self):
422
"""When the parent has no public url the parent is used as-is."""
423
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
424
# now we do a stacked push, which should determine the public location
426
out, err = self.run_bzr(['push', '--stacked',
427
self.get_url('published')], working_dir='branch')
428
self.assertEqual('', out)
429
self.assertEqual('Created new stacked branch referring to %s.\n' %
430
trunk_tree.branch.base, err)
431
self.assertPublished(branch_tree.last_revision(),
432
trunk_tree.branch.base)
434
def test_push_new_branch_stacked_uses_parent_public(self):
435
"""Pushing a new branch with --stacked creates a stacked branch."""
436
trunk_tree, branch_tree = self.create_trunk_and_feature_branch()
437
# the trunk is published on a web server
438
self.transport_readonly_server = http_server.HttpServer
439
trunk_public = self.make_branch('public_trunk', format='1.9')
440
trunk_public.pull(trunk_tree.branch)
441
trunk_public_url = self.get_readonly_url('public_trunk')
442
trunk_tree.branch.set_public_branch(trunk_public_url)
443
# now we do a stacked push, which should determine the public location
445
out, err = self.run_bzr(['push', '--stacked',
446
self.get_url('published')], working_dir='branch')
447
self.assertEqual('', out)
448
self.assertEqual('Created new stacked branch referring to %s.\n' %
449
trunk_public_url, err)
450
self.assertPublished(branch_tree.last_revision(), trunk_public_url)
452
def test_push_new_branch_stacked_no_parent(self):
453
"""Pushing with --stacked and no parent branch errors."""
454
branch = self.make_branch_and_tree('branch', format='1.9')
455
# now we do a stacked push, which should fail as the place to refer too
456
# cannot be determined.
457
out, err = self.run_bzr_error(
458
['Could not determine branch to refer to\\.'], ['push', '--stacked',
459
self.get_url('published')], working_dir='branch')
460
self.assertEqual('', out)
461
self.assertFalse(self.get_transport('published').has('.'))
463
def test_push_notifies_default_stacking(self):
464
self.make_branch('stack_on', format='1.6')
465
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
466
self.make_branch('from', format='1.6')
467
out, err = self.run_bzr('push -d from to')
468
self.assertContainsRe(err,
469
'Using default stacking branch stack_on at .*')
471
def test_push_stacks_with_default_stacking_if_target_is_stackable(self):
472
self.make_branch('stack_on', format='1.6')
473
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
474
self.make_branch('from', format='pack-0.92')
475
out, err = self.run_bzr('push -d from to')
476
b = branch.Branch.open('to')
477
self.assertEqual('../stack_on', b.get_stacked_on_url())
479
def test_push_does_not_change_format_with_default_if_target_cannot(self):
480
self.make_branch('stack_on', format='pack-0.92')
481
self.make_bzrdir('.').get_config().set_default_stack_on('stack_on')
482
self.make_branch('from', format='pack-0.92')
483
out, err = self.run_bzr('push -d from to')
484
b = branch.Branch.open('to')
485
self.assertRaises(errors.UnstackableBranchFormat, b.get_stacked_on_url)
487
def test_push_doesnt_create_broken_branch(self):
488
"""Pushing a new standalone branch works even when there's a default
489
stacking policy at the destination.
491
The new branch will preserve the repo format (even if it isn't the
492
default for the branch), and will be stacked when the repo format
493
allows (which means that the branch format isn't necessarly preserved).
495
self.make_repository('repo', shared=True, format='1.6')
496
builder = self.make_branch_builder('repo/local', format='pack-0.92')
497
builder.start_series()
498
builder.build_snapshot('rev-1', None, [
499
('add', ('', 'root-id', 'directory', '')),
500
('add', ('filename', 'f-id', 'file', 'content\n'))])
501
builder.build_snapshot('rev-2', ['rev-1'], [])
502
builder.build_snapshot('rev-3', ['rev-2'],
503
[('modify', ('f-id', 'new-content\n'))])
504
builder.finish_series()
505
branch = builder.get_branch()
506
# Push rev-1 to "trunk", so that we can stack on it.
507
self.run_bzr('push -d repo/local trunk -r 1')
508
# Set a default stacking policy so that new branches will automatically
510
self.make_bzrdir('.').get_config().set_default_stack_on('trunk')
511
# Push rev-2 to a new branch "remote". It will be stacked on "trunk".
512
out, err = self.run_bzr('push -d repo/local remote -r 2')
513
self.assertContainsRe(
514
err, 'Using default stacking branch trunk at .*')
515
# Push rev-3 onto "remote". If "remote" not stacked and is missing the
516
# fulltext record for f-id @ rev-1, then this will fail.
517
out, err = self.run_bzr('push -d repo/local remote -r 3')
519
def test_push_verbose_shows_log(self):
520
tree = self.make_branch_and_tree('source')
522
out, err = self.run_bzr('push -v -d source target')
523
# initial push contains log
524
self.assertContainsRe(out, 'rev1')
526
out, err = self.run_bzr('push -v -d source target')
527
# subsequent push contains log
528
self.assertContainsRe(out, 'rev2')
529
# subsequent log is accurate
530
self.assertNotContainsRe(out, 'rev1')
532
def test_push_from_subdir(self):
533
t = self.make_branch_and_tree('tree')
534
self.build_tree(['tree/dir/', 'tree/dir/file'])
535
t.add('dir', 'dir/file')
537
out, err = self.run_bzr('push ../../pushloc', working_dir='tree/dir')
538
self.assertEqual('', out)
539
self.assertEqual('Created new branch.\n', err)
542
class RedirectingMemoryTransport(memory.MemoryTransport):
544
def mkdir(self, relpath, mode=None):
545
if self._cwd == '/source/':
546
raise errors.RedirectRequested(self.abspath(relpath),
547
self.abspath('../target'),
549
elif self._cwd == '/infinite-loop/':
550
raise errors.RedirectRequested(self.abspath(relpath),
551
self.abspath('../infinite-loop'),
554
return super(RedirectingMemoryTransport, self).mkdir(
557
def get(self, relpath):
558
if self.clone(relpath)._cwd == '/infinite-loop/':
559
raise errors.RedirectRequested(self.abspath(relpath),
560
self.abspath('../infinite-loop'),
563
return super(RedirectingMemoryTransport, self).get(relpath)
565
def _redirected_to(self, source, target):
566
# We do accept redirections
567
return transport.get_transport(target)
570
class RedirectingMemoryServer(memory.MemoryServer):
573
self._dirs = {'/': None}
576
self._scheme = 'redirecting-memory+%s:///' % id(self)
577
transport.register_transport(self._scheme, self._memory_factory)
579
def _memory_factory(self, url):
580
result = RedirectingMemoryTransport(url)
581
result._dirs = self._dirs
582
result._files = self._files
583
result._locks = self._locks
587
transport.unregister_transport(self._scheme, self._memory_factory)
590
class TestPushRedirect(tests.TestCaseWithTransport):
593
tests.TestCaseWithTransport.setUp(self)
594
self.memory_server = RedirectingMemoryServer()
595
self.start_server(self.memory_server)
596
# Make the branch and tree that we'll be pushing.
597
t = self.make_branch_and_tree('tree')
598
self.build_tree(['tree/file'])
602
def test_push_redirects_on_mkdir(self):
603
"""If the push requires a mkdir, push respects redirect requests.
605
This is added primarily to handle lp:/ URI support, so that users can
606
push to new branches by specifying lp:/ URIs.
608
destination_url = self.memory_server.get_url() + 'source'
609
self.run_bzr(['push', '-d', 'tree', destination_url])
611
local_revision = branch.Branch.open('tree').last_revision()
612
remote_revision = branch.Branch.open(
613
self.memory_server.get_url() + 'target').last_revision()
614
self.assertEqual(remote_revision, local_revision)
616
def test_push_gracefully_handles_too_many_redirects(self):
617
"""Push fails gracefully if the mkdir generates a large number of
620
destination_url = self.memory_server.get_url() + 'infinite-loop'
621
out, err = self.run_bzr_error(
622
['Too many redirections trying to make %s\\.\n'
623
% re.escape(destination_url)],
624
['push', '-d', 'tree', destination_url], retcode=3)
625
self.assertEqual('', out)
628
class TestPushStrictMixin(object):
630
def make_local_branch_and_tree(self):
631
self.tree = self.make_branch_and_tree('local')
632
self.build_tree_contents([('local/file', 'initial')])
633
self.tree.add('file')
634
self.tree.commit('adding file', rev_id='added')
635
self.build_tree_contents([('local/file', 'modified')])
636
self.tree.commit('modify file', rev_id='modified')
638
def set_config_push_strict(self, value):
639
# set config var (any of bazaar.conf, locations.conf, branch.conf
641
conf = self.tree.branch.get_config()
642
conf.set_user_option('push_strict', value)
644
_default_command = ['push', '../to']
645
_default_wd = 'local'
646
_default_errors = ['Working tree ".*/local/" has uncommitted '
647
'changes \(See bzr status\)\.',]
648
_default_pushed_revid = 'modified'
650
def assertPushFails(self, args):
651
self.run_bzr_error(self._default_errors, self._default_command + args,
652
working_dir=self._default_wd, retcode=3)
654
def assertPushSucceeds(self, args, pushed_revid=None):
655
self.run_bzr(self._default_command + args,
656
working_dir=self._default_wd)
657
if pushed_revid is None:
658
pushed_revid = self._default_pushed_revid
659
tree_to = workingtree.WorkingTree.open('to')
660
repo_to = tree_to.branch.repository
661
self.assertTrue(repo_to.has_revision(pushed_revid))
662
self.assertEqual(tree_to.branch.last_revision_info()[1], pushed_revid)
666
class TestPushStrictWithoutChanges(tests.TestCaseWithTransport,
667
TestPushStrictMixin):
670
super(TestPushStrictWithoutChanges, self).setUp()
671
self.make_local_branch_and_tree()
673
def test_push_default(self):
674
self.assertPushSucceeds([])
676
def test_push_strict(self):
677
self.assertPushSucceeds(['--strict'])
679
def test_push_no_strict(self):
680
self.assertPushSucceeds(['--no-strict'])
682
def test_push_config_var_strict(self):
683
self.set_config_push_strict('true')
684
self.assertPushSucceeds([])
686
def test_push_config_var_no_strict(self):
687
self.set_config_push_strict('false')
688
self.assertPushSucceeds([])
691
class TestPushStrictWithChanges(tests.TestCaseWithTransport,
692
TestPushStrictMixin):
694
_changes_type = None # Set by load_tests
697
super(TestPushStrictWithChanges, self).setUp()
698
# Apply the changes defined in load_tests: one of _uncommitted_changes,
699
# _pending_merges or _out_of_sync_trees
700
getattr(self, self._changes_type)()
702
def _uncommitted_changes(self):
703
self.make_local_branch_and_tree()
704
# Make a change without committing it
705
self.build_tree_contents([('local/file', 'in progress')])
707
def _pending_merges(self):
708
self.make_local_branch_and_tree()
709
# Create 'other' branch containing a new file
710
other_bzrdir = self.tree.bzrdir.sprout('other')
711
other_tree = other_bzrdir.open_workingtree()
712
self.build_tree_contents([('other/other-file', 'other')])
713
other_tree.add('other-file')
714
other_tree.commit('other commit', rev_id='other')
715
# Merge and revert, leaving a pending merge
716
self.tree.merge_from_branch(other_tree.branch)
717
self.tree.revert(filenames=['other-file'], backups=False)
719
def _out_of_sync_trees(self):
720
self.make_local_branch_and_tree()
721
self.run_bzr(['checkout', '--lightweight', 'local', 'checkout'])
722
# Make a change and commit it
723
self.build_tree_contents([('local/file', 'modified in local')])
724
self.tree.commit('modify file', rev_id='modified-in-local')
725
# Exercise commands from the checkout directory
726
self._default_wd = 'checkout'
727
self._default_errors = ["Working tree is out of date, please run"
729
self._default_pushed_revid = 'modified-in-local'
731
def test_push_default(self):
732
self.assertPushFails([])
734
def test_push_with_revision(self):
735
self.assertPushSucceeds(['-r', 'revid:added'], pushed_revid='added')
737
def test_push_no_strict(self):
738
self.assertPushSucceeds(['--no-strict'])
740
def test_push_strict_with_changes(self):
741
self.assertPushFails(['--strict'])
743
def test_push_respect_config_var_strict(self):
744
self.set_config_push_strict('true')
745
self.assertPushFails([])
747
def test_push_bogus_config_var_ignored(self):
748
self.set_config_push_strict("I don't want you to be strict")
749
self.assertPushFails([])
751
def test_push_no_strict_command_line_override_config(self):
752
self.set_config_push_strict('yES')
753
self.assertPushFails([])
754
self.assertPushSucceeds(['--no-strict'])
756
def test_push_strict_command_line_override_config(self):
757
self.set_config_push_strict('oFF')
758
self.assertPushFails(['--strict'])
759
self.assertPushSucceeds([])