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 ../branch1', working_dir='tree')
302
branch_location = WorkingTree.open('tree').branch.base
303
self.assertEndsWith(branch_location, 'branch1/')
305
def test_switch_lightweight_after_branch_moved_relative(self):
306
self.prepare_lightweight_switch()
307
self.run_bzr('switch --force branch1', working_dir='tree')
308
branch_location = WorkingTree.open('tree').branch.base
309
self.assertEndsWith(branch_location, 'branch1/')
311
def test_create_branch_no_branch(self):
312
self.prepare_lightweight_switch()
313
self.run_bzr_error(['cannot create branch without source branch'],
314
'switch --create-branch ../branch2', working_dir='tree')
316
def test_create_branch(self):
317
branch = self.make_branch('branch')
318
tree = branch.create_checkout('tree', lightweight=True)
319
tree.commit('one', rev_id='rev-1')
320
self.run_bzr('switch --create-branch ../branch2', working_dir='tree')
321
tree = WorkingTree.open('tree')
322
self.assertEndsWith(tree.branch.base, '/branch2/')
324
def test_create_branch_local(self):
325
branch = self.make_branch('branch')
326
tree = branch.create_checkout('tree', lightweight=True)
327
tree.commit('one', rev_id='rev-1')
328
self.run_bzr('switch --create-branch branch2', working_dir='tree')
329
tree = WorkingTree.open('tree')
330
# The new branch should have been created at the same level as
331
# 'branch', because we did not have a '/' segment
332
self.assertEqual(branch.base[:-1] + '2/', tree.branch.base)
334
def test_create_branch_short_name(self):
335
branch = self.make_branch('branch')
336
tree = branch.create_checkout('tree', lightweight=True)
337
tree.commit('one', rev_id='rev-1')
338
self.run_bzr('switch -b branch2', working_dir='tree')
339
tree = WorkingTree.open('tree')
340
# The new branch should have been created at the same level as
341
# 'branch', because we did not have a '/' segment
342
self.assertEqual(branch.base[:-1] + '2/', tree.branch.base)
344
def test_create_branch_directory_services(self):
345
branch = self.make_branch('branch')
346
tree = branch.create_checkout('tree', lightweight=True)
347
class FooLookup(object):
348
def look_up(self, name, url):
350
directories.register('foo:', FooLookup, 'Create branches named foo-')
351
self.addCleanup(directories.remove, 'foo:')
352
self.run_bzr('switch -b foo:branch2', working_dir='tree')
353
tree = WorkingTree.open('tree')
354
self.assertEndsWith(tree.branch.base, 'foo-branch2/')
356
def test_switch_with_post_switch_hook(self):
357
from bzrlib import branch as _mod_branch
359
_mod_branch.Branch.hooks.install_named_hook('post_switch',
361
self.make_branch_and_tree('branch')
362
self.run_bzr('branch branch branch2')
363
self.run_bzr('checkout branch checkout')
365
self.assertLength(0, calls)
366
out, err = self.run_bzr('switch ../branch2')
367
self.assertLength(1, calls)
369
def test_switch_lightweight_co_with_post_switch_hook(self):
370
from bzrlib import branch as _mod_branch
372
_mod_branch.Branch.hooks.install_named_hook('post_switch',
374
self.make_branch_and_tree('branch')
375
self.run_bzr('branch branch branch2')
376
self.run_bzr('checkout --lightweight branch checkout')
378
self.assertLength(0, calls)
379
out, err = self.run_bzr('switch ../branch2')
380
self.assertLength(1, calls)
382
def test_switch_lightweight_directory(self):
383
"""Test --directory option"""
385
# create a source branch
386
a_tree = self.make_branch_and_tree('a')
387
self.build_tree_contents([('a/a', 'initial\n')])
389
a_tree.commit(message='initial')
391
# clone and add a differing revision
392
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
393
self.build_tree_contents([('b/a', 'initial\nmore\n')])
394
b_tree.commit(message='more')
396
self.run_bzr('checkout --lightweight a checkout')
397
self.run_bzr('switch --directory checkout b')
398
self.assertFileEqual('initial\nmore\n', 'checkout/a')
401
class TestSwitchParentLocationBase(TestCaseWithTransport):
404
"""Set up a repository and branch ready for testing."""
405
super(TestSwitchParentLocationBase, self).setUp()
406
self.script_runner = script.ScriptRunner()
407
self.script_runner.run_script(self, '''
408
$ bzr init-repo --no-trees repo
411
shared repository: repo
412
$ bzr init repo/trunk
413
Created a repository branch...
414
Using shared repository: ...
417
def assertParent(self, expected_parent, branch):
418
"""Verify that the parent is not None and is set correctly."""
419
actual_parent = branch.get_parent()
420
self.assertIsSameRealPath(urlutils.local_path_to_url(expected_parent),
424
class TestSwitchParentLocation(TestSwitchParentLocationBase):
426
def _checkout_and_switch(self, option=''):
427
self.script_runner.run_script(self, '''
428
$ bzr checkout %(option)s repo/trunk checkout
430
$ bzr switch --create-branch switched
431
2>Tree is up to date at revision 0.
432
2>Switched to branch:...switched...
435
bound_branch = branch.Branch.open_containing('checkout')[0]
436
master_branch = branch.Branch.open_containing('repo/switched')[0]
437
return (bound_branch, master_branch)
439
def test_switch_parent_lightweight(self):
440
"""Lightweight checkout using bzr switch."""
441
bb, mb = self._checkout_and_switch(option='--lightweight')
442
self.assertParent('repo/trunk', bb)
443
self.assertParent('repo/trunk', mb)
445
def test_switch_parent_heavyweight(self):
446
"""Heavyweight checkout using bzr switch."""
447
bb, mb = self._checkout_and_switch()
448
self.assertParent('repo/trunk', bb)
449
self.assertParent('repo/trunk', mb)
452
class TestSwitchDoesntOpenMasterBranch(TestCaseWithTransport):
453
# See https://bugs.launchpad.net/bzr/+bug/812285
454
# "bzr switch --create-branch" can point the new branch's parent to the
455
# master branch, but it doesn't have to open it to do so.
457
def test_switch_create_doesnt_open_master_branch(self):
458
master = self.make_branch_and_tree('master')
460
# Note: not a lightweight checkout
461
checkout = master.branch.create_checkout('checkout')
463
def open_hook(branch):
464
# Just append the final directory of the branch
465
name = branch.base.rstrip('/').rsplit('/', 1)[1]
467
branch.Branch.hooks.install_named_hook('open', open_hook,
469
self.run_bzr('switch --create-branch -d checkout feature')
470
# We only open the master branch 1 time.
471
# This test should be cleaner to write, but see bug:
472
# https://bugs.launchpad.net/bzr/+bug/812295
473
self.assertEqual(1, opened.count('master'))
476
class TestSmartServerSwitch(TestCaseWithTransport):
478
def test_switch_lightweight(self):
479
self.setup_smart_server_with_call_log()
480
t = self.make_branch_and_tree('from')
481
for count in range(9):
482
t.commit(message='commit %d' % count)
483
out, err = self.run_bzr(['checkout', '--lightweight', self.get_url('from'),
485
self.reset_smart_call_log()
486
self.run_bzr(['switch', self.get_url('from')], working_dir='target')
487
# This figure represent the amount of work to perform this use case. It
488
# is entirely ok to reduce this number if a test fails due to rpc_count
489
# being too low. If rpc_count increases, more network roundtrips have
490
# become necessary for this use case. Please do not adjust this number
491
# upwards without agreement from bzr's network support maintainers.
492
self.assertLength(24, self.hpss_calls)
493
self.assertLength(4, self.hpss_connections)
494
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)