~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/blackbox/test_branch.py

  • Committer: Robert Collins
  • Date: 2009-03-03 03:27:51 UTC
  • mto: (4070.2.5 integration)
  • mto: This revision was merged to the branch mainline in revision 4075.
  • Revision ID: robertc@robertcollins.net-20090303032751-ubyfhezgjul6y5ic
Get BzrDir.cloning_metadir working.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006, 2008, 2009 Canonical Ltd
 
2
#
 
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.
 
7
#
 
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.
 
12
#
 
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
 
 
18
"""Black-box tests for bzr branch."""
 
19
 
 
20
import os
 
21
 
 
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
 
29
 
 
30
 
 
31
class TestBranch(ExternalBase):
 
32
 
 
33
    def example_branch(self, path='.'):
 
34
        tree = self.make_branch_and_tree(path)
 
35
        self.build_tree_contents([(path + '/hello', 'foo')])
 
36
        tree.add('hello')
 
37
        tree.commit(message='setup')
 
38
        self.build_tree_contents([(path + '/goodbye', 'baz')])
 
39
        tree.add('goodbye')
 
40
        tree.commit(message='setup')
 
41
 
 
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)
 
51
 
 
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)
 
58
 
 
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'])
 
65
        tree_a.add('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')
 
69
        f.close()
 
70
        tree_a.commit('commit a-2', rev_id='a-2')
 
71
 
 
72
        tree_b = make_shared_tree('b')
 
73
        self.build_tree(['repo/b/file'])
 
74
        tree_b.add('file')
 
75
        tree_b.commit('commit b-1', rev_id='b-1')
 
76
 
 
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'))
 
80
 
 
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'))
 
89
 
 
90
    def test_branch_hardlink(self):
 
91
        self.requireFeature(HardlinkFeature)
 
92
        source = self.make_branch_and_tree('source')
 
93
        self.build_tree(['source/file1'])
 
94
        source.add('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)
 
100
 
 
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)))
 
109
 
 
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')
 
115
 
 
116
 
 
117
class TestBranchStacked(ExternalBase):
 
118
    """Tests for branch --stacked"""
 
119
 
 
120
    def check_shallow_branch(self, branch_revid, stacked_on):
 
121
        """Assert that the branch 'newbranch' has been published correctly.
 
122
 
 
123
        :param stacked_on: url of a branch this one is stacked upon.
 
124
        :param branch_revid: a revision id that should be the only
 
125
            revision present in the stacked branch, and it should not be in
 
126
            the reference branch.
 
127
        """
 
128
        new_branch = branch.Branch.open('newbranch')
 
129
        # The branch refers to the mainline
 
130
        self.assertEqual(stacked_on, new_branch.get_stacked_on_url())
 
131
        # and the branch's work was pushed
 
132
        self.assertTrue(new_branch.repository.has_revision(branch_revid))
 
133
        # The newly committed revision shoud be present in the stacked branch,
 
134
        # but not in the stacked-on branch.  Because stacking is set up by the
 
135
        # branch object, if we open the stacked branch's repository directly,
 
136
        # bypassing the branch, we see only what's in the stacked repository.
 
137
        stacked_repo = bzrdir.BzrDir.open('newbranch').open_repository()
 
138
        stacked_repo_revisions = set(stacked_repo.all_revision_ids())
 
139
        if len(stacked_repo_revisions) != 1:
 
140
            self.fail("wrong revisions in stacked repository: %r"
 
141
                % (stacked_repo_revisions,))
 
142
 
 
143
    def assertRevisionInRepository(self, repo_path, revid):
 
144
        """Check that a revision is in a repository, disregarding stacking."""
 
145
        repo = bzrdir.BzrDir.open(repo_path).open_repository()
 
146
        self.assertTrue(repo.has_revision(revid))
 
147
 
 
148
    def assertRevisionNotInRepository(self, repo_path, revid):
 
149
        """Check that a revision is not in a repository, disregarding stacking."""
 
150
        repo = bzrdir.BzrDir.open(repo_path).open_repository()
 
151
        self.assertFalse(repo.has_revision(revid))
 
152
 
 
153
    def assertRevisionsInBranchRepository(self, revid_list, branch_path):
 
154
        repo = branch.Branch.open(branch_path).repository
 
155
        self.assertEqual(set(revid_list),
 
156
            repo.has_revisions(revid_list))
 
