1
# Copyright (C) 2005, 2006, 2008, 2009 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
"""Black-box tests for bzr branch."""
22
from bzrlib import (branch, bzrdir, errors, repository)
23
from bzrlib.repofmt.knitrepo import RepositoryFormatKnit1
24
from bzrlib.tests.blackbox import ExternalBase
25
from bzrlib.tests import HardlinkFeature
26
from bzrlib.tests.test_sftp_transport import TestCaseWithSFTPServer
27
from bzrlib.urlutils import local_path_to_url, strip_trailing_slash
28
from bzrlib.workingtree import WorkingTree
31
class TestBranch(ExternalBase):
33
def example_branch(self, path='.'):
34
tree = self.make_branch_and_tree(path)
35
self.build_tree_contents([(path + '/hello', 'foo')])
37
tree.commit(message='setup')
38
self.build_tree_contents([(path + '/goodbye', 'baz')])
40
tree.commit(message='setup')
42
def test_branch(self):
43
"""Branch from one branch to another."""
44
self.example_branch('a')
45
self.run_bzr('branch a b')
46
b = branch.Branch.open('b')
47
self.run_bzr('branch a c -r 1')
48
# previously was erroneously created by branching
49
self.assertFalse(b._transport.has('branch-name'))
50
b.bzrdir.open_workingtree().commit(message='foo', allow_pointless=True)
52
def test_branch_only_copies_history(self):
53
# Knit branches should only push the history for the current revision.
54
format = bzrdir.BzrDirMetaFormat1()
55
format.repository_format = RepositoryFormatKnit1()
56
shared_repo = self.make_repository('repo', format=format, shared=True)
57
shared_repo.set_make_working_trees(True)
59
def make_shared_tree(path):
60
shared_repo.bzrdir.root_transport.mkdir(path)
61
shared_repo.bzrdir.create_branch_convenience('repo/' + path)
62
return WorkingTree.open('repo/' + path)
63
tree_a = make_shared_tree('a')
64
self.build_tree(['repo/a/file'])
66
tree_a.commit('commit a-1', rev_id='a-1')
67
f = open('repo/a/file', 'ab')
68
f.write('more stuff\n')
70
tree_a.commit('commit a-2', rev_id='a-2')
72
tree_b = make_shared_tree('b')
73
self.build_tree(['repo/b/file'])
75
tree_b.commit('commit b-1', rev_id='b-1')
77
self.assertTrue(shared_repo.has_revision('a-1'))
78
self.assertTrue(shared_repo.has_revision('a-2'))
79
self.assertTrue(shared_repo.has_revision('b-1'))
81
# Now that we have a repository with shared files, make sure
82
# that things aren't copied out by a 'branch'
83
self.run_bzr('branch repo/b branch-b')
84
pushed_tree = WorkingTree.open('branch-b')
85
pushed_repo = pushed_tree.branch.repository
86
self.assertFalse(pushed_repo.has_revision('a-1'))
87
self.assertFalse(pushed_repo.has_revision('a-2'))
88
self.assertTrue(pushed_repo.has_revision('b-1'))
90
def test_branch_hardlink(self):
91
self.requireFeature(HardlinkFeature)
92
source = self.make_branch_and_tree('source')
93
self.build_tree(['source/file1'])
95
source.commit('added file')
96
self.run_bzr(['branch', 'source', 'target', '--hardlink'])
97
source_stat = os.stat('source/file1')
98
target_stat = os.stat('target/file1')
99
self.assertEqual(source_stat, target_stat)
101
def test_branch_standalone(self):
102
shared_repo = self.make_repository('repo', shared=True)
103
self.example_branch('source')
104
self.run_bzr('branch --standalone source repo/target')
105
b = branch.Branch.open('repo/target')
106
expected_repo_path = os.path.abspath('repo/target/.bzr/repository')
107
self.assertEqual(strip_trailing_slash(b.repository.base),
108
strip_trailing_slash(local_path_to_url(expected_repo_path)))
110
def test_branch_no_tree(self):
111
self.example_branch('source')
112
self.run_bzr('branch --no-tree source target')
113
self.failIfExists('target/hello')
114
self.failIfExists('target/goodbye')
116
def test_branch_into_existing_dir(self):
117
self.example_branch('a')
118
# existing dir with similar files but no .bzr dir
119
self.build_tree_contents([('b/',)])
120
self.build_tree_contents([('b/hello', 'bar')]) # different content
121
self.build_tree_contents([('b/goodbye', 'baz')])# same content
122
# fails without --use-existing-dir
123
out,err = self.run_bzr('branch a b', retcode=3)
124
self.assertEqual('', out)
125
self.assertEqual('bzr: ERROR: Target directory "b" already exists.\n',
128
self.run_bzr('branch a b --use-existing-dir')
130
self.failUnlessExists('b/hello.moved')
131
self.failIfExists('b/godbye.moved')
132
# we can't branch into branch
133
out,err = self.run_bzr('branch a b --use-existing-dir', retcode=3)
134
self.assertEqual('', out)
135
self.assertEqual('bzr: ERROR: Already a branch: "b".\n', err)
138
class TestBranchStacked(ExternalBase):
139
"""Tests for branch --stacked"""
141
def check_shallow_branch(self, branch_revid, stacked_on):
142
"""Assert that the branch 'newbranch' has been published correctly.
144
:param stacked_on: url of a branch this one is stacked upon.
145
:param branch_revid: a revision id that should be the only
146
revision present in the stacked branch, and it should not be in
147
the reference branch.
149
new_branch = branch.Branch.open('newbranch')
150
# The branch refers to the mainline
151
self.assertEqual(stacked_on, new_branch.get_stacked_on_url())
152
# and the branch's work was pushed
153
self.assertTrue(new_branch.repository.has_revision(branch_revid))
154
# The newly committed revision shoud be present in the stacked branch,
155
# but not in the stacked-on branch. Because stacking is set up by the
156
# branch object, if we open the stacked branch's repository directly,
157
# bypassing the branch, we see only what's in the stacked repository.
158
stacked_repo = bzrdir.BzrDir.open('newbranch').open_repository()
159
stacked_repo_revisions = set(stacked_repo.all_revision_ids())
160
if len(stacked_repo_revisions) != 1:
161
self.fail("wrong revisions in stacked repository: %r"
162
% (stacked_repo_revisions,))
164
def assertRevisionInRepository(self, repo_path, revid):
165
"""Check that a revision is in a repository, disregarding stacking."""
166
repo = bzrdir.BzrDir.open(repo_path).open_repository()
167
self.assertTrue(repo.has_revision(revid))
169
def assertRevisionNotInRepository(self, repo_path, revid):
170
"""Check that a revision is not in a repository, disregarding stacking."""
171
repo = bzrdir.BzrDir.open(repo_path).open_repository()
172
self.assertFalse(repo.has_revision(revid))
174
def assertRevisionsInBranchRepository(self, revid_list, branch_path):
175
repo = branch.Branch.open(branch_path).repository
176
self.assertEqual(set(revid_list),
177
repo.has_revisions(revid_list))
179
def test_branch_stacked_branch_not_stacked(self):
180
"""Branching a stacked branch is not stacked by default"""
182
trunk_tree = self.make_branch_and_tree('target',
184
trunk_tree.commit('mainline')
185
# and a branch from it which is stacked
186
branch_tree = self.make_branch_and_tree('branch',
188
branch_tree.branch.set_stacked_on_url(trunk_tree.branch.base)
189
# with some work on it
190
branch_tree.commit('moar work plz')
191
# branching our local branch gives us a new stacked branch pointing at
193
out, err = self.run_bzr(['branch', 'branch', 'newbranch'])
194
self.assertEqual('', out)
195
self.assertEqual('Branched 1 revision(s).\n',
197
# it should have preserved the branch format, and so it should be
198
# capable of supporting stacking, but not actually have a stacked_on
200
self.assertRaises(errors.NotStacked,
201
bzrdir.BzrDir.open('newbranch').open_branch().get_stacked_on_url)
203
def test_branch_stacked_branch_stacked(self):
204
"""Asking to stack on a stacked branch does work"""
206
trunk_tree = self.make_branch_and_tree('target',
208
trunk_revid = trunk_tree.commit('mainline')
209
# and a branch from it which is stacked
210
branch_tree = self.make_branch_and_tree('branch',
212
branch_tree.branch.set_stacked_on_url(trunk_tree.branch.base)
213
# with some work on it
214
branch_revid = branch_tree.commit('moar work plz')
215
# you can chain branches on from there
216
out, err = self.run_bzr(['branch', 'branch', '--stacked', 'branch2'])
217
self.assertEqual('', out)
218
self.assertEqual('Created new stacked branch referring to %s.\n' %
219
branch_tree.branch.base, err)
220
self.assertEqual(branch_tree.branch.base,
221
branch.Branch.open('branch2').get_stacked_on_url())
222
branch2_tree = WorkingTree.open('branch2')
223
branch2_revid = branch2_tree.commit('work on second stacked branch')
224
self.assertRevisionInRepository('branch2', branch2_revid)
225
self.assertRevisionsInBranchRepository(
226
[trunk_revid, branch_revid, branch2_revid],
229
def test_branch_stacked(self):
231
trunk_tree = self.make_branch_and_tree('mainline',
233
original_revid = trunk_tree.commit('mainline')
234
self.assertRevisionInRepository('mainline', original_revid)
235
# and a branch from it which is stacked
236
out, err = self.run_bzr(['branch', '--stacked', 'mainline',
238
self.assertEqual('', out)
239
self.assertEqual('Created new stacked branch referring to %s.\n' %
240
trunk_tree.branch.base, err)
241
self.assertRevisionNotInRepository('newbranch', original_revid)
242
new_tree = WorkingTree.open('newbranch')
243
new_revid = new_tree.commit('new work')
244
self.check_shallow_branch(new_revid, trunk_tree.branch.base)
246
def test_branch_stacked_from_smart_server(self):
247
# We can branch stacking on a smart server
248
from bzrlib.smart.server import SmartTCPServer_for_testing
249
self.transport_server = SmartTCPServer_for_testing
250
trunk = self.make_branch('mainline', format='1.9')
251
out, err = self.run_bzr(
252
['branch', '--stacked', self.get_url('mainline'), 'shallow'])
254
def test_branch_stacked_from_non_stacked_format(self):
255
"""The origin format doesn't support stacking"""
256
trunk = self.make_branch('trunk', format='pack-0.92')
257
out, err = self.run_bzr(
258
['branch', '--stacked', 'trunk', 'shallow'])
259
# We should notify the user that we upgraded their format
260
self.assertEqualDiff(
261
'Source repository format does not support stacking, using format:\n'
262
' Packs 5 (adds stacking support, requires bzr 1.6)\n'
263
'Source branch format does not support stacking, using format:\n'
265
'Created new stacked branch referring to %s.\n' % (trunk.base,),
268
def test_branch_stacked_from_rich_root_non_stackable(self):
269
trunk = self.make_branch('trunk', format='rich-root-pack')
270
out, err = self.run_bzr(
271
['branch', '--stacked', 'trunk', 'shallow'])
272
# We should notify the user that we upgraded their format
273
self.assertEqualDiff(
274
'Source repository format does not support stacking, using format:\n'
275
' Packs 5 rich-root (adds stacking support, requires bzr 1.6.1)\n'
276
'Source branch format does not support stacking, using format:\n'
278
'Created new stacked branch referring to %s.\n' % (trunk.base,),
282
class TestSmartServerBranching(ExternalBase):
284
def test_branch_from_trivial_branch_to_same_server_branch_acceptance(self):
285
self.setup_smart_server_with_call_log()
286
t = self.make_branch_and_tree('from')
287
for count in range(9):
288
t.commit(message='commit %d' % count)
289
self.reset_smart_call_log()
290
out, err = self.run_bzr(['branch', self.get_url('from'),
291
self.get_url('target')])
292
# This figure represent the amount of work to perform this use case. It
293
# is entirely ok to reduce this number if a test fails due to rpc_count
294
# being too low. If rpc_count increases, more network roundtrips have
295
# become necessary for this use case. Please do not adjust this number
296
# upwards without agreement from bzr's network support maintainers.
297
self.assertLength(38, self.hpss_calls)
299
def test_branch_from_trivial_branch_streaming_acceptance(self):
300
self.setup_smart_server_with_call_log()
301
t = self.make_branch_and_tree('from')
302
for count in range(9):
303
t.commit(message='commit %d' % count)
304
self.reset_smart_call_log()
305
out, err = self.run_bzr(['branch', self.get_url('from'),
307
# This figure represent the amount of work to perform this use case. It
308
# is entirely ok to reduce this number if a test fails due to rpc_count
309
# being too low. If rpc_count increases, more network roundtrips have
310
# become necessary for this use case. Please do not adjust this number
311
# upwards without agreement from bzr's network support maintainers.
312
self.assertLength(10, self.hpss_calls)
314
def test_branch_from_trivial_stacked_branch_streaming_acceptance(self):
315
self.setup_smart_server_with_call_log()
316
t = self.make_branch_and_tree('trunk')
317
for count in range(8):
318
t.commit(message='commit %d' % count)
319
tree2 = t.branch.bzrdir.sprout('feature', stacked=True
321
tree2.commit('feature change')
322
self.reset_smart_call_log()
323
out, err = self.run_bzr(['branch', self.get_url('feature'),
325
# This figure represent the amount of work to perform this use case. It
326
# is entirely ok to reduce this number if a test fails due to rpc_count
327
# being too low. If rpc_count increases, more network roundtrips have
328
# become necessary for this use case. Please do not adjust this number
329
# upwards without agreement from bzr's network support maintainers.
330
self.assertLength(15, self.hpss_calls)
333
class TestRemoteBranch(TestCaseWithSFTPServer):
336
super(TestRemoteBranch, self).setUp()
337
tree = self.make_branch_and_tree('branch')
338
self.build_tree_contents([('branch/file', 'file content\n')])
340
tree.commit('file created')
342
def test_branch_local_remote(self):
343
self.run_bzr(['branch', 'branch', self.get_url('remote')])
344
t = self.get_transport()
345
# Ensure that no working tree what created remotely
346
self.assertFalse(t.has('remote/file'))
348
def test_branch_remote_remote(self):
349
# Light cheat: we access the branch remotely
350
self.run_bzr(['branch', self.get_url('branch'),
351
self.get_url('remote')])
352
t = self.get_transport()
353
# Ensure that no working tree what created remotely
354
self.assertFalse(t.has('remote/file'))