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.bzrdir import BzrDir
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_revision(self):
178
tree = self._create_sample_tree()
179
checkout = tree.branch.create_checkout('checkout', lightweight=True)
180
self.run_bzr(['switch', 'branch-1', '-r1'], working_dir='checkout')
181
self.assertPathExists('checkout/file-1')
182
self.assertPathDoesNotExist('checkout/file-2')
184
def test_switch_into_colocated(self):
185
# Create a new colocated branch from an existing non-colocated branch.
186
tree = self.make_branch_and_tree('.', format='development-colo')
187
self.build_tree(['file-1', 'file-2'])
189
revid1 = tree.commit('rev1')
191
revid2 = tree.commit('rev2')
192
self.run_bzr(['switch', '-b', 'anotherbranch'])
194
set(['', 'anotherbranch']),
195
set(tree.branch.bzrdir.get_branches().keys()))
197
def test_switch_into_unrelated_colocated(self):
198
# Create a new colocated branch from an existing non-colocated branch.
199
tree = self.make_branch_and_tree('.', format='development-colo')
200
self.build_tree(['file-1', 'file-2'])
202
revid1 = tree.commit('rev1')
204
revid2 = tree.commit('rev2')
205
tree.bzrdir.create_branch(name='foo')
206
self.run_bzr_error(['Cannot switch a branch, only a checkout.'],
208
self.run_bzr(['switch', '--force', 'foo'])
210
def test_switch_existing_colocated(self):
211
# Create a branch branch-1 that initially is a checkout of 'foo'
212
# Use switch to change it to 'anotherbranch'
213
repo = self.make_repository('branch-1', format='development-colo')
214
target_branch = repo.bzrdir.create_branch(name='foo')
215
repo.bzrdir.set_branch_reference(target_branch)
216
tree = repo.bzrdir.create_workingtree()
217
self.build_tree(['branch-1/file-1', 'branch-1/file-2'])
219
revid1 = tree.commit('rev1')
221
revid2 = tree.commit('rev2')
222
otherbranch = tree.bzrdir.create_branch(name='anotherbranch')
223
otherbranch.generate_revision_history(revid1)
224
self.run_bzr(['switch', 'anotherbranch'], working_dir='branch-1')
225
tree = WorkingTree.open("branch-1")
226
self.assertEquals(tree.last_revision(), revid1)
227
self.assertEquals(tree.branch.control_url, otherbranch.control_url)
229
def test_switch_new_colocated(self):
230
# Create a branch branch-1 that initially is a checkout of 'foo'
231
# Use switch to create 'anotherbranch' which derives from that
232
repo = self.make_repository('branch-1', format='development-colo')
233
target_branch = repo.bzrdir.create_branch(name='foo')
234
repo.bzrdir.set_branch_reference(target_branch)
235
tree = repo.bzrdir.create_workingtree()
236
self.build_tree(['branch-1/file-1', 'branch-1/file-2'])
238
revid1 = tree.commit('rev1')
239
self.run_bzr(['switch', '-b', 'anotherbranch'], working_dir='branch-1')
240
bzrdir = BzrDir.open("branch-1")
242
set([b.name for b in bzrdir.list_branches()]),
243
set(["foo", "anotherbranch"]))
244
self.assertEquals(bzrdir.open_branch().name, "anotherbranch")
245
self.assertEquals(bzrdir.open_branch().last_revision(), revid1)
247
def test_switch_new_colocated_unicode(self):
248
# Create a branch branch-1 that initially is a checkout of 'foo'
249
# Use switch to create 'branch\xe9' which derives from that
250
self.requireFeature(UnicodeFilenameFeature)
251
repo = self.make_repository('branch-1', format='development-colo')
252
target_branch = repo.bzrdir.create_branch(name='foo')
253
repo.bzrdir.set_branch_reference(target_branch)
254
tree = repo.bzrdir.create_workingtree()
255
self.build_tree(['branch-1/file-1', 'branch-1/file-2'])
257
revid1 = tree.commit('rev1')
258
self.run_bzr(['switch', '-b', u'branch\xe9'], working_dir='branch-1')
259
bzrdir = BzrDir.open("branch-1")
261
set([b.name for b in bzrdir.list_branches()]),
262
set(["foo", u"branch\xe9"]))
263
self.assertEquals(bzrdir.open_branch().name, u"branch\xe9")
264
self.assertEquals(bzrdir.open_branch().last_revision(), revid1)
266
def test_switch_only_revision(self):
267
tree = self._create_sample_tree()
268
checkout = tree.branch.create_checkout('checkout', lightweight=True)
269
self.assertPathExists('checkout/file-1')
270
self.assertPathExists('checkout/file-2')
271
self.run_bzr(['switch', '-r1'], working_dir='checkout')
272
self.assertPathExists('checkout/file-1')
273
self.assertPathDoesNotExist('checkout/file-2')
274
# Check that we don't accept a range
276
['bzr switch --revision takes exactly one revision identifier'],
277
['switch', '-r0..2'], working_dir='checkout')
279
def prepare_lightweight_switch(self):
280
branch = self.make_branch('branch')
281
branch.create_checkout('tree', lightweight=True)
282
osutils.rename('branch', 'branch1')
284
def test_switch_lightweight_after_branch_moved(self):
285
self.prepare_lightweight_switch()
286
self.run_bzr('switch --force ../branch1', working_dir='tree')
287
branch_location = WorkingTree.open('tree').branch.base
288
self.assertEndsWith(branch_location, 'branch1/')
290
def test_switch_lightweight_after_branch_moved_relative(self):
291
self.prepare_lightweight_switch()
292
self.run_bzr('switch --force branch1', working_dir='tree')
293
branch_location = WorkingTree.open('tree').branch.base
294
self.assertEndsWith(branch_location, 'branch1/')
296
def test_create_branch_no_branch(self):
297
self.prepare_lightweight_switch()
298
self.run_bzr_error(['cannot create branch without source branch'],
299
'switch --create-branch ../branch2', working_dir='tree')
301
def test_create_branch(self):
302
branch = self.make_branch('branch')
303
tree = branch.create_checkout('tree', lightweight=True)
304
tree.commit('one', rev_id='rev-1')
305
self.run_bzr('switch --create-branch ../branch2', working_dir='tree')
306
tree = WorkingTree.open('tree')
307
self.assertEndsWith(tree.branch.base, '/branch2/')
309
def test_create_branch_local(self):
310
branch = self.make_branch('branch')
311
tree = branch.create_checkout('tree', lightweight=True)
312
tree.commit('one', rev_id='rev-1')
313
self.run_bzr('switch --create-branch branch2', working_dir='tree')
314
tree = WorkingTree.open('tree')
315
# The new branch should have been created at the same level as
316
# 'branch', because we did not have a '/' segment
317
self.assertEqual(branch.base[:-1] + '2/', tree.branch.base)
319
def test_create_branch_short_name(self):
320
branch = self.make_branch('branch')
321
tree = branch.create_checkout('tree', lightweight=True)
322
tree.commit('one', rev_id='rev-1')
323
self.run_bzr('switch -b branch2', working_dir='tree')
324
tree = WorkingTree.open('tree')
325
# The new branch should have been created at the same level as
326
# 'branch', because we did not have a '/' segment
327
self.assertEqual(branch.base[:-1] + '2/', tree.branch.base)
329
def test_create_branch_directory_services(self):
330
branch = self.make_branch('branch')
331
tree = branch.create_checkout('tree', lightweight=True)
332
class FooLookup(object):
333
def look_up(self, name, url):
335
directories.register('foo:', FooLookup, 'Create branches named foo-')
336
self.addCleanup(directories.remove, 'foo:')
337
self.run_bzr('switch -b foo:branch2', working_dir='tree')
338
tree = WorkingTree.open('tree')
339
self.assertEndsWith(tree.branch.base, 'foo-branch2/')
341
def test_switch_with_post_switch_hook(self):
342
from bzrlib import branch as _mod_branch
344
_mod_branch.Branch.hooks.install_named_hook('post_switch',
346
self.make_branch_and_tree('branch')
347
self.run_bzr('branch branch branch2')
348
self.run_bzr('checkout branch checkout')
350
self.assertLength(0, calls)
351
out, err = self.run_bzr('switch ../branch2')
352
self.assertLength(1, calls)
354
def test_switch_lightweight_co_with_post_switch_hook(self):
355
from bzrlib import branch as _mod_branch
357
_mod_branch.Branch.hooks.install_named_hook('post_switch',
359
self.make_branch_and_tree('branch')
360
self.run_bzr('branch branch branch2')
361
self.run_bzr('checkout --lightweight branch checkout')
363
self.assertLength(0, calls)
364
out, err = self.run_bzr('switch ../branch2')
365
self.assertLength(1, calls)
367
def test_switch_lightweight_directory(self):
368
"""Test --directory option"""
370
# create a source branch
371
a_tree = self.make_branch_and_tree('a')
372
self.build_tree_contents([('a/a', 'initial\n')])
374
a_tree.commit(message='initial')
376
# clone and add a differing revision
377
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
378
self.build_tree_contents([('b/a', 'initial\nmore\n')])
379
b_tree.commit(message='more')
381
self.run_bzr('checkout --lightweight a checkout')
382
self.run_bzr('switch --directory checkout b')
383
self.assertFileEqual('initial\nmore\n', 'checkout/a')
386
class TestSwitchParentLocationBase(TestCaseWithTransport):
389
"""Set up a repository and branch ready for testing."""
390
super(TestSwitchParentLocationBase, self).setUp()
391
self.script_runner = script.ScriptRunner()
392
self.script_runner.run_script(self, '''
393
$ bzr init-repo --no-trees repo
396
shared repository: repo
397
$ bzr init repo/trunk
398
Created a repository branch...
399
Using shared repository: ...
402
def assertParent(self, expected_parent, branch):
403
"""Verify that the parent is not None and is set correctly."""
404
actual_parent = branch.get_parent()
405
self.assertIsSameRealPath(urlutils.local_path_to_url(expected_parent),
409
class TestSwitchParentLocation(TestSwitchParentLocationBase):
411
def _checkout_and_switch(self, option=''):
412
self.script_runner.run_script(self, '''
413
$ bzr checkout %(option)s repo/trunk checkout
415
$ bzr switch --create-branch switched
416
2>Tree is up to date at revision 0.
417
2>Switched to branch:...switched...
420
bound_branch = branch.Branch.open_containing('checkout')[0]
421
master_branch = branch.Branch.open_containing('repo/switched')[0]
422
return (bound_branch, master_branch)
424
def test_switch_parent_lightweight(self):
425
"""Lightweight checkout using bzr switch."""
426
bb, mb = self._checkout_and_switch(option='--lightweight')
427
self.assertParent('repo/trunk', bb)
428
self.assertParent('repo/trunk', mb)
430
def test_switch_parent_heavyweight(self):
431
"""Heavyweight checkout using bzr switch."""
432
bb, mb = self._checkout_and_switch()
433
self.assertParent('repo/trunk', bb)
434
self.assertParent('repo/trunk', mb)
437
class TestSwitchDoesntOpenMasterBranch(TestCaseWithTransport):
438
# See https://bugs.launchpad.net/bzr/+bug/812285
439
# "bzr switch --create-branch" can point the new branch's parent to the
440
# master branch, but it doesn't have to open it to do so.
442
def test_switch_create_doesnt_open_master_branch(self):
443
master = self.make_branch_and_tree('master')
445
# Note: not a lightweight checkout
446
checkout = master.branch.create_checkout('checkout')
448
def open_hook(branch):
449
# Just append the final directory of the branch
450
name = branch.base.rstrip('/').rsplit('/', 1)[1]
452
branch.Branch.hooks.install_named_hook('open', open_hook,
454
self.run_bzr('switch --create-branch -d checkout feature')
455
# We only open the master branch 1 time.
456
# This test should be cleaner to write, but see bug:
457
# https://bugs.launchpad.net/bzr/+bug/812295
458
self.assertEqual(1, opened.count('master'))
461
class TestSmartServerSwitch(TestCaseWithTransport):
463
def test_switch_lightweight(self):
464
self.setup_smart_server_with_call_log()
465
t = self.make_branch_and_tree('from')
466
for count in range(9):
467
t.commit(message='commit %d' % count)
468
out, err = self.run_bzr(['checkout', '--lightweight', self.get_url('from'),
470
self.reset_smart_call_log()
471
self.run_bzr(['switch', self.get_url('from')], working_dir='target')
472
# This figure represent the amount of work to perform this use case. It
473
# is entirely ok to reduce this number if a test fails due to rpc_count
474
# being too low. If rpc_count increases, more network roundtrips have
475
# become necessary for this use case. Please do not adjust this number
476
# upwards without agreement from bzr's network support maintainers.
477
self.assertLength(24, self.hpss_calls)
478
self.assertLength(5, self.hpss_connections)
479
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)