157
 
 
158
    def test_branch_stacked_branch_not_stacked(self):
 
159
        """Branching a stacked branch is not stacked by default"""
 
160
        # We have a mainline
 
161
        trunk_tree = self.make_branch_and_tree('target',
 
162
            format='development')
 
163
        trunk_tree.commit('mainline')
 
164
        # and a branch from it which is stacked
 
165
        branch_tree = self.make_branch_and_tree('branch',
 
166
            format='development')
 
167
        branch_tree.branch.set_stacked_on_url(trunk_tree.branch.base)
 
168
        # with some work on it
 
169
        branch_tree.commit('moar work plz')
 
170
        # branching our local branch gives us a new stacked branch pointing at
 
171
        # mainline.
 
172
        out, err = self.run_bzr(['branch', 'branch', 'newbranch'])
 
173
        self.assertEqual('', out)
 
174
        self.assertEqual('Branched 1 revision(s).\n',
 
175
            err)
 
176
        # it should have preserved the branch format, and so it should be
 
177
        # capable of supporting stacking, but not actually have a stacked_on
 
178
        # branch configured
 
179
        self.assertRaises(errors.NotStacked,
 
180
            bzrdir.BzrDir.open('newbranch').open_branch().get_stacked_on_url)
 
181
 
 
182
    def test_branch_stacked_branch_stacked(self):
 
183
        """Asking to stack on a stacked branch does work"""
 
184
        # We have a mainline
 
185
        trunk_tree = self.make_branch_and_tree('target',
 
186
            format='development')
 
187
        trunk_revid = trunk_tree.commit('mainline')
 
188
        # and a branch from it which is stacked
 
189
        branch_tree = self.make_branch_and_tree('branch',
 
190
            format='development')
 
191
        branch_tree.branch.set_stacked_on_url(trunk_tree.branch.base)
 
192
        # with some work on it
 
193
        branch_revid = branch_tree.commit('moar work plz')
 
194
        # you can chain branches on from there
 
195
        out, err = self.run_bzr(['branch', 'branch', '--stacked', 'branch2'])
 
196
        self.assertEqual('', out)
 
197
        self.assertEqual('Created new stacked branch referring to %s.\n' %
 
198
            branch_tree.branch.base, err)
 
199
        self.assertEqual(branch_tree.branch.base,
 
200
            branch.Branch.open('branch2').get_stacked_on_url())
 
201
        branch2_tree = WorkingTree.open('branch2')
 
202
        branch2_revid = branch2_tree.commit('work on second stacked branch')
 
203
        self.assertRevisionInRepository('branch2', branch2_revid)
 
204
        self.assertRevisionsInBranchRepository(
 
205
            [trunk_revid, branch_revid, branch2_revid],
 
206
            'branch2')
 
207
 
 
208
    def test_branch_stacked(self):
 
209
        # We have a mainline
 
210
        trunk_tree = self.make_branch_and_tree('mainline',
 
211
            format='development')
 
212
        original_revid = trunk_tree.commit('mainline')
 
213
        self.assertRevisionInRepository('mainline', original_revid)
 
214
        # and a branch from it which is stacked
 
215
        out, err = self.run_bzr(['branch', '--stacked', 'mainline',
 
216
            'newbranch'])
 
217
        self.assertEqual('', out)
 
218
        self.assertEqual('Created new stacked branch referring to %s.\n' %
 
219
            trunk_tree.branch.base, err)
 
220
        self.assertRevisionNotInRepository('newbranch', original_revid)
 
221
        new_tree = WorkingTree.open('newbranch')
 
222
        new_revid = new_tree.commit('new work')
 
223
        self.check_shallow_branch(new_revid, trunk_tree.branch.base)
 
224
 
 
225
    def test_branch_stacked_from_smart_server(self):
 
226
        # We can branch stacking on a smart server
 
227
        from bzrlib.smart.server import SmartTCPServer_for_testing
 
228
        self.transport_server = SmartTCPServer_for_testing
 
229
        trunk = self.make_branch('mainline', format='development')
 
230
        out, err = self.run_bzr(
 
231
            ['branch', '--stacked', self.get_url('mainline'), 'shallow'])
 
232
 
 
233
    def test_branch_stacked_from_non_stacked_format(self):
 
234
        """The origin format doesn't support stacking"""
 
