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