1
# Copyright (C) 2007-2012 Canonical Ltd
2
# -*- coding: utf-8 -*-
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
"""Tests for the switch command of bzr."""
23
from bzrlib.controldir import ControlDir
29
from bzrlib.workingtree import WorkingTree
30
from bzrlib.tests import (
31
TestCaseWithTransport,
34
from bzrlib.tests.features import UnicodeFilenameFeature
35
from bzrlib.directory_service import directories
37
from bzrlib.tests.matchers import ContainsNoVfsCalls
40
class TestSwitch(TestCaseWithTransport):
42
def _create_sample_tree(self):
43
tree = self.make_branch_and_tree('branch-1')
44
self.build_tree(['branch-1/file-1', 'branch-1/file-2'])
51
def test_switch_up_to_date_light_checkout(self):
52
self.make_branch_and_tree('branch')
53
self.run_bzr('branch branch branch2')
54
self.run_bzr('checkout --lightweight branch checkout')
56
out, err = self.run_bzr('switch ../branch2')
57
self.assertContainsRe(err, 'Tree is up to date at revision 0.\n')
58
self.assertContainsRe(err, 'Switched to branch: .*/branch2.\n')
59
self.assertEqual('', out)
61
def test_switch_out_of_date_light_checkout(self):
62
self.make_branch_and_tree('branch')
63
self.run_bzr('branch branch branch2')
64
self.build_tree(['branch2/file'])
65
self.run_bzr('add branch2/file')
66
self.run_bzr('commit -m add-file branch2')
67
self.run_bzr('checkout --lightweight branch checkout')
69
out, err = self.run_bzr('switch ../branch2')
70
#self.assertContainsRe(err, '\+N file')
71
self.assertContainsRe(err, 'Updated to revision 1.\n')
72
self.assertContainsRe(err, 'Switched to branch: .*/branch2.\n')
73
self.assertEqual('', out)
75
def _test_switch_nick(self, lightweight):
76
"""Check that the nick gets switched too."""
77
tree1 = self.make_branch_and_tree('branch1')
78
tree2 = self.make_branch_and_tree('branch2')
79
tree2.pull(tree1.branch)
80
checkout = tree1.branch.create_checkout('checkout',
81
lightweight=lightweight)
82
self.assertEqual(checkout.branch.nick, tree1.branch.nick)
83
self.assertEqual(checkout.branch.get_config().has_explicit_nickname(),
85
self.run_bzr('switch branch2', working_dir='checkout')
87
# we need to get the tree again, otherwise we don't get the new branch
88
checkout = WorkingTree.open('checkout')
89
self.assertEqual(checkout.branch.nick, tree2.branch.nick)
90
self.assertEqual(checkout.branch.get_config().has_explicit_nickname(),
93
def test_switch_nick(self):
94
self._test_switch_nick(lightweight=False)
96
def test_switch_nick_lightweight(self):
97
self._test_switch_nick(lightweight=True)
99
def _test_switch_explicit_nick(self, lightweight):
100
"""Check that the nick gets switched too."""
101
tree1 = self.make_branch_and_tree('branch1')
102
tree2 = self.make_branch_and_tree('branch2')
103
tree2.pull(tree1.branch)
104
checkout = tree1.branch.create_checkout('checkout',
105
lightweight=lightweight)
106
self.assertEqual(checkout.branch.nick, tree1.branch.nick)
107
checkout.branch.nick = "explicit_nick"
108
self.assertEqual(checkout.branch.nick, "explicit_nick")
109
self.assertEqual(checkout.branch.get_config()._get_explicit_nickname(),
111
self.run_bzr('switch branch2', working_dir='checkout')
113
# we need to get the tree again, otherwise we don't get the new branch
114
checkout = WorkingTree.open('checkout')
115
self.assertEqual(checkout.branch.nick, tree2.branch.nick)
116
self.assertEqual(checkout.branch.get_config()._get_explicit_nickname(),
119
def test_switch_explicit_nick(self):
120
self._test_switch_explicit_nick(lightweight=False)
122
def test_switch_explicit_nick_lightweight(self):
123
self._test_switch_explicit_nick(lightweight=True)
125
def test_switch_finds_relative_branch(self):
126
"""Switch will find 'foo' relative to the branch the checkout is of."""
127
self.build_tree(['repo/'])
128
tree1 = self.make_branch_and_tree('repo/brancha')
130
tree2 = self.make_branch_and_tree('repo/branchb')
131
tree2.pull(tree1.branch)
132
branchb_id = tree2.commit('bar')
133
checkout = tree1.branch.create_checkout('checkout', lightweight=True)
134
self.run_bzr(['switch', 'branchb'], working_dir='checkout')
135
self.assertEqual(branchb_id, checkout.last_revision())
136
checkout = checkout.bzrdir.open_workingtree()
137
self.assertEqual(tree2.branch.base, checkout.branch.base)
139
def test_switch_finds_relative_bound_branch(self):
140
"""Using switch on a heavy checkout should find master sibling
142
The behaviour of lighweight and heavy checkouts should be
143
consistent when using the convenient "switch to sibling" feature
144
Both should switch to a sibling of the branch
145
they are bound to, and not a sibling of themself"""
147
self.build_tree(['repo/',
149
tree1 = self.make_branch_and_tree('repo/brancha')
151
tree2 = self.make_branch_and_tree('repo/branchb')
152
tree2.pull(tree1.branch)
153
branchb_id = tree2.commit('bar')
154
checkout = tree1.branch.create_checkout('heavyco/a', lightweight=False)
155
self.run_bzr(['switch', 'branchb'], working_dir='heavyco/a')
156
# Refresh checkout as 'switch' modified it
157
checkout = checkout.bzrdir.open_workingtree()
158
self.assertEqual(branchb_id, checkout.last_revision())
159
self.assertEqual(tree2.branch.base,
160
checkout.branch.get_bound_location())
162
def test_switch_finds_relative_unicode_branch(self):
163
"""Switch will find 'foo' relative to the branch the checkout is of."""
164
self.requireFeature(UnicodeFilenameFeature)
165
self.build_tree(['repo/'])
166
tree1 = self.make_branch_and_tree('repo/brancha')
168
tree2 = self.make_branch_and_tree(u'repo/branch\xe9')
169
tree2.pull(tree1.branch)
170
branchb_id = tree2.commit('bar')
171
checkout = tree1.branch.create_checkout('checkout', lightweight=True)
172
self.run_bzr(['switch', u'branch\xe9'], working_dir='checkout')
173
self.assertEqual(branchb_id, checkout.last_revision())
174
checkout = checkout.bzrdir.open_workingtree()
175
self.assertEqual(tree2.branch.base, checkout.branch.base)
177
def test_switch_finds_relative_unicode_branch(self):
178
"""Switch will find 'foo' relative to the branch the checkout is of."""
179
self.requireFeature(UnicodeFilenameFeature)
180
self.build_tree(['repo/'])
181
tree1 = self.make_branch_and_tree('repo/brancha')
183
tree2 = self.make_branch_and_tree(u'repo/branch\xe9')
184
tree2.pull(tree1.branch)
185
branchb_id = tree2.commit('bar')
186
checkout = tree1.branch.create_checkout('checkout', lightweight=True)
187
self.run_bzr(['switch', u'branch\xe9'], working_dir='checkout')
188
self.assertEqual(branchb_id, checkout.last_revision())
189
checkout = checkout.bzrdir.open_workingtree()
190
self.assertEqual(tree2.branch.base, checkout.branch.base)
192
def test_switch_revision(self):
193
tree = self._create_sample_tree()
194
checkout = tree.branch.create_checkout('checkout', lightweight=True)
195
self.run_bzr(['switch', 'branch-1', '-r1'], working_dir='checkout')
196
self.assertPathExists('checkout/file-1')
197
self.assertPathDoesNotExist('checkout/file-2')
199
def test_switch_into_colocated(self):
200
# Create a new colocated branch from an existing non-colocated branch.
201
tree = self.make_branch_and_tree('.', format='development-colo')
202
self.build_tree(['file-1', 'file-2'])
204
revid1 = tree.commit('rev1')
206
revid2 = tree.commit('rev2')
207
self.run_bzr(['switch', '-b', 'anotherbranch'])
209
set(['', 'anotherbranch']),
210
set(tree.branch.bzrdir.get_branches().keys()))
212
def test_switch_into_unrelated_colocated(self):
213
# Create a new colocated branch from an existing non-colocated branch.
214
tree = self.make_branch_and_tree('.', format='development-colo')
215
self.build_tree(['file-1', 'file-2'])
217
revid1 = tree.commit('rev1')
219
revid2 = tree.commit('rev2')
220
tree.bzrdir.create_branch(name='foo')
221
self.run_bzr_error(['Cannot switch a branch, only a checkout.'],
223
self.run_bzr(['switch', '--force', 'foo'])
225
def test_switch_existing_colocated(self):
226
# Create a branch branch-1 that initially is a checkout of 'foo'
227
# Use switch to change it to 'anotherbranch'
228
repo = self.make_repository('branch-1', format='development-colo')
229
target_branch = repo.bzrdir.create_branch(name='foo')
230
repo.bzrdir.set_branch_reference(target_branch)
231
tree = repo.bzrdir.create_workingtree()
232
self.build_tree(['branch-1/file-1', 'branch-1/file-2'])
234
revid1 = tree.commit('rev1')
236
revid2 = tree.commit('rev2')
237
otherbranch = tree.bzrdir.create_branch(name='anotherbranch')
238
otherbranch.generate_revision_history(revid1)
239
self.run_bzr(['switch', 'anotherbranch'], working_dir='branch-1')
240
tree = WorkingTree.open("branch-1")
241
self.assertEquals(tree.last_revision(), revid1)
242
self.assertEquals(tree.branch.control_url, otherbranch.control_url)
244
def test_switch_new_colocated(self):
245
# Create a branch branch-1 that initially is a checkout of 'foo'
246
# Use switch to create 'anotherbranch' which derives from that
247
repo = self.make_repository('branch-1', format='development-colo')
248
target_branch = repo.bzrdir.create_branch(name='foo')
249
repo.bzrdir.set_branch_reference(target_branch)
250
tree = repo.bzrdir.create_workingtree()
251
self.build_tree(['branch-1/file-1', 'branch-1/file-2'])
253
revid1 = tree.commit('rev1')
254
self.run_bzr(['switch', '-b', 'anotherbranch'], working_dir='branch-1')
255
bzrdir = ControlDir.open("branch-1")
257
set([b.name for b in bzrdir.list_branches()]),
258
set(["foo", "anotherbranch"]))
259
self.assertEquals(bzrdir.open_branch().name, "anotherbranch")
260
self.assertEquals(bzrdir.open_branch().last_revision(), revid1)
262
def test_switch_new_colocated_unicode(self):
263
# Create a branch branch-1 that initially is a checkout of 'foo'
264
# Use switch to create 'branch\xe9' which derives from that
265
self.requireFeature(UnicodeFilenameFeature)
266
repo = self.make_repository('branch-1', format='development-colo')
267
target_branch = repo.bzrdir.create_branch(name='foo')
268
repo.bzrdir.set_branch_reference(target_branch)
269
tree = repo.bzrdir.create_workingtree()
270
self.build_tree(['branch-1/file-1', 'branch-1/file-2'])
272
revid1 = tree.commit('rev1')
273
self.run_bzr(['switch', '-b', u'branch\xe9'], working_dir='branch-1')
274
bzrdir = ControlDir.open("branch-1")
276
set([b.name for b in bzrdir.list_branches()]),
277
set(["foo", u"branch\xe9"]))
278
self.assertEquals(bzrdir.open_branch().name, u"branch\xe9")
279
self.assertEquals(bzrdir.open_branch().last_revision(), revid1)
281
def test_switch_only_revision(self):
282
tree = self._create_sample_tree()
283
checkout = tree.branch.create_checkout('checkout', lightweight=True)
284
self.assertPathExists('checkout/file-1')
285
self.assertPathExists('checkout/file-2')
286
self.run_bzr(['switch', '-r1'], working_dir='checkout')
287
self.assertPathExists('checkout/file-1')
288
self.assertPathDoesNotExist('checkout/file-2')
289
# Check that we don't accept a range
291
['bzr switch --revision takes exactly one revision identifier'],
292
['switch', '-r0..2'], working_dir='checkout')
294
def prepare_lightweight_switch(self):
295
branch = self.make_branch('branch')
296
branch.create_checkout('tree', lightweight=True)
297
osutils.rename('branch', 'branch1')
299
def test_switch_lightweight_after_branch_moved(self):
300
self.prepare_lightweight_switch()
301
self.run_bzr('switch --force --with-changes ../branch1',
303
branch_location = WorkingTree.open('tree').branch.base
304
self.assertEndsWith(branch_location, 'branch1/')
306
def test_switch_lightweight_after_branch_moved_relative(self):
307
self.prepare_lightweight_switch()
308
self.run_bzr('switch --force branch1 --with-changes',
310
branch_location = WorkingTree.open('tree').branch.base
311
self.assertEndsWith(branch_location, 'branch1/')
313
def test_create_branch_no_branch(self):
314
self.prepare_lightweight_switch()
315
self.run_bzr_error(['cannot create branch without source branch'],
316
'switch --create-branch ../branch2', working_dir='tree')
318
def test_create_branch(self):
319
branch = self.make_branch('branch')
320
tree = branch.create_checkout('tree', lightweight=True)
321
tree.commit('one', rev_id='rev-1')
322
self.run_bzr('switch --create-branch ../branch2', working_dir='tree')
323
tree = WorkingTree.open('tree')
324
self.assertEndsWith(tree.branch.base, '/branch2/')
326
def test_create_branch_local(self):
327
branch = self.make_branch('branch')
328
tree = branch.create_checkout('tree', lightweight=True)
329
tree.commit('one', rev_id='rev-1')
330
self.run_bzr('switch --create-branch branch2', working_dir='tree')
331
tree = WorkingTree.open('tree')
332
# The new branch should have been created at the same level as
333
# 'branch', because we did not have a '/' segment
334
self.assertEqual(branch.base[:-1] + '2/', tree.branch.base)
336
def test_create_branch_short_name(self):
337
branch = self.make_branch('branch')
338
tree = branch.create_checkout('tree', lightweight=True)
339
tree.commit('one', rev_id='rev-1')
340
self.run_bzr('switch -b branch2', working_dir='tree')
341
tree = WorkingTree.open('tree')
342
# The new branch should have been created at the same level as
343
# 'branch', because we did not have a '/' segment
344
self.assertEqual(branch.base[:-1] + '2/', tree.branch.base)
346
def test_create_branch_directory_services(self):
347
branch = self.make_branch('branch')
348
tree = branch.create_checkout('tree', lightweight=True)
349
class FooLookup(object):
350
def look_up(self, name, url):
352
directories.register('foo:', FooLookup, 'Create branches named foo-')
353
self.addCleanup(directories.remove, 'foo:')
354
self.run_bzr('switch -b foo:branch2', working_dir='tree')
355
tree = WorkingTree.open('tree')
356
self.assertEndsWith(tree.branch.base, 'foo-branch2/')
358
def test_switch_with_post_switch_hook(self):
359
from bzrlib import branch as _mod_branch
361
_mod_branch.Branch.hooks.install_named_hook('post_switch',
363
self.make_branch_and_tree('branch')
364
self.run_bzr('branch branch branch2')
365
self.run_bzr('checkout branch checkout')
367
self.assertLength(0, calls)
368
out, err = self.run_bzr('switch ../branch2')
369
self.assertLength(1, calls)
371
def test_switch_lightweight_co_with_post_switch_hook(self):
372
from bzrlib import branch as _mod_branch
374
_mod_branch.Branch.hooks.install_named_hook('post_switch',
376
self.make_branch_and_tree('branch')
377
self.run_bzr('branch branch branch2')
378
self.run_bzr('checkout --lightweight branch checkout')
380
self.assertLength(0, calls)
381
out, err = self.run_bzr('switch ../branch2')
382
self.assertLength(1, calls)
384
def test_switch_lightweight_directory(self):
385
"""Test --directory option"""
387
# create a source branch
388
a_tree = self.make_branch_and_tree('a')
389
self.build_tree_contents([('a/a', 'initial\n')])
391
a_tree.commit(message='initial')
393
# clone and add a differing revision
394
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
395
self.build_tree_contents([('b/a', 'initial\nmore\n')])
396
b_tree.commit(message='more')
398
self.run_bzr('checkout --lightweight a checkout')
399
self.run_bzr('switch --directory checkout b')
400
self.assertFileEqual('initial\nmore\n', 'checkout/a')
403
class TestSwitchParentLocationBase(TestCaseWithTransport):
406
"""Set up a repository and branch ready for testing."""
407
super(TestSwitchParentLocationBase, self).setUp()
408
self.script_runner = script.ScriptRunner()
409
self.script_runner.run_script(self, '''
410
$ bzr init-repo --no-trees repo
413
shared repository: repo
414
$ bzr init repo/trunk
415
Created a repository branch...
416
Using shared repository: ...
419
def assertParent(self, expected_parent, branch):
420
"""Verify that the parent is not None and is set correctly."""
421
actual_parent = branch.get_parent()
422
self.assertIsSameRealPath(urlutils.local_path_to_url(expected_parent),
426
class TestSwitchParentLocation(TestSwitchParentLocationBase):
428
def _checkout_and_switch(self, option=''):
429
self.script_runner.run_script(self, '''
430
$ bzr checkout %(option)s repo/trunk checkout
432
$ bzr switch --create-branch switched
433
2>Tree is up to date at revision 0.
434
2>Switched to branch:...switched...
437
bound_branch = branch.Branch.open_containing('checkout')[0]
438
master_branch = branch.Branch.open_containing('repo/switched')[0]
439
return (bound_branch, master_branch)
441
def test_switch_parent_lightweight(self):
442
"""Lightweight checkout using bzr switch."""
443
bb, mb = self._checkout_and_switch(option='--lightweight')
444
self.assertParent('repo/trunk', bb)
445
self.assertParent('repo/trunk', mb)
447
def test_switch_parent_heavyweight(self):
448
"""Heavyweight checkout using bzr switch."""
449
bb, mb = self._checkout_and_switch()
450
self.assertParent('repo/trunk', bb)
451
self.assertParent('repo/trunk', mb)
454
class TestSwitchDoesntOpenMasterBranch(TestCaseWithTransport):
455
# See https://bugs.launchpad.net/bzr/+bug/812285
456
# "bzr switch --create-branch" can point the new branch's parent to the
457
# master branch, but it doesn't have to open it to do so.
459
def test_switch_create_doesnt_open_master_branch(self):
460
master = self.make_branch_and_tree('master')
462
# Note: not a lightweight checkout
463
checkout = master.branch.create_checkout('checkout')
465
def open_hook(branch):
466
# Just append the final directory of the branch
467
name = branch.base.rstrip('/').rsplit('/', 1)[1]
469
branch.Branch.hooks.install_named_hook('open', open_hook,
471
self.run_bzr('switch --create-branch -d checkout feature')
472
# We only open the master branch 1 time.
473
# This test should be cleaner to write, but see bug:
474
# https://bugs.launchpad.net/bzr/+bug/812295
475
self.assertEqual(1, opened.count('master'))
478
class TestSmartServerSwitch(TestCaseWithTransport):
480
def test_switch_lightweight(self):
481
self.setup_smart_server_with_call_log()
482
t = self.make_branch_and_tree('from')
483
for count in range(9):
484
t.commit(message='commit %d' % count)
485
out, err = self.run_bzr(['checkout', '--lightweight', self.get_url('from'),
487
self.reset_smart_call_log()
488
self.run_bzr(['switch', '--with-changes',
489
self.get_url('from')], working_dir='target')
490
# This figure represent the amount of work to perform this use case. It
491
# is entirely ok to reduce this number if a test fails due to rpc_count
492
# being too low. If rpc_count increases, more network roundtrips have
493
# become necessary for this use case. Please do not adjust this number
494
# upwards without agreement from bzr's network support maintainers.
495
self.assertLength(24, self.hpss_calls)
496
self.assertLength(4, self.hpss_connections)
497
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
500
class TestSwitchUncommitted(TestCaseWithTransport):
503
tree = self.make_branch_and_tree('orig')
505
tree.branch.bzrdir.sprout('new')
506
checkout = tree.branch.create_checkout('checkout', lightweight=True)
507
self.build_tree(['checkout/a'])
508
self.assertPathExists('checkout/a')
511
def test_switch_stores_local(self):
513
self.run_bzr(['switch', '-d', 'checkout', 'new'])
514
self.assertPathDoesNotExist('checkout/a')
515
self.run_bzr(['switch', '-d', 'checkout', 'orig'])
516
self.assertPathExists('checkout/a')
518
def test_with_changes_does_not_store(self):
520
self.run_bzr(['switch', '-d', 'checkout', '--with-changes', 'new'])
521
self.assertPathExists('checkout/a')
523
def test_with_changes_does_not_restore(self):
525
self.run_bzr(['switch', '-d', 'checkout', 'new'])
526
self.assertPathDoesNotExist('checkout/a')
527
self.run_bzr(['switch', '-d', 'checkout', '--with-changes', 'orig'])
528
self.assertPathDoesNotExist('checkout/a')