235
        trunk = self.make_branch('trunk', format='pack-0.92')
 
236
        out, err = self.run_bzr(
 
237
            ['branch', '--stacked', 'trunk', 'shallow'])
 
238
        # We should notify the user that we upgraded their format
 
239
        self.assertEqualDiff(
 
240
            'Source format does not support stacking, using format: \'1.6\'\n'
 
241
            '  Packs 5 (adds stacking support, requires bzr 1.6)\n'
 
242
            '\n'
 
243
            'Created new stacked branch referring to %s.\n' % (trunk.base,),
 
244
            err)
 
245
 
 
246
    def test_branch_stacked_from_rich_root_non_stackable(self):
 
247
        trunk = self.make_branch('trunk', format='rich-root-pack')
 
248
        out, err = self.run_bzr(
 
249
            ['branch', '--stacked', 'trunk', 'shallow'])
 
250
        # We should notify the user that we upgraded their format
 
251
        self.assertEqualDiff(
 
252
            'Source format does not support stacking, using format:'
 
253
            ' \'1.6.1-rich-root\'\n'
 
254
            '  Packs 5 rich-root (adds stacking support, requires bzr 1.6.1)\n'
 
255
            '\n'
 
256
            'Created new stacked branch referring to %s.\n' % (trunk.base,),
 
257
            err)
 
258
 
 
259
 
 
260
class TestSmartServerBranching(ExternalBase):
 
261
 
 
262
    def test_branch_from_trivial_branch_to_same_server_branch_acceptance(self):
 
263
        self.setup_smart_server_with_call_log()
 
264
        t = self.make_branch_and_tree('from')
 
265
        for count in range(9):
 
266
            t.commit(message='commit %d' % count)
 
267
        self.reset_smart_call_log()
 
268
        out, err = self.run_bzr(['branch', self.get_url('from'),
 
269
            self.get_url('target')])
 
270
        rpc_count = len(self.hpss_calls)
 
271
        # This figure represent the amount of work to perform this use case. It
 
272
        # is entirely ok to reduce this number if a test fails due to rpc_count
 
273
        # being too low. If rpc_count increases, more network roundtrips have
 
274
        # become necessary for this use case. Please do not adjust this number
 
275
        # upwards without agreement from bzr's network support maintainers.
 
276
        self.assertEqual(95, rpc_count)
 
277
 
 
278
    def test_branch_from_trivial_branch_streaming_acceptance(self):
 
279
        self.setup_smart_server_with_call_log()
 
280
        t = self.make_branch_and_tree('from')
 
281
        for count in range(9):
 
282
            t.commit(message='commit %d' % count)
 
283
        self.reset_smart_call_log()
 
284
        out, err = self.run_bzr(['branch', self.get_url('from'),
 
285
            'local-target'])
 
286
        rpc_count = len(self.hpss_calls)
 
287
        # This figure represent the amount of work to perform this use case. It
 
288
        # is entirely ok to reduce this number if a test fails due to rpc_count
 
289
        # being too low. If rpc_count increases, more network roundtrips have
 
290
        # become necessary for this use case. Please do not adjust this number
 
291
        # upwards without agreement from bzr's network support maintainers.
 
292
        self.assertEqual(21, rpc_count)
 
293
 
 
294
 
 
295
class TestRemoteBranch(TestCaseWithSFTPServer):
 
296
 
 
297
    def setUp(self):
 
298
        super(TestRemoteBranch, self).setUp()
 
299
        tree = self.make_branch_and_tree('branch')
 
300
        self.build_tree_contents([('branch/file', 'file content\n')])
 
301
        tree.add('file')
 
302
        tree.commit('file created')
 
303
 
 
304
    def test_branch_local_remote(self):
 
305
        self.run_bzr(['branch', 'branch', self.get_url('remote')])
 
306
        t = self.get_transport()
 
307
        # Ensure that no working tree what created remotely
 
308
        self.assertFalse(t.has('remote/file'))
 
309
 
 
310
    def test_branch_remote_remote(self):
 
311
        # Light cheat: we access the branch remotely
 
312
        self.run_bzr(['branch', self.get_url('branch'),
 
313
                      self.get_url('remote')])
 
314
        t = self.get_transport()
 
315
        # Ensure that no working tree what created remotely
 
316
        self.assertFalse(t.has('remote/file'))
 
317