1
# Copyright (C) 2007-2010 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
self.assertEqual(branchb_id, checkout.last_revision())
157
self.assertEqual(tree2.branch.base, checkout.branch.get_bound_location())
159
def test_switch_finds_relative_unicode_branch(self):
160
"""Switch will find 'foo' relative to the branch the checkout is of."""
161
self.requireFeature(UnicodeFilenameFeature)
162
self.build_tree(['repo/'])
163
tree1 = self.make_branch_and_tree('repo/brancha')
165
tree2 = self.make_branch_and_tree(u'repo/branch\xe9')
166
tree2.pull(tree1.branch)
167
branchb_id = tree2.commit('bar')
168
checkout = tree1.branch.create_checkout('checkout', lightweight=True)
169
self.run_bzr(['switch', u'branch\xe9'], working_dir='checkout')
170
self.assertEqual(branchb_id, checkout.last_revision())
171
checkout = checkout.bzrdir.open_workingtree()
172
self.assertEqual(tree2.branch.base, checkout.branch.base)
174
def test_switch_revision(self):
175
tree = self._create_sample_tree()
176
checkout = tree.branch.create_checkout('checkout', lightweight=True)
177
self.run_bzr(['switch', 'branch-1', '-r1'], working_dir='checkout')
178
self.assertPathExists('checkout/file-1')
179
self.assertPathDoesNotExist('checkout/file-2')
181
def test_switch_existing_colocated(self):
182
# Create a branch branch-1 that initially is a checkout of 'foo'
183
# Use switch to change it to 'anotherbranch'
184
repo = self.make_repository('branch-1', format='development-colo')
185
target_branch = repo.bzrdir.create_branch(name='foo')
186
branch.BranchReferenceFormat().initialize(
187
repo.bzrdir, target_branch=target_branch)
188
tree = repo.bzrdir.create_workingtree()
189
self.build_tree(['branch-1/file-1', 'branch-1/file-2'])
191
revid1 = tree.commit('rev1')
193
revid2 = tree.commit('rev2')
194
otherbranch = tree.bzrdir.create_branch(name='anotherbranch')
195
otherbranch.generate_revision_history(revid1)
196
self.run_bzr(['switch', 'anotherbranch'], working_dir='branch-1')
197
tree = WorkingTree.open("branch-1")
198
self.assertEquals(tree.last_revision(), revid1)
199
self.assertEquals(tree.branch.control_url, otherbranch.control_url)
201
def test_switch_new_colocated(self):
202
# Create a branch branch-1 that initially is a checkout of 'foo'
203
# Use switch to create 'anotherbranch' which derives from that
204
repo = self.make_repository('branch-1', format='development-colo')
205
target_branch = repo.bzrdir.create_branch(name='foo')
206
branch.BranchReferenceFormat().initialize(
207
repo.bzrdir, target_branch=target_branch)
208
tree = repo.bzrdir.create_workingtree()
209
self.build_tree(['branch-1/file-1', 'branch-1/file-2'])
211
revid1 = tree.commit('rev1')
212
self.run_bzr(['switch', '-b', 'anotherbranch'], working_dir='branch-1')
213
bzrdir = BzrDir.open("branch-1")
215
set([b.name for b in bzrdir.list_branches()]),
216
set(["foo", "anotherbranch"]))
217
self.assertEquals(bzrdir.open_branch().name, "anotherbranch")
218
self.assertEquals(bzrdir.open_branch().last_revision(), revid1)
220
def test_switch_new_colocated_unicode(self):
221
# Create a branch branch-1 that initially is a checkout of 'foo'
222
# Use switch to create 'branch\xe9' which derives from that
223
self.requireFeature(UnicodeFilenameFeature)
224
repo = self.make_repository('branch-1', format='development-colo')
225
target_branch = repo.bzrdir.create_branch(name='foo')
226
branch.BranchReferenceFormat().initialize(
227
repo.bzrdir, target_branch=target_branch)
228
tree = repo.bzrdir.create_workingtree()
229
self.build_tree(['branch-1/file-1', 'branch-1/file-2'])
231
revid1 = tree.commit('rev1')
232
self.run_bzr(['switch', '-b', u'branch\xe9'], working_dir='branch-1')
233
bzrdir = BzrDir.open("branch-1")
235
set([b.name for b in bzrdir.list_branches()]),
236
set(["foo", u"branch\xe9"]))
237
self.assertEquals(bzrdir.open_branch().name, u"branch\xe9")
238
self.assertEquals(bzrdir.open_branch().last_revision(), revid1)
240
def test_switch_only_revision(self):
241
tree = self._create_sample_tree()
242
checkout = tree.branch.create_checkout('checkout', lightweight=True)
243
self.assertPathExists('checkout/file-1')
244
self.assertPathExists('checkout/file-2')
245
self.run_bzr(['switch', '-r1'], working_dir='checkout')
246
self.assertPathExists('checkout/file-1')
247
self.assertPathDoesNotExist('checkout/file-2')
248
# Check that we don't accept a range
250
['bzr switch --revision takes exactly one revision identifier'],
251
['switch', '-r0..2'], working_dir='checkout')
253
def prepare_lightweight_switch(self):
254
branch = self.make_branch('branch')
255
branch.create_checkout('tree', lightweight=True)
256
osutils.rename('branch', 'branch1')
258
def test_switch_lightweight_after_branch_moved(self):
259
self.prepare_lightweight_switch()
260
self.run_bzr('switch --force ../branch1', working_dir='tree')
261
branch_location = WorkingTree.open('tree').branch.base
262
self.assertEndsWith(branch_location, 'branch1/')
264
def test_switch_lightweight_after_branch_moved_relative(self):
265
self.prepare_lightweight_switch()
266
self.run_bzr('switch --force branch1', working_dir='tree')
267
branch_location = WorkingTree.open('tree').branch.base
268
self.assertEndsWith(branch_location, 'branch1/')
270
def test_create_branch_no_branch(self):
271
self.prepare_lightweight_switch()
272
self.run_bzr_error(['cannot create branch without source branch'],
273
'switch --create-branch ../branch2', working_dir='tree')
275
def test_create_branch(self):
276
branch = self.make_branch('branch')
277
tree = branch.create_checkout('tree', lightweight=True)
278
tree.commit('one', rev_id='rev-1')
279
self.run_bzr('switch --create-branch ../branch2', working_dir='tree')
280
tree = WorkingTree.open('tree')
281
self.assertEndsWith(tree.branch.base, '/branch2/')
283
def test_create_branch_local(self):
284
branch = self.make_branch('branch')
285
tree = branch.create_checkout('tree', lightweight=True)
286
tree.commit('one', rev_id='rev-1')
287
self.run_bzr('switch --create-branch branch2', working_dir='tree')
288
tree = WorkingTree.open('tree')
289
# The new branch should have been created at the same level as
290
# 'branch', because we did not have a '/' segment
291
self.assertEqual(branch.base[:-1] + '2/', tree.branch.base)
293
def test_create_branch_short_name(self):
294
branch = self.make_branch('branch')
295
tree = branch.create_checkout('tree', lightweight=True)
296
tree.commit('one', rev_id='rev-1')
297
self.run_bzr('switch -b branch2', working_dir='tree')
298
tree = WorkingTree.open('tree')
299
# The new branch should have been created at the same level as
300
# 'branch', because we did not have a '/' segment
301
self.assertEqual(branch.base[:-1] + '2/', tree.branch.base)
303
def test_create_branch_directory_services(self):
304
branch = self.make_branch('branch')
305
tree = branch.create_checkout('tree', lightweight=True)
306
class FooLookup(object):
307
def look_up(self, name, url):
309
directories.register('foo:', FooLookup, 'Create branches named foo-')
310
self.addCleanup(directories.remove, 'foo:')
311
self.run_bzr('switch -b foo:branch2', working_dir='tree')
312
tree = WorkingTree.open('tree')
313
self.assertEndsWith(tree.branch.base, 'foo-branch2/')
315
def test_switch_with_post_switch_hook(self):
316
from bzrlib import branch as _mod_branch
318
_mod_branch.Branch.hooks.install_named_hook('post_switch',
320
self.make_branch_and_tree('branch')
321
self.run_bzr('branch branch branch2')
322
self.run_bzr('checkout branch checkout')
324
self.assertLength(0, calls)
325
out, err = self.run_bzr('switch ../branch2')
326
self.assertLength(1, calls)
328
def test_switch_lightweight_co_with_post_switch_hook(self):
329
from bzrlib import branch as _mod_branch
331
_mod_branch.Branch.hooks.install_named_hook('post_switch',
333
self.make_branch_and_tree('branch')
334
self.run_bzr('branch branch branch2')
335
self.run_bzr('checkout --lightweight branch checkout')
337
self.assertLength(0, calls)
338
out, err = self.run_bzr('switch ../branch2')
339
self.assertLength(1, calls)
341
def test_switch_lightweight_directory(self):
342
"""Test --directory option"""
344
# create a source branch
345
a_tree = self.make_branch_and_tree('a')
346
self.build_tree_contents([('a/a', 'initial\n')])
348
a_tree.commit(message='initial')
350
# clone and add a differing revision
351
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
352
self.build_tree_contents([('b/a', 'initial\nmore\n')])
353
b_tree.commit(message='more')
355
self.run_bzr('checkout --lightweight a checkout')
356
self.run_bzr('switch --directory checkout b')
357
self.assertFileEqual('initial\nmore\n', 'checkout/a')
360
class TestSwitchParentLocationBase(TestCaseWithTransport):
363
"""Set up a repository and branch ready for testing."""
364
super(TestSwitchParentLocationBase, self).setUp()
365
self.script_runner = script.ScriptRunner()
366
self.script_runner.run_script(self, '''
367
$ bzr init-repo --no-trees repo
370
shared repository: repo
371
$ bzr init repo/trunk
372
Created a repository branch...
373
Using shared repository: ...
376
def assertParent(self, expected_parent, branch):
377
"""Verify that the parent is not None and is set correctly."""
378
actual_parent = branch.get_parent()
379
self.assertIsSameRealPath(urlutils.local_path_to_url(expected_parent),
383
class TestSwitchParentLocation(TestSwitchParentLocationBase):
385
def _checkout_and_switch(self, option=''):
386
self.script_runner.run_script(self, '''
387
$ bzr checkout %(option)s repo/trunk checkout
389
$ bzr switch --create-branch switched
390
2>Tree is up to date at revision 0.
391
2>Switched to branch:...switched...
394
bound_branch = branch.Branch.open_containing('checkout')[0]
395
master_branch = branch.Branch.open_containing('repo/switched')[0]
396
return (bound_branch, master_branch)
398
def test_switch_parent_lightweight(self):
399
"""Lightweight checkout using bzr switch."""
400
bb, mb = self._checkout_and_switch(option='--lightweight')
401
self.assertParent('repo/trunk', bb)
402
self.assertParent('repo/trunk', mb)
404
def test_switch_parent_heavyweight(self):
405
"""Heavyweight checkout using bzr switch."""
406
bb, mb = self._checkout_and_switch()
407
self.assertParent('repo/trunk', bb)
408
self.assertParent('repo/trunk', mb)
411
class TestSwitchDoesntOpenMasterBranch(TestCaseWithTransport):
412
# See https://bugs.launchpad.net/bzr/+bug/812285
413
# "bzr switch --create-branch" can point the new branch's parent to the
414
# master branch, but it doesn't have to open it to do so.
416
def test_switch_create_doesnt_open_master_branch(self):
417
master = self.make_branch_and_tree('master')
419
# Note: not a lightweight checkout
420
checkout = master.branch.create_checkout('checkout')
422
def open_hook(branch):
423
# Just append the final directory of the branch
424
name = branch.base.rstrip('/').rsplit('/', 1)[1]
426
branch.Branch.hooks.install_named_hook('open', open_hook,
428
self.run_bzr('switch --create-branch -d checkout feature')
429
# We only open the master branch 1 time.
430
# This test should be cleaner to write, but see bug:
431
# https://bugs.launchpad.net/bzr/+bug/812295
432
self.assertEqual(1, opened.count('master'))
435
class TestSmartServerSwitch(TestCaseWithTransport):
437
def test_switch_lightweight(self):
438
self.setup_smart_server_with_call_log()
439
t = self.make_branch_and_tree('from')
440
for count in range(9):
441
t.commit(message='commit %d' % count)
442
out, err = self.run_bzr(['checkout', '--lightweight', self.get_url('from'),
444
self.reset_smart_call_log()
445
self.run_bzr(['switch', self.get_url('from')], working_dir='target')
446
# This figure represent the amount of work to perform this use case. It
447
# is entirely ok to reduce this number if a test fails due to rpc_count
448
# being too low. If rpc_count increases, more network roundtrips have
449
# become necessary for this use case. Please do not adjust this number
450
# upwards without agreement from bzr's network support maintainers.
451
self.assertLength(24, self.hpss_calls)
452
self.assertLength(5, self.hpss_connections)
453
self.assertThat(self.hpss_calls, ContainsNoVfsCalls)