~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_branch.py

  • Committer: Aaron Bentley
  • Date: 2005-12-15 06:48:46 UTC
  • mto: This revision was merged to the branch mainline in revision 1533.
  • Revision ID: aaron.bentley@utoronto.ca-20051215064846-aa6cb6c42313c8e3
Updated NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2012, 2016 Canonical Ltd
2
 
#
 
1
# (C) 2005 Canonical Ltd
 
2
 
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
#
 
7
 
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
#
 
12
 
13
13
# You should have received a copy of the GNU General Public License
14
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
16
 
 
17
 
"""Tests for branch implementations - tests a branch format."""
18
 
 
19
 
import contextlib
20
 
 
21
 
from bzrlib import (
22
 
    branch as _mod_branch,
23
 
    controldir,
24
 
    config,
25
 
    delta as _mod_delta,
26
 
    errors,
27
 
    merge,
28
 
    osutils,
29
 
    urlutils,
30
 
    transform,
31
 
    transport,
32
 
    remote,
33
 
    repository,
34
 
    revision,
35
 
    shelf,
36
 
    symbol_versioning,
37
 
    tests,
38
 
    )
39
 
from bzrlib.tests import (
40
 
    per_branch,
41
 
    )
42
 
from bzrlib.tests.http_server import HttpServer
43
 
from bzrlib.transport import memory
44
 
 
45
 
 
46
 
class TestTestCaseWithBranch(per_branch.TestCaseWithBranch):
47
 
 
48
 
    def test_branch_format_matches_bzrdir_branch_format(self):
49
 
        bzrdir_branch_format = self.bzrdir_format.get_branch_format()
50
 
        self.assertIs(self.branch_format.__class__,
51
 
                      bzrdir_branch_format.__class__)
52
 
 
53
 
    def test_make_branch_gets_expected_format(self):
54
 
        branch = self.make_branch('.')
55
 
        self.assertIs(self.branch_format.__class__,
56
 
            branch._format.__class__)
57
 
 
58
 
 
59
 
class TestBranch(per_branch.TestCaseWithBranch):
60
 
 
61
 
    def test_create_tree_with_merge(self):
62
 
        tree = self.create_tree_with_merge()
63
 
        tree.lock_read()
64
 
        self.addCleanup(tree.unlock)
65
 
        graph = tree.branch.repository.get_graph()
66
 
        ancestry_graph = graph.get_parent_map(
67
 
            tree.branch.repository.all_revision_ids())
68
 
        self.assertEqual({'rev-1':('null:',),
69
 
                          'rev-2':('rev-1', ),
70
 
                          'rev-1.1.1':('rev-1', ),
71
 
                          'rev-3':('rev-2', 'rev-1.1.1', ),
72
 
                         }, ancestry_graph)
73
 
 
74
 
    def test_revision_ids_are_utf8(self):
75
 
        wt = self.make_branch_and_tree('tree')
76
 
        wt.commit('f', rev_id='rev1')
77
 
        wt.commit('f', rev_id='rev2')
78
 
        wt.commit('f', rev_id='rev3')
79
 
 
80
 
        br = self.get_branch()
81
 
        br.fetch(wt.branch)
82
 
        br.generate_revision_history('rev3')
83
 
        for revision_id in ['rev3', 'rev2', 'rev1']:
84
 
            self.assertIsInstance(revision_id, str)
85
 
        last = br.last_revision()
86
 
        self.assertEqual('rev3', last)
87
 
        self.assertIsInstance(last, str)
88
 
        revno, last = br.last_revision_info()
89
 
        self.assertEqual(3, revno)
90
 
        self.assertEqual('rev3', last)
91
 
        self.assertIsInstance(last, str)
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
import os
 
18
 
 
19
from bzrlib.branch import Branch, needs_read_lock, needs_write_lock
 
20
from bzrlib.clone import copy_branch
 
21
from bzrlib.commit import commit
 
22
import bzrlib.errors as errors
 
23
from bzrlib.errors import NoSuchRevision, UnlistableBranch, NotBranchError
 
24
import bzrlib.gpg
 
25
from bzrlib.tests import TestCase, TestCaseInTempDir
 
26
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
 
27
from bzrlib.trace import mutter
 
28
import bzrlib.transactions as transactions
 
29
from bzrlib.revision import NULL_REVISION
 
30
 
 
31
# TODO: Make a branch using basis branch, and check that it 
 
32
# doesn't request any files that could have been avoided, by 
 
33
# hooking into the Transport.
 
34
 
 
35
class TestBranch(TestCaseInTempDir):
 
36
 
 
37
    def test_append_revisions(self):
 
38
        """Test appending more than one revision"""
 
39
        br = Branch.initialize(u".")
 
40
        br.append_revision("rev1")
 
41
        self.assertEquals(br.revision_history(), ["rev1",])
 
42
        br.append_revision("rev2", "rev3")
 
43
        self.assertEquals(br.revision_history(), ["rev1", "rev2", "rev3"])
92
44
 
93
45
    def test_fetch_revisions(self):
94
46
        """Test fetch-revision operation."""
95
 
        wt = self.make_branch_and_tree('b1')
96
 
        b1 = wt.branch
97
 
        self.build_tree_contents([('b1/foo', 'hello')])
98
 
        wt.add(['foo'], ['foo-id'])
99
 
        wt.commit('lala!', rev_id='revision-1', allow_pointless=False)
100
 
 
101
 
        b2 = self.make_branch('b2')
102
 
        b2.fetch(b1)
103
 
 
104
 
        rev = b2.repository.get_revision('revision-1')
105
 
        tree = b2.repository.revision_tree('revision-1')
106
 
        tree.lock_read()
107
 
        self.addCleanup(tree.unlock)
108
 
        self.assertEqual(tree.get_file_text('foo-id'), 'hello')
109
 
 
110
 
    def test_get_revision_delta(self):
111
 
        tree_a = self.make_branch_and_tree('a')
112
 
        self.build_tree(['a/foo'])
113
 
        tree_a.add('foo', 'file1')
114
 
        tree_a.commit('rev1', rev_id='rev1')
115
 
        self.build_tree(['a/vla'])
116
 
        tree_a.add('vla', 'file2')
117
 
        tree_a.commit('rev2', rev_id='rev2')
118
 
 
119
 
        delta = self.applyDeprecated(symbol_versioning.deprecated_in(
120
 
            (2, 5, 0)), tree_a.branch.get_revision_delta, 1)
121
 
        self.assertIsInstance(delta, _mod_delta.TreeDelta)
122
 
        self.assertEqual([('foo', 'file1', 'file')], delta.added)
123
 
        delta = self.applyDeprecated(symbol_versioning.deprecated_in(
124
 
            (2, 5, 0)), tree_a.branch.get_revision_delta, 2)
125
 
        self.assertIsInstance(delta, _mod_delta.TreeDelta)
126
 
        self.assertEqual([('vla', 'file2', 'file')], delta.added)
127
 
 
128
 
    def get_unbalanced_tree_pair(self):
 
47
        from bzrlib.fetch import Fetcher
 
48
        os.mkdir('b1')
 
49
        os.mkdir('b2')
 
50
        b1 = Branch.initialize('b1')
 
51
        b2 = Branch.initialize('b2')
 
52
        file(os.sep.join(['b1', 'foo']), 'w').write('hello')
 
53
        b1.working_tree().add(['foo'], ['foo-id'])
 
54
        b1.working_tree().commit('lala!', rev_id='revision-1', allow_pointless=False)
 
55
 
 
56
        mutter('start fetch')
 
57
        f = Fetcher(from_branch=b1, to_branch=b2)
 
58
        eq = self.assertEquals
 
59
        eq(f.count_copied, 1)
 
60
        eq(f.last_revision, 'revision-1')
 
61
 
 
62
        rev = b2.get_revision('revision-1')
 
63
        tree = b2.revision_tree('revision-1')
 
64
        eq(tree.get_file_text('foo-id'), 'hello')
 
65
 
 
66
    def test_revision_tree(self):
 
67
        b1 = Branch.initialize(u'.')
 
68
        b1.working_tree().commit('lala!', rev_id='revision-1', allow_pointless=True)
 
69
        tree = b1.revision_tree('revision-1')
 
70
        tree = b1.revision_tree(None)
 
71
        self.assertEqual(len(tree.list_files()), 0)
 
72
        tree = b1.revision_tree(NULL_REVISION)
 
73
        self.assertEqual(len(tree.list_files()), 0)
 
74
 
 
75
    def get_unbalanced_branch_pair(self):
129
76
        """Return two branches, a and b, with one file in a."""
130
 
        tree_a = self.make_branch_and_tree('a')
131
 
        self.build_tree_contents([('a/b', 'b')])
132
 
        tree_a.add('b')
133
 
        tree_a.commit("silly commit", rev_id='A')
134
 
 
135
 
        tree_b = self.make_branch_and_tree('b')
136
 
        return tree_a, tree_b
 
77
        os.mkdir('a')
 
78
        br_a = Branch.initialize("a")
 
79
        file('a/b', 'wb').write('b')
 
80
        br_a.working_tree().add('b')
 
81
        commit(br_a, "silly commit", rev_id='A')
 
82
        os.mkdir('b')
 
83
        br_b = Branch.initialize("b")
 
84
        return br_a, br_b
137
85
 
138
86
    def get_balanced_branch_pair(self):
139
87
        """Returns br_a, br_b as with one commit in a, and b has a's stores."""
140
 
        tree_a, tree_b = self.get_unbalanced_tree_pair()
141
 
        tree_b.branch.repository.fetch(tree_a.branch.repository)
142
 
        return tree_a, tree_b
143
 
 
144
 
    def test_clone_partial(self):
 
88
        br_a, br_b = self.get_unbalanced_branch_pair()
 
89
        br_a.push_stores(br_b)
 
90
        return br_a, br_b
 
91
 
 
92
    def test_push_stores(self):
 
93
        """Copy the stores from one branch to another"""
 
94
        br_a, br_b = self.get_unbalanced_branch_pair()
 
95
        # ensure the revision is missing.
 
96
        self.assertRaises(NoSuchRevision, br_b.get_revision, 
 
97
                          br_a.revision_history()[0])
 
98
        br_a.push_stores(br_b)
 
99
        # check that b now has all the data from a's first commit.
 
100
        rev = br_b.get_revision(br_a.revision_history()[0])
 
101
        tree = br_b.revision_tree(br_a.revision_history()[0])
 
102
        for file_id in tree:
 
103
            if tree.inventory[file_id].kind == "file":
 
104
                tree.get_file(file_id).read()
 
105
        return br_a, br_b
 
106
 
 
107
    def test_copy_branch(self):
 
108
        """Copy the stores from one branch to another"""
 
109
        br_a, br_b = self.get_balanced_branch_pair()
 
110
        commit(br_b, "silly commit")
 
111
        os.mkdir('c')
 
112
        br_c = copy_branch(br_a, 'c', basis_branch=br_b)
 
113
        self.assertEqual(br_a.revision_history(), br_c.revision_history())
 
114
 
 
115
    def test_copy_partial(self):
145
116
        """Copy only part of the history of a branch."""
146
 
        # TODO: RBC 20060208 test with a revision not on revision-history.
147
 
        #       what should that behaviour be ? Emailed the list.
148
 
        # First, make a branch with two commits.
149
 
        wt_a = self.make_branch_and_tree('a')
150
 
        self.build_tree(['a/one'])
151
 
        wt_a.add(['one'])
152
 
        wt_a.commit('commit one', rev_id='1')
 
117
        self.build_tree(['a/', 'a/one'])
 
118
        br_a = Branch.initialize('a')
 
119
        br_a.working_tree().add(['one'])
 
120
        br_a.working_tree().commit('commit one', rev_id='u@d-1')
153
121
        self.build_tree(['a/two'])
154
 
        wt_a.add(['two'])
155
 
        wt_a.commit('commit two', rev_id='2')
156
 
        # Now make a copy of the repository.
157
 
        repo_b = self.make_repository('b')
158
 
        wt_a.branch.repository.copy_content_into(repo_b)
159
 
        # wt_a might be a lightweight checkout, so get a hold of the actual
160
 
        # branch (because you can't do a partial clone of a lightweight
161
 
        # checkout).
162
 
        branch = wt_a.branch.bzrdir.open_branch()
163
 
        # Then make a branch where the new repository is, but specify a revision
164
 
        # ID.  The new branch's history will stop at the specified revision.
165
 
        br_b = branch.clone(repo_b.bzrdir, revision_id='1')
166
 
        self.assertEqual('1', br_b.last_revision())
167
 
 
168
 
    def get_parented_branch(self):
169
 
        wt_a = self.make_branch_and_tree('a')
170
 
        self.build_tree(['a/one'])
171
 
        wt_a.add(['one'])
172
 
        wt_a.commit('commit one', rev_id='1')
173
 
 
174
 
        branch_b = wt_a.branch.bzrdir.sprout('b', revision_id='1').open_branch()
175
 
        self.assertEqual(wt_a.branch.base, branch_b.get_parent())
176
 
        return branch_b
177
 
 
178
 
    def test_clone_branch_nickname(self):
179
 
        # test the nick name is preserved always
180
 
        raise tests.TestSkipped('XXX branch cloning is not yet tested.')
181
 
 
182
 
    def test_clone_branch_parent(self):
183
 
        # test the parent is preserved always
184
 
        branch_b = self.get_parented_branch()
185
 
        repo_c = self.make_repository('c')
186
 
        branch_b.repository.copy_content_into(repo_c)
187
 
        branch_c = branch_b.clone(repo_c.bzrdir)
188
 
        self.assertNotEqual(None, branch_c.get_parent())
189
 
        self.assertEqual(branch_b.get_parent(), branch_c.get_parent())
190
 
 
191
 
        # We can also set a specific parent, and it should be honored
192
 
        random_parent = 'http://example.com/path/to/branch'
193
 
        branch_b.set_parent(random_parent)
194
 
        repo_d = self.make_repository('d')
195
 
        branch_b.repository.copy_content_into(repo_d)
196
 
        branch_d = branch_b.clone(repo_d.bzrdir)
197
 
        self.assertEqual(random_parent, branch_d.get_parent())
198
 
 
199
 
    def test_submit_branch(self):
200
 
        """Submit location can be queried and set"""
201
 
        branch = self.make_branch('branch')
202
 
        self.assertEqual(branch.get_submit_branch(), None)
203
 
        branch.set_submit_branch('sftp://example.com')
204
 
        self.assertEqual(branch.get_submit_branch(), 'sftp://example.com')
205
 
        branch.set_submit_branch('sftp://example.net')
206
 
        self.assertEqual(branch.get_submit_branch(), 'sftp://example.net')
207
 
 
208
 
    def test_public_branch(self):
209
 
        """public location can be queried and set"""
210
 
        branch = self.make_branch('branch')
211
 
        self.assertEqual(branch.get_public_branch(), None)
212
 
        branch.set_public_branch('sftp://example.com')
213
 
        self.assertEqual(branch.get_public_branch(), 'sftp://example.com')
214
 
        branch.set_public_branch('sftp://example.net')
215
 
        self.assertEqual(branch.get_public_branch(), 'sftp://example.net')
216
 
        branch.set_public_branch(None)
217
 
        self.assertEqual(branch.get_public_branch(), None)
218
 
 
219
 
    def test_record_initial_ghost(self):
220
 
        """Branches should support having ghosts."""
221
 
        wt = self.make_branch_and_tree('.')
222
 
        if not wt.branch.repository._format.supports_ghosts:
223
 
            raise tests.TestNotApplicable("repository format does not "
224
 
                "support ghosts")
225
 
        wt.set_parent_ids(['non:existent@rev--ision--0--2'],
226
 
            allow_leftmost_as_ghost=True)
227
 
        self.assertEqual(['non:existent@rev--ision--0--2'],
228
 
            wt.get_parent_ids())
229
 
        rev_id = wt.commit('commit against a ghost first parent.')
230
 
        rev = wt.branch.repository.get_revision(rev_id)
231
 
        self.assertEqual(rev.parent_ids, ['non:existent@rev--ision--0--2'])
 
122
        br_a.working_tree().add(['two'])
 
123
        br_a.working_tree().commit('commit two', rev_id='u@d-2')
 
124
        br_b = copy_branch(br_a, 'b', revision='u@d-1')
 
125
        self.assertEqual(br_b.last_revision(), 'u@d-1')
 
126
        self.assertTrue(os.path.exists('b/one'))
 
127
        self.assertFalse(os.path.exists('b/two'))
 
128
        
 
129
    def test_record_initial_ghost_merge(self):
 
130
        """A pending merge with no revision present is still a merge."""
 
131
        branch = Branch.initialize(u'.')
 
132
        branch.working_tree().add_pending_merge('non:existent@rev--ision--0--2')
 
133
        branch.working_tree().commit('pretend to merge nonexistent-revision', rev_id='first')
 
134
        rev = branch.get_revision(branch.last_revision())
 
135
        self.assertEqual(len(rev.parent_ids), 1)
232
136
        # parent_sha1s is not populated now, WTF. rbc 20051003
233
137
        self.assertEqual(len(rev.parent_sha1s), 0)
234
 
 
235
 
    def test_record_two_ghosts(self):
236
 
        """Recording with all ghosts works."""
237
 
        wt = self.make_branch_and_tree('.')
238
 
        if not wt.branch.repository._format.supports_ghosts:
239
 
            raise tests.TestNotApplicable("repository format does not "
240
 
                "support ghosts")
241
 
        wt.set_parent_ids([
242
 
                'foo@azkhazan-123123-abcabc',
243
 
                'wibble@fofof--20050401--1928390812',
244
 
            ],
245
 
            allow_leftmost_as_ghost=True)
246
 
        rev_id = wt.commit("commit from ghost base with one merge")
247
 
        # the revision should have been committed with two parents
248
 
        rev = wt.branch.repository.get_revision(rev_id)
249
 
        self.assertEqual(['foo@azkhazan-123123-abcabc',
250
 
            'wibble@fofof--20050401--1928390812'],
251
 
            rev.parent_ids)
 
138
        self.assertEqual(rev.parent_ids[0], 'non:existent@rev--ision--0--2')
252
139
 
253
140
    def test_bad_revision(self):
254
 
        self.assertRaises(errors.InvalidRevisionId,
255
 
                          self.get_branch().repository.get_revision,
256
 
                          None)
257
 
 
258
 
    def test_nicks_bzr(self):
259
 
        """Test the behaviour of branch nicks specific to bzr branches.
260
 
 
261
 
        Nicknames are implicitly the name of the branch's directory, unless an
262
 
        explicit nickname is set.  That is, an explicit nickname always
263
 
        overrides the implicit one.
264
 
 
265
 
        """
266
 
        t = self.get_transport()
267
 
        branch = self.make_branch('bzr.dev')
268
 
        if not isinstance(branch, _mod_branch.BzrBranch):
269
 
            raise tests.TestNotApplicable("not a bzr branch format")
270
 
        # The nick will be 'bzr.dev', because there is no explicit nick set.
 
141
        branch = Branch.initialize(u'.')
 
142
        self.assertRaises(errors.InvalidRevisionId, branch.get_revision, None)
 
143
 
 
144
# TODO 20051003 RBC:
 
145
# compare the gpg-to-sign info for a commit with a ghost and 
 
146
#     an identical tree without a ghost
 
147
# fetch missing should rewrite the TOC of weaves to list newly available parents.
 
148
        
 
149
    def test_pending_merges(self):
 
150
        """Tracking pending-merged revisions."""
 
151
        b = Branch.initialize(u'.')
 
152
        wt = b.working_tree()
 
153
        self.assertEquals(wt.pending_merges(), [])
 
154
        wt.add_pending_merge('foo@azkhazan-123123-abcabc')
 
155
        self.assertEquals(wt.pending_merges(), ['foo@azkhazan-123123-abcabc'])
 
156
        wt.add_pending_merge('foo@azkhazan-123123-abcabc')
 
157
        self.assertEquals(wt.pending_merges(), ['foo@azkhazan-123123-abcabc'])
 
158
        wt.add_pending_merge('wibble@fofof--20050401--1928390812')
 
159
        self.assertEquals(wt.pending_merges(),
 
160
                          ['foo@azkhazan-123123-abcabc',
 
161
                           'wibble@fofof--20050401--1928390812'])
 
162
        b.working_tree().commit("commit from base with two merges")
 
163
        rev = b.get_revision(b.revision_history()[0])
 
164
        self.assertEquals(len(rev.parent_ids), 2)
 
165
        self.assertEquals(rev.parent_ids[0],
 
166
                          'foo@azkhazan-123123-abcabc')
 
167
        self.assertEquals(rev.parent_ids[1],
 
168
                           'wibble@fofof--20050401--1928390812')
 
169
        # list should be cleared when we do a commit
 
170
        self.assertEquals(wt.pending_merges(), [])
 
171
 
 
172
    def test_sign_existing_revision(self):
 
173
        branch = Branch.initialize(u'.')
 
174
        branch.working_tree().commit("base", allow_pointless=True, rev_id='A')
 
175
        from bzrlib.testament import Testament
 
176
        branch.sign_revision('A', bzrlib.gpg.LoopbackGPGStrategy(None))
 
177
        self.assertEqual(Testament.from_revision(branch, 'A').as_short_text(),
 
178
                         branch.revision_store.get('A', 'sig').read())
 
179
 
 
180
    def test_store_signature(self):
 
181
        branch = Branch.initialize(u'.')
 
182
        branch.store_revision_signature(bzrlib.gpg.LoopbackGPGStrategy(None),
 
183
                                        'FOO', 'A')
 
184
        self.assertEqual('FOO', branch.revision_store.get('A', 'sig').read())
 
185
 
 
186
    def test__relcontrolfilename(self):
 
187
        branch = Branch.initialize(u'.')
 
188
        self.assertEqual('.bzr/%25', branch._rel_controlfilename('%'))
 
189
        
 
190
    def test__relcontrolfilename_empty(self):
 
191
        branch = Branch.initialize(u'.')
 
192
        self.assertEqual('.bzr', branch._rel_controlfilename(''))
 
193
 
 
194
    def test_nicks(self):
 
195
        """Branch nicknames"""
 
196
        os.mkdir('bzr.dev')
 
197
        branch = Branch.initialize('bzr.dev')
271
198
        self.assertEqual(branch.nick, 'bzr.dev')
272
 
        # Move the branch to a different directory, 'bzr.ab'.  Now that branch
273
 
        # will report its nick as 'bzr.ab'.
274
 
        t.move('bzr.dev', 'bzr.ab')
275
 
        branch = _mod_branch.Branch.open(self.get_url('bzr.ab'))
 
199
        os.rename('bzr.dev', 'bzr.ab')
 
200
        branch = Branch.open('bzr.ab')
276
201
        self.assertEqual(branch.nick, 'bzr.ab')
277
 
        # Set the branch nick explicitly.  This will ensure there's a branch
278
 
        # config file in the branch.
279
 
        branch.nick = "Aaron's branch"
280
 
        if not isinstance(branch, remote.RemoteBranch):
281
 
            self.assertTrue(branch._transport.has("branch.conf"))
282
 
        # Because the nick has been set explicitly, the nick is now always
283
 
        # "Aaron's branch", regardless of directory name.
284
 
        self.assertEqual(branch.nick, "Aaron's branch")
285
 
        t.move('bzr.ab', 'integration')
286
 
        branch = _mod_branch.Branch.open(self.get_url('integration'))
287
 
        self.assertEqual(branch.nick, "Aaron's branch")
288
 
        branch.nick = u"\u1234"
289
 
        self.assertEqual(branch.nick, u"\u1234")
290
 
 
291
 
    def test_nicks(self):
292
 
        """Test explicit and implicit branch nicknames.
293
 
 
294
 
        A nickname is always available, whether set explicitly or not.
295
 
        """
296
 
        t = self.get_transport()
297
 
        branch = self.make_branch('bzr.dev')
298
 
        # An implicit nick name is set; what it is exactly depends on the
299
 
        # format.
300
 
        self.assertIsInstance(branch.nick, basestring)
301
 
        # Set the branch nick explicitly.
302
 
        branch.nick = "Aaron's branch"
303
 
        # Because the nick has been set explicitly, the nick is now always
304
 
        # "Aaron's branch".
 
202
        branch.nick = "Aaron's branch"
 
203
        branch.nick = "Aaron's branch"
 
204
        self.failUnless(os.path.exists(branch.controlfilename("branch.conf")))
 
205
        self.assertEqual(branch.nick, "Aaron's branch")
 
206
        os.rename('bzr.ab', 'integration')
 
207
        branch = Branch.open('integration')
305
208
        self.assertEqual(branch.nick, "Aaron's branch")
306
209
        branch.nick = u"\u1234"
307
210
        self.assertEqual(branch.nick, u"\u1234")
308
211
 
309
212
    def test_commit_nicks(self):
310
213
        """Nicknames are committed to the revision"""
311
 
        wt = self.make_branch_and_tree('bzr.dev')
312
 
        branch = wt.branch
 
214
        os.mkdir('bzr.dev')
 
215
        branch = Branch.initialize('bzr.dev')
313
216
        branch.nick = "My happy branch"
314
 
        wt.commit('My commit respect da nick.')
315
 
        committed = branch.repository.get_revision(branch.last_revision())
316
 
        self.assertEqual(committed.properties["branch-nick"],
 
217
        branch.working_tree().commit('My commit respect da nick.')
 
218
        committed = branch.get_revision(branch.last_revision())
 
219
        self.assertEqual(committed.properties["branch-nick"], 
317
220
                         "My happy branch")
318
221
 
319
 
    def test_create_colocated(self):
320
 
        try:
321
 
            repo = self.make_repository('.', shared=True)
322
 
        except errors.IncompatibleFormat:
323
 
            return
324
 
        if repo.bzrdir._format.colocated_branches:
325
 
            raise tests.TestNotApplicable(
326
 
                "control dir does not support colocated branches")
327
 
        self.assertEqual(0, len(repo.bzrdir.list_branches()))
328
 
        if not self.bzrdir_format.colocated_branches:
329
 
            raise tests.TestNotApplicable("control dir format does not support "
330
 
                "colocated branches")
331
 
        try:
332
 
            child_branch1 = self.branch_format.initialize(repo.bzrdir, 
333
 
                name='branch1')
334
 
        except errors.UninitializableFormat:
335
 
            # branch references are not default init'able and
336
 
            # not all bzrdirs support colocated branches.
337
 
            return
338
 
        self.assertEqual(1, len(repo.bzrdir.list_branches()))
339
 
        self.branch_format.initialize(repo.bzrdir, name='branch2')
340
 
        self.assertEqual(2, len(repo.bzrdir.list_branches()))
341
 
 
342
 
    def test_create_append_revisions_only(self):
343
 
        try:
344
 
            repo = self.make_repository('.', shared=True)
345
 
        except errors.IncompatibleFormat:
346
 
            return
347
 
        for val in (True, False):
348
 
            try:
349
 
                branch = self.branch_format.initialize(repo.bzrdir,
350
 
                    append_revisions_only=True)
351
 
            except (errors.UninitializableFormat, errors.UpgradeRequired):
352
 
                # branch references are not default init'able and
353
 
                # not all branches support append_revisions_only
354
 
                return
355
 
            self.assertEqual(True, branch.get_append_revisions_only())
356
 
            repo.bzrdir.destroy_branch()
357
 
 
358
 
    def test_get_set_append_revisions_only(self):
359
 
        branch = self.make_branch('.')
360
 
        if branch._format.supports_set_append_revisions_only():
361
 
            branch.set_append_revisions_only(True)
362
 
            self.assertTrue(branch.get_append_revisions_only())
363
 
            branch.set_append_revisions_only(False)
364
 
            self.assertFalse(branch.get_append_revisions_only())
365
 
        else:
366
 
            self.assertRaises(errors.UpgradeRequired,
367
 
                branch.set_append_revisions_only, True)
368
 
            self.assertFalse(branch.get_append_revisions_only())
369
 
 
370
 
    def test_create_open_branch_uses_repository(self):
371
 
        try:
372
 
            repo = self.make_repository('.', shared=True)
373
 
        except errors.IncompatibleFormat:
374
 
            raise tests.TestNotApplicable("requires shared repository support")
375
 
        child_transport = repo.bzrdir.root_transport.clone('child')
376
 
        child_transport.mkdir('.')
377
 
        try:
378
 
            child_dir = self.bzrdir_format.initialize_on_transport(child_transport)
379
 
        except errors.UninitializableFormat:
380
 
            raise tests.TestNotApplicable("control dir format not initializable")
381
 
        try:
382
 
            child_branch = self.branch_format.initialize(child_dir)
383
 
        except errors.UninitializableFormat:
384
 
            # branch references are not default init'able.
385
 
            return
386
 
        self.assertEqual(repo.bzrdir.root_transport.base,
387
 
                         child_branch.repository.bzrdir.root_transport.base)
388
 
        child_branch = _mod_branch.Branch.open(self.get_url('child'))
389
 
        self.assertEqual(repo.bzrdir.root_transport.base,
390
 
                         child_branch.repository.bzrdir.root_transport.base)
391
 
 
392
 
    def test_format_description(self):
393
 
        tree = self.make_branch_and_tree('tree')
394
 
        text = tree.branch._format.get_format_description()
395
 
        self.assertTrue(len(text))
396
 
 
397
 
    def test_get_commit_builder(self):
398
 
        branch = self.make_branch(".")
399
 
        branch.lock_write()
400
 
        builder = branch.get_commit_builder([])
401
 
        self.assertIsInstance(builder, repository.CommitBuilder)
402
 
        branch.repository.commit_write_group()
403
 
        branch.unlock()
404
 
 
405
 
    def test_generate_revision_history(self):
406
 
        """Create a fake revision history easily."""
407
 
        tree = self.make_branch_and_tree('.')
408
 
        rev1 = tree.commit('foo')
409
 
        tree.lock_write()
410
 
        self.addCleanup(tree.unlock)
411
 
        graph = tree.branch.repository.get_graph()
412
 
        orig_history = list(
413
 
            graph.iter_lefthand_ancestry(
414
 
                tree.branch.last_revision(), [revision.NULL_REVISION]))
415
 
        rev2 = tree.commit('bar', allow_pointless=True)
416
 
        tree.branch.generate_revision_history(rev1)
417
 
        self.assertEqual(orig_history, list(
418
 
            graph.iter_lefthand_ancestry(
419
 
                tree.branch.last_revision(), [revision.NULL_REVISION])))
420
 
 
421
 
    def test_generate_revision_history_NULL_REVISION(self):
422
 
        tree = self.make_branch_and_tree('.')
423
 
        rev1 = tree.commit('foo')
424
 
        tree.lock_write()
425
 
        self.addCleanup(tree.unlock)
426
 
        tree.branch.generate_revision_history(revision.NULL_REVISION)
427
 
        self.assertEqual(revision.NULL_REVISION, tree.branch.last_revision())
428
 
 
429
 
    def test_create_checkout(self):
430
 
        tree_a = self.make_branch_and_tree('a')
431
 
        branch_a = tree_a.branch
432
 
        checkout_b = branch_a.create_checkout('b')
433
 
        self.assertEqual('null:', checkout_b.last_revision())
434
 
        checkout_b.commit('rev1', rev_id='rev1')
435
 
        self.assertEqual('rev1', branch_a.last_revision())
436
 
        self.assertNotEqual(checkout_b.branch.base, branch_a.base)
437
 
 
438
 
        checkout_c = branch_a.create_checkout('c', lightweight=True)
439
 
        self.assertEqual('rev1', checkout_c.last_revision())
440
 
        checkout_c.commit('rev2', rev_id='rev2')
441
 
        self.assertEqual('rev2', branch_a.last_revision())
442
 
        self.assertEqual(checkout_c.branch.base, branch_a.base)
443
 
 
444
 
        checkout_d = branch_a.create_checkout('d', lightweight=True)
445
 
        self.assertEqual('rev2', checkout_d.last_revision())
446
 
        checkout_e = branch_a.create_checkout('e')
447
 
        self.assertEqual('rev2', checkout_e.last_revision())
448
 
 
449
 
    def test_create_anonymous_lightweight_checkout(self):
450
 
        """A lightweight checkout from a readonly branch should succeed."""
451
 
        tree_a = self.make_branch_and_tree('a')
452
 
        rev_id = tree_a.commit('put some content in the branch')
453
 
        # open the branch via a readonly transport
454
 
        url = self.get_readonly_url(urlutils.basename(tree_a.branch.base))
455
 
        t = transport.get_transport_from_url(url)
456
 
        if not tree_a.branch.bzrdir._format.supports_transport(t):
457
 
            raise tests.TestNotApplicable("format does not support transport")
458
 
        source_branch = _mod_branch.Branch.open(url)
459
 
        # sanity check that the test will be valid
460
 
        self.assertRaises((errors.LockError, errors.TransportNotPossible),
461
 
            source_branch.lock_write)
462
 
        checkout = source_branch.create_checkout('c', lightweight=True)
463
 
        self.assertEqual(rev_id, checkout.last_revision())
464
 
 
465
 
    def test_create_anonymous_heavyweight_checkout(self):
466
 
        """A regular checkout from a readonly branch should succeed."""
467
 
        tree_a = self.make_branch_and_tree('a')
468
 
        rev_id = tree_a.commit('put some content in the branch')
469
 
        # open the branch via a readonly transport
470
 
        url = self.get_readonly_url(
471
 
            osutils.basename(tree_a.branch.base.rstrip('/')))
472
 
        t = transport.get_transport_from_url(url)
473
 
        if not tree_a.branch.bzrdir._format.supports_transport(t):
474
 
            raise tests.TestNotApplicable("format does not support transport")
475
 
        source_branch = _mod_branch.Branch.open(url)
476
 
        # sanity check that the test will be valid
477
 
        self.assertRaises((errors.LockError, errors.TransportNotPossible),
478
 
            source_branch.lock_write)
479
 
        checkout = source_branch.create_checkout('c')
480
 
        self.assertEqual(rev_id, checkout.last_revision())
481
 
 
482
 
    def test_heads_to_fetch(self):
483
 
        # heads_to_fetch is a method that returns a collection of revids that
484
 
        # need to be fetched to copy this branch into another repo.  At a
485
 
        # minimum this will include the tip.
486
 
        # (In native formats, this is the tip + tags, but other formats may
487
 
        # have other revs needed)
488
 
        tree = self.make_branch_and_tree('a')
489
 
        tree.commit('first commit', rev_id='rev1')
490
 
        tree.commit('second commit', rev_id='rev2')
491
 
        must_fetch, should_fetch = tree.branch.heads_to_fetch()
492
 
        self.assertTrue('rev2' in must_fetch)
493
 
 
494
 
    def test_heads_to_fetch_not_null_revision(self):
495
 
        # NULL_REVISION does not appear in the result of heads_to_fetch, even
496
 
        # for an empty branch.
497
 
        tree = self.make_branch_and_tree('a')
498
 
        must_fetch, should_fetch = tree.branch.heads_to_fetch()
499
 
        self.assertFalse(revision.NULL_REVISION in must_fetch)
500
 
        self.assertFalse(revision.NULL_REVISION in should_fetch)
501
 
 
502
 
 
503
 
class TestBranchFormat(per_branch.TestCaseWithBranch):
504
 
 
505
 
    def test_branch_format_network_name(self):
506
 
        br = self.make_branch('.')
507
 
        format = br._format
508
 
        network_name = format.network_name()
509
 
        self.assertIsInstance(network_name, str)
510
 
        # We want to test that the network_name matches the actual format on
511
 
        # disk. For local branches that means that using network_name as a key
512
 
        # in the registry gives back the same format. For remote branches we
513
 
        # check that the network_name of the RemoteBranchFormat we have locally
514
 
        # matches the actual format present on disk.
515
 
        if isinstance(format, remote.RemoteBranchFormat):
516
 
            br._ensure_real()
517
 
            real_branch = br._real_branch
518
 
            self.assertEqual(real_branch._format.network_name(), network_name)
519
 
        else:
520
 
            registry = _mod_branch.network_format_registry
521
 
            looked_up_format = registry.get(network_name)
522
 
            self.assertEqual(format.__class__, looked_up_format.__class__)
523
 
 
524
 
    def test_get_config_calls(self):
525
 
        # Smoke test that all branch succeed getting a config
526
 
        br = self.make_branch('.')
527
 
        br.get_config()
528
 
        br.get_config_stack()
529
 
 
530
 
 
531
 
class ChrootedTests(per_branch.TestCaseWithBranch):
532
 
    """A support class that provides readonly urls outside the local namespace.
533
 
 
534
 
    This is done by checking if self.transport_server is a MemoryServer. if it
535
 
    is then we are chrooted already, if it is not then an HttpServer is used
536
 
    for readonly urls.
537
 
    """
538
 
 
539
 
    def setUp(self):
540
 
        super(ChrootedTests, self).setUp()
541
 
        if not self.vfs_transport_factory == memory.MemoryServer:
542
 
            self.transport_readonly_server = HttpServer
 
222
 
 
223
class TestRemote(TestCaseWithWebserver):
543
224
 
544
225
    def test_open_containing(self):
545
 
        self.assertRaises(errors.NotBranchError,
546
 
                          _mod_branch.Branch.open_containing,
547
 
                          self.get_readonly_url(''))
548
 
        self.assertRaises(errors.NotBranchError,
549
 
                          _mod_branch.Branch.open_containing,
550
 
                          self.get_readonly_url('g/p/q'))
551
 
        branch = self.make_branch('.')
552
 
        if not branch.bzrdir._format.supports_transport(
553
 
            transport.get_transport_from_url(self.get_readonly_url('.'))):
554
 
            raise tests.TestNotApplicable("format does not support transport")
555
 
        branch, relpath = _mod_branch.Branch.open_containing(
556
 
            self.get_readonly_url(''))
 
226
        self.assertRaises(NotBranchError, Branch.open_containing,
 
227
                          self.get_remote_url(''))
 
228
        self.assertRaises(NotBranchError, Branch.open_containing,
 
229
                          self.get_remote_url('g/p/q'))
 
230
        b = Branch.initialize(u'.')
 
231
        branch, relpath = Branch.open_containing(self.get_remote_url(''))
557
232
        self.assertEqual('', relpath)
558
 
        branch, relpath = _mod_branch.Branch.open_containing(
559
 
            self.get_readonly_url('g/p/q'))
 
233
        branch, relpath = Branch.open_containing(self.get_remote_url('g/p/q'))
560
234
        self.assertEqual('g/p/q', relpath)
561
 
 
 
235
        
 
236
# TODO: rewrite this as a regular unittest, without relying on the displayed output        
 
237
#         >>> from bzrlib.commit import commit
 
238
#         >>> bzrlib.trace.silent = True
 
239
#         >>> br1 = ScratchBranch(files=['foo', 'bar'])
 
240
#         >>> br1.working_tree().add('foo')
 
241
#         >>> br1.working_tree().add('bar')
 
242
#         >>> commit(br1, "lala!", rev_id="REVISION-ID-1", verbose=False)
 
243
#         >>> br2 = ScratchBranch()
 
244
#         >>> br2.update_revisions(br1)
 
245
#         Added 2 texts.
 
246
#         Added 1 inventories.
 
247
#         Added 1 revisions.
 
248
#         >>> br2.revision_history()
 
249
#         [u'REVISION-ID-1']
 
250
#         >>> br2.update_revisions(br1)
 
251
#         Added 0 revisions.
 
252
#         >>> br1.text_store.total_size() == br2.text_store.total_size()
 
253
#         True
562
254
 
563
255
class InstrumentedTransaction(object):
564
256
 
583
275
    def unlock(self):
584
276
        self._calls.append('ul')
585
277
 
586
 
    @_mod_branch.needs_read_lock
 
278
    @needs_read_lock
587
279
    def do_with_read(self):
588
280
        return 1
589
281
 
590
 
    @_mod_branch.needs_read_lock
 
282
    @needs_read_lock
591
283
    def except_with_read(self):
592
284
        raise RuntimeError
593
285
 
594
 
    @_mod_branch.needs_write_lock
 
286
    @needs_write_lock
595
287
    def do_with_write(self):
596
288
        return 2
597
289
 
598
 
    @_mod_branch.needs_write_lock
 
290
    @needs_write_lock
599
291
    def except_with_write(self):
600
292
        raise RuntimeError
601
293
 
602
294
 
603
 
class TestDecorators(tests.TestCase):
 
295
class TestDecorators(TestCase):
604
296
 
605
297
    def test_needs_read_lock(self):
606
298
        branch = TestDecorator()
623
315
        self.assertEqual(['lw', 'ul'], branch._calls)
624
316
 
625
317
 
626
 
class TestBranchPushLocations(per_branch.TestCaseWithBranch):
627
 
 
 
318
class TestBranchTransaction(TestCaseInTempDir):
 
319
 
 
320
    def setUp(self):
 
321
        super(TestBranchTransaction, self).setUp()
 
322
        self.branch = Branch.initialize(u'.')
 
323
        
 
324
    def test_default_get_transaction(self):
 
325
        """branch.get_transaction on a new branch should give a PassThrough."""
 
326
        self.failUnless(isinstance(self.branch.get_transaction(),
 
327
                                   transactions.PassThroughTransaction))
 
328
 
 
329
    def test__set_new_transaction(self):
 
330
        self.branch._set_transaction(transactions.ReadOnlyTransaction())
 
331
 
 
332
    def test__set_over_existing_transaction_raises(self):
 
333
        self.branch._set_transaction(transactions.ReadOnlyTransaction())
 
334
        self.assertRaises(errors.LockError,
 
335
                          self.branch._set_transaction,
 
336
                          transactions.ReadOnlyTransaction())
 
337
 
 
338
    def test_finish_no_transaction_raises(self):
 
339
        self.assertRaises(errors.LockError, self.branch._finish_transaction)
 
340
 
 
341
    def test_finish_readonly_transaction_works(self):
 
342
        self.branch._set_transaction(transactions.ReadOnlyTransaction())
 
343
        self.branch._finish_transaction()
 
344
        self.assertEqual(None, self.branch._transaction)
 
345
 
 
346
    def test_unlock_calls_finish(self):
 
347
        self.branch.lock_read()
 
348
        transaction = InstrumentedTransaction()
 
349
        self.branch._transaction = transaction
 
350
        self.branch.unlock()
 
351
        self.assertEqual(['finish'], transaction.calls)
 
352
 
 
353
    def test_lock_read_acquires_ro_transaction(self):
 
354
        self.branch.lock_read()
 
355
        self.failUnless(isinstance(self.branch.get_transaction(),
 
356
                                   transactions.ReadOnlyTransaction))
 
357
        self.branch.unlock()
 
358
        
 
359
    def test_lock_write_acquires_passthrough_transaction(self):
 
360
        self.branch.lock_write()
 
361
        # cannot use get_transaction as its magic
 
362
        self.failUnless(isinstance(self.branch._transaction,
 
363
                                   transactions.PassThroughTransaction))
 
364
        self.branch.unlock()
 
365
 
 
366
 
 
367
class TestBranchPushLocations(TestCaseInTempDir):
 
368
 
 
369
    def setUp(self):
 
370
        super(TestBranchPushLocations, self).setUp()
 
371
        self.branch = Branch.initialize(u'.')
 
372
        
628
373
    def test_get_push_location_unset(self):
629
 
        self.assertEqual(None, self.get_branch().get_push_location())
 
374
        self.assertEqual(None, self.branch.get_push_location())
630
375
 
631
376
    def test_get_push_location_exact(self):
632
 
        b = self.get_branch()
633
 
        config.LocationConfig.from_string(
634
 
            '[%s]\npush_location=foo\n' % (b.base,), b.base, save=True)
635
 
        self.assertEqual("foo", self.get_branch().get_push_location())
 
377
        self.build_tree(['.bazaar/'])
 
378
        print >> open('.bazaar/branches.conf', 'wt'), ("[%s]\n"
 
379
                                                       "push_location=foo" %
 
380
                                                       os.getcwdu())
 
381
        self.assertEqual("foo", self.branch.get_push_location())
636
382
 
637
383
    def test_set_push_location(self):
638
 
        branch = self.get_branch()
639
 
        branch.set_push_location('foo')
640
 
        self.assertEqual('foo', branch.get_push_location())
641
 
 
642
 
 
643
 
class TestChildSubmitFormats(per_branch.TestCaseWithBranch):
644
 
 
645
 
    def test_get_child_submit_format_default(self):
646
 
        submit_format = self.get_branch().get_child_submit_format()
647
 
        self.assertTrue(submit_format is None or
648
 
                        isinstance(submit_format, str))
649
 
 
650
 
    def test_get_child_submit_format(self):
651
 
        branch = self.get_branch()
652
 
        branch.get_config_stack().set('child_submit_format', '10')
653
 
        branch = self.get_branch()
654
 
        self.assertEqual('10', branch.get_child_submit_format())
655
 
 
656
 
 
657
 
class TestFormat(per_branch.TestCaseWithBranch):
658
 
    """Tests for the format itself."""
659
 
 
660
 
    def test_get_reference(self):
661
 
        """get_reference on all regular branches should return None."""
662
 
        if not self.branch_format.is_supported():
663
 
            # unsupported formats are not loopback testable
664
 
            # because the default open will not open them and
665
 
            # they may not be initializable.
666
 
            return
667
 
        made_branch = self.make_branch('.')
668
 
        self.assertEqual(None,
669
 
            made_branch._format.get_reference(made_branch.bzrdir))
670
 
 
671
 
    def test_set_reference(self):
672
 
        """set_reference on all regular branches should be callable."""
673
 
        if not self.branch_format.is_supported():
674
 
            # unsupported formats are not loopback testable
675
 
            # because the default open will not open them and
676
 
            # they may not be initializable.
677
 
            return
678
 
        this_branch = self.make_branch('this')
679
 
        other_branch = self.make_branch('other')
680
 
        try:
681
 
            this_branch._format.set_reference(this_branch.bzrdir, None,
682
 
                other_branch)
683
 
        except NotImplementedError:
684
 
            # that's ok
685
 
            pass
686
 
        else:
687
 
            ref = this_branch._format.get_reference(this_branch.bzrdir)
688
 
            self.assertEqual(ref, other_branch.base)
689
 
 
690
 
    def test_format_initialize_find_open(self):
691
 
        # loopback test to check the current format initializes to itself.
692
 
        if not self.branch_format.is_supported():
693
 
            # unsupported formats are not loopback testable
694
 
            # because the default open will not open them and
695
 
            # they may not be initializable.
696
 
            return
697
 
        # supported formats must be able to init and open
698
 
        t = self.get_transport()
699
 
        readonly_t = transport.get_transport_from_url(self.get_readonly_url())
700
 
        made_branch = self.make_branch('.')
701
 
        self.assertIsInstance(made_branch, _mod_branch.Branch)
702
 
 
703
 
        # find it via bzrdir opening:
704
 
        opened_control = controldir.ControlDir.open(readonly_t.base)
705
 
        direct_opened_branch = opened_control.open_branch()
706
 
        self.assertEqual(direct_opened_branch.__class__, made_branch.__class__)
707
 
        self.assertEqual(opened_control, direct_opened_branch.bzrdir)
708
 
        self.assertIsInstance(direct_opened_branch._format,
709
 
            self.branch_format.__class__)
710
 
 
711
 
        # find it via Branch.open
712
 
        opened_branch = _mod_branch.Branch.open(readonly_t.base)
713
 
        self.assertIsInstance(opened_branch, made_branch.__class__)
714
 
        self.assertEqual(made_branch._format.__class__,
715
 
                         opened_branch._format.__class__)
716
 
        # if it has a unique id string, can we probe for it ?
717
 
        try:
718
 
            self.branch_format.get_format_string()
719
 
        except NotImplementedError:
720
 
            return
721
 
        self.assertEqual(self.branch_format,
722
 
                         opened_control.find_branch_format())
723
 
 
724
 
 
725
 
class TestBound(per_branch.TestCaseWithBranch):
726
 
 
727
 
    def test_bind_unbind(self):
728
 
        branch = self.make_branch('1')
729
 
        branch2 = self.make_branch('2')
730
 
        try:
731
 
            branch.bind(branch2)
732
 
        except errors.UpgradeRequired:
733
 
            raise tests.TestNotApplicable('Format does not support binding')
734
 
        self.assertTrue(branch.unbind())
735
 
        self.assertFalse(branch.unbind())
736
 
        self.assertIs(None, branch.get_bound_location())
737
 
 
738
 
    def test_old_bound_location(self):
739
 
        branch = self.make_branch('branch1')
740
 
        try:
741
 
            self.assertIs(None, branch.get_old_bound_location())
742
 
        except errors.UpgradeRequired:
743
 
            raise tests.TestNotApplicable(
744
 
                    'Format does not store old bound locations')
745
 
        branch2 = self.make_branch('branch2')
746
 
        branch.bind(branch2)
747
 
        self.assertIs(None, branch.get_old_bound_location())
748
 
        branch.unbind()
749
 
        self.assertContainsRe(branch.get_old_bound_location(), '\/branch2\/$')
750
 
 
751
 
    def test_bind_diverged(self):
752
 
        tree_a = self.make_branch_and_tree('tree_a')
753
 
        tree_a.commit('rev1a')
754
 
        tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
755
 
        tree_a.commit('rev2a')
756
 
        tree_b.commit('rev2b')
757
 
        try:
758
 
            tree_b.branch.bind(tree_a.branch)
759
 
        except errors.UpgradeRequired:
760
 
            raise tests.TestNotApplicable('Format does not support binding')
761
 
 
762
 
    def test_unbind_clears_cached_master_branch(self):
763
 
        """b.unbind clears any cached value of b.get_master_branch."""
764
 
        master = self.make_branch('master')
765
 
        branch = self.make_branch('branch')
766
 
        try:
767
 
            branch.bind(master)
768
 
        except errors.UpgradeRequired:
769
 
            raise tests.TestNotApplicable('Format does not support binding')
770
 
        self.addCleanup(branch.lock_write().unlock)
771
 
        self.assertNotEqual(None, branch.get_master_branch())
772
 
        branch.unbind()
773
 
        self.assertEqual(None, branch.get_master_branch())
774
 
 
775
 
    def test_bind_clears_cached_master_branch(self):
776
 
        """b.bind clears any cached value of b.get_master_branch."""
777
 
        master1 = self.make_branch('master1')
778
 
        master2 = self.make_branch('master2')
779
 
        branch = self.make_branch('branch')
780
 
        try:
781
 
            branch.bind(master1)
782
 
        except errors.UpgradeRequired:
783
 
            raise tests.TestNotApplicable('Format does not support binding')
784
 
        self.addCleanup(branch.lock_write().unlock)
785
 
        self.assertNotEqual(None, branch.get_master_branch())
786
 
        branch.bind(master2)
787
 
        self.assertEqual('.', urlutils.relative_url(self.get_url('master2'),
788
 
                branch.get_master_branch().base))
789
 
 
790
 
    def test_set_bound_location_clears_cached_master_branch(self):
791
 
        """b.set_bound_location clears any cached value of b.get_master_branch.
792
 
        """
793
 
        master1 = self.make_branch('master1')
794
 
        master2 = self.make_branch('master2')
795
 
        branch = self.make_branch('branch')
796
 
        try:
797
 
            branch.bind(master1)
798
 
        except errors.UpgradeRequired:
799
 
            raise tests.TestNotApplicable('Format does not support binding')
800
 
        self.addCleanup(branch.lock_write().unlock)
801
 
        self.assertNotEqual(None, branch.get_master_branch())
802
 
        branch.set_bound_location(self.get_url('master2'))
803
 
        self.assertEqual('.', urlutils.relative_url(self.get_url('master2'),
804
 
                branch.get_master_branch().base))
805
 
 
806
 
 
807
 
class TestStrict(per_branch.TestCaseWithBranch):
808
 
 
809
 
    def test_strict_history(self):
810
 
        tree1 = self.make_branch_and_tree('tree1')
811
 
        try:
812
 
            tree1.branch.set_append_revisions_only(True)
813
 
        except errors.UpgradeRequired:
814
 
            raise tests.TestSkipped('Format does not support strict history')
815
 
        tree1.commit('empty commit')
816
 
        tree2 = tree1.bzrdir.sprout('tree2').open_workingtree()
817
 
        tree2.commit('empty commit 2')
818
 
        tree1.pull(tree2.branch)
819
 
        tree1.commit('empty commit 3')
820
 
        tree2.commit('empty commit 4')
821
 
        self.assertRaises(errors.DivergedBranches, tree1.pull, tree2.branch)
822
 
        tree2.merge_from_branch(tree1.branch)
823
 
        tree2.commit('empty commit 5')
824
 
        self.assertRaises(errors.AppendRevisionsOnlyViolation, tree1.pull,
825
 
                          tree2.branch)
826
 
        tree3 = tree1.bzrdir.sprout('tree3').open_workingtree()
827
 
        tree3.merge_from_branch(tree2.branch)
828
 
        tree3.commit('empty commit 6')
829
 
        tree2.pull(tree3.branch)
830
 
 
831
 
 
832
 
class TestIgnoreFallbacksParameter(per_branch.TestCaseWithBranch):
833
 
 
834
 
    def make_branch_with_fallback(self):
835
 
        fallback = self.make_branch('fallback')
836
 
        if not fallback._format.supports_stacking():
837
 
            raise tests.TestNotApplicable("format does not support stacking")
838
 
        stacked = self.make_branch('stacked')
839
 
        stacked.set_stacked_on_url(fallback.base)
840
 
        return stacked
841
 
 
842
 
    def test_fallbacks_not_opened(self):
843
 
        stacked = self.make_branch_with_fallback()
844
 
        self.get_transport('').rename('fallback', 'moved')
845
 
        reopened_dir = controldir.ControlDir.open(stacked.base)
846
 
        reopened = reopened_dir.open_branch(ignore_fallbacks=True)
847
 
        self.assertEqual([], reopened.repository._fallback_repositories)
848
 
 
849
 
    def test_fallbacks_are_opened(self):
850
 
        stacked = self.make_branch_with_fallback()
851
 
        reopened_dir = controldir.ControlDir.open(stacked.base)
852
 
        reopened = reopened_dir.open_branch(ignore_fallbacks=False)
853
 
        self.assertLength(1, reopened.repository._fallback_repositories)
854
 
 
855
 
 
856
 
class TestReferenceLocation(per_branch.TestCaseWithBranch):
857
 
 
858
 
    def test_reference_parent(self):
859
 
        tree = self.make_branch_and_tree('tree')
860
 
        subtree = self.make_branch_and_tree('tree/subtree')
861
 
        subtree.set_root_id('subtree-id')
862
 
        try:
863
 
            tree.add_reference(subtree)
864
 
        except errors.UnsupportedOperation:
865
 
            raise tests.TestNotApplicable('Tree cannot hold references.')
866
 
        reference_parent = tree.branch.reference_parent(
867
 
            'subtree-id',
868
 
            urlutils.relative_url(tree.branch.user_url, subtree.branch.user_url))
869
 
        self.assertEqual(subtree.branch.base, reference_parent.base)
870
 
 
871
 
    def test_reference_parent_accepts_possible_transports(self):
872
 
        tree = self.make_branch_and_tree('tree')
873
 
        subtree = self.make_branch_and_tree('tree/subtree')
874
 
        subtree.set_root_id('subtree-id')
875
 
        try:
876
 
            tree.add_reference(subtree)
877
 
        except errors.UnsupportedOperation:
878
 
            raise tests.TestNotApplicable('Tree cannot hold references.')
879
 
        reference_parent = tree.branch.reference_parent('subtree-id',
880
 
            urlutils.relative_url(
881
 
                tree.branch.user_url, subtree.branch.user_url),
882
 
            possible_transports=[subtree.bzrdir.root_transport])
883
 
 
884
 
    def test_get_reference_info(self):
885
 
        branch = self.make_branch('branch')
886
 
        try:
887
 
            path, loc = branch.get_reference_info('file-id')
888
 
        except errors.UnsupportedOperation:
889
 
            raise tests.TestNotApplicable('Branch cannot hold references.')
890
 
        self.assertIs(None, path)
891
 
        self.assertIs(None, loc)
892
 
 
893
 
    def test_set_reference_info(self):
894
 
        branch = self.make_branch('branch')
895
 
        try:
896
 
            branch.set_reference_info('file-id', 'path/to/location',
897
 
                                      'path/to/file')
898
 
        except errors.UnsupportedOperation:
899
 
            raise tests.TestNotApplicable('Branch cannot hold references.')
900
 
 
901
 
    def test_set_get_reference_info(self):
902
 
        branch = self.make_branch('branch')
903
 
        try:
904
 
            branch.set_reference_info('file-id', 'path/to/file',
905
 
                                      'path/to/location')
906
 
        except errors.UnsupportedOperation:
907
 
            raise tests.TestNotApplicable('Branch cannot hold references.')
908
 
        # Create a new instance to ensure storage is permanent
909
 
        branch = _mod_branch.Branch.open('branch')
910
 
        tree_path, branch_location = branch.get_reference_info('file-id')
911
 
        self.assertEqual('path/to/location', branch_location)
912
 
 
913
 
    def test_set_null_reference_info(self):
914
 
        branch = self.make_branch('branch')
915
 
        try:
916
 
            branch.set_reference_info('file-id', 'path/to/file',
917
 
                                      'path/to/location')
918
 
        except errors.UnsupportedOperation:
919
 
            raise tests.TestNotApplicable('Branch cannot hold references.')
920
 
        branch.set_reference_info('file-id', None, None)
921
 
        tree_path, branch_location = branch.get_reference_info('file-id')
922
 
        self.assertIs(None, tree_path)
923
 
        self.assertIs(None, branch_location)
924
 
 
925
 
    def test_set_null_reference_info_when_null(self):
926
 
        branch = self.make_branch('branch')
927
 
        try:
928
 
            tree_path, branch_location = branch.get_reference_info('file-id')
929
 
        except errors.UnsupportedOperation:
930
 
            raise tests.TestNotApplicable('Branch cannot hold references.')
931
 
        self.assertIs(None, tree_path)
932
 
        self.assertIs(None, branch_location)
933
 
        branch.set_reference_info('file-id', None, None)
934
 
 
935
 
    def test_set_null_requires_two_nones(self):
936
 
        branch = self.make_branch('branch')
937
 
        try:
938
 
            e = self.assertRaises(ValueError, branch.set_reference_info,
939
 
                                  'file-id', 'path', None)
940
 
        except errors.UnsupportedOperation:
941
 
            raise tests.TestNotApplicable('Branch cannot hold references.')
942
 
        self.assertEqual('tree_path must be None when branch_location is'
943
 
                         ' None.', str(e))
944
 
        e = self.assertRaises(ValueError, branch.set_reference_info,
945
 
                              'file-id', None, 'location')
946
 
        self.assertEqual('branch_location must be None when tree_path is'
947
 
                         ' None.', str(e))
948
 
 
949
 
    def make_branch_with_reference(self, location, reference_location,
950
 
                                   file_id='file-id'):
951
 
        branch = self.make_branch(location)
952
 
        try:
953
 
            branch.set_reference_info(file_id, 'path/to/file',
954
 
                                      reference_location)
955
 
        except errors.UnsupportedOperation:
956
 
            raise tests.TestNotApplicable('Branch cannot hold references.')
957
 
        return branch
958
 
 
959
 
    def test_reference_parent_from_reference_info_(self):
960
 
        referenced_branch = self.make_branch('reference_branch')
961
 
        branch = self.make_branch_with_reference('branch',
962
 
                                                 referenced_branch.base)
963
 
        parent = branch.reference_parent('file-id', 'path/to/file')
964
 
        self.assertEqual(parent.base, referenced_branch.base)
965
 
 
966
 
    def test_branch_relative_reference_location(self):
967
 
        branch = self.make_branch('branch')
968
 
        try:
969
 
            branch.set_reference_info('file-id', 'path/to/file',
970
 
            '../reference_branch')
971
 
        except errors.UnsupportedOperation:
972
 
            raise tests.TestNotApplicable('Branch cannot hold references.')
973
 
        referenced_branch = self.make_branch('reference_branch')
974
 
        parent = branch.reference_parent('file-id', 'path/to/file')
975
 
        self.assertEqual(parent.base, referenced_branch.base)
976
 
 
977
 
    def test_sprout_copies_reference_location(self):
978
 
        branch = self.make_branch_with_reference('branch', '../reference')
979
 
        new_branch = branch.bzrdir.sprout('new-branch').open_branch()
980
 
        self.assertEqual('../reference',
981
 
                         new_branch.get_reference_info('file-id')[1])
982
 
 
983
 
    def test_clone_copies_reference_location(self):
984
 
        branch = self.make_branch_with_reference('branch', '../reference')
985
 
        new_branch = branch.bzrdir.clone('new-branch').open_branch()
986
 
        self.assertEqual('../reference',
987
 
                         new_branch.get_reference_info('file-id')[1])
988
 
 
989
 
    def test_copied_locations_are_rebased(self):
990
 
        branch = self.make_branch_with_reference('branch', 'reference')
991
 
        new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
992
 
        self.assertEqual('../reference',
993
 
                         new_branch.get_reference_info('file-id')[1])
994
 
 
995
 
    def test_update_references_retains_old_references(self):
996
 
        branch = self.make_branch_with_reference('branch', 'reference')
997
 
        new_branch = self.make_branch_with_reference(
998
 
            'new_branch', 'reference', 'file-id2')
999
 
        new_branch.update_references(branch)
1000
 
        self.assertEqual('reference',
1001
 
                         branch.get_reference_info('file-id')[1])
1002
 
 
1003
 
    def test_update_references_retains_known_references(self):
1004
 
        branch = self.make_branch_with_reference('branch', 'reference')
1005
 
        new_branch = self.make_branch_with_reference(
1006
 
            'new_branch', 'reference2')
1007
 
        new_branch.update_references(branch)
1008
 
        self.assertEqual('reference',
1009
 
                         branch.get_reference_info('file-id')[1])
1010
 
 
1011
 
    def test_update_references_skips_known_references(self):
1012
 
        branch = self.make_branch_with_reference('branch', 'reference')
1013
 
        new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
1014
 
        new_branch.set_reference_info('file-id', '../foo', '../foo')
1015
 
        new_branch.update_references(branch)
1016
 
        self.assertEqual('reference',
1017
 
                         branch.get_reference_info('file-id')[1])
1018
 
 
1019
 
    def test_pull_updates_references(self):
1020
 
        branch = self.make_branch_with_reference('branch', 'reference')
1021
 
        new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
1022
 
        new_branch.set_reference_info('file-id2', '../foo', '../foo')
1023
 
        branch.pull(new_branch)
1024
 
        self.assertEqual('foo',
1025
 
                         branch.get_reference_info('file-id2')[1])
1026
 
 
1027
 
    def test_push_updates_references(self):
1028
 
        branch = self.make_branch_with_reference('branch', 'reference')
1029
 
        new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
1030
 
        new_branch.set_reference_info('file-id2', '../foo', '../foo')
1031
 
        new_branch.push(branch)
1032
 
        self.assertEqual('foo',
1033
 
                         branch.get_reference_info('file-id2')[1])
1034
 
 
1035
 
    def test_merge_updates_references(self):
1036
 
        branch = self.make_branch_with_reference('branch', 'reference')
1037
 
        tree = self.make_branch_and_tree('tree')
1038
 
        tree.commit('foo')
1039
 
        branch.pull(tree.branch)
1040
 
        checkout = branch.create_checkout('checkout', lightweight=True)
1041
 
        checkout.commit('bar')
1042
 
        tree.lock_write()
1043
 
        self.addCleanup(tree.unlock)
1044
 
        merger = merge.Merger.from_revision_ids(None, tree,
1045
 
                                                branch.last_revision(),
1046
 
                                                other_branch=branch)
1047
 
        merger.merge_type = merge.Merge3Merger
1048
 
        merger.do_merge()
1049
 
        self.assertEqual('../branch/reference',
1050
 
                         tree.branch.get_reference_info('file-id')[1])
1051
 
 
1052
 
 
1053
 
class TestBranchControlComponent(per_branch.TestCaseWithBranch):
1054
 
    """Branch implementations adequately implement ControlComponent."""
1055
 
 
1056
 
    def test_urls(self):
1057
 
        br = self.make_branch('branch')
1058
 
        self.assertIsInstance(br.user_url, str)
1059
 
        self.assertEqual(br.user_url, br.user_transport.base)
1060
 
        # for all current bzrdir implementations the user dir must be 
1061
 
        # above the control dir but we might need to relax that?
1062
 
        self.assertEqual(br.control_url.find(br.user_url), 0)
1063
 
        self.assertEqual(br.control_url, br.control_transport.base)
1064
 
 
1065
 
 
1066
 
class FakeShelfCreator(object):
1067
 
 
1068
 
    def __init__(self, branch):
1069
 
        self.branch = branch
1070
 
 
1071
 
    def write_shelf(self, shelf_file, message=None):
1072
 
        tree = self.branch.repository.revision_tree(revision.NULL_REVISION)
1073
 
        with transform.TransformPreview(tree) as tt:
1074
 
            shelf.ShelfCreator._write_shelf(
1075
 
                shelf_file, tt, revision.NULL_REVISION)
1076
 
 
1077
 
 
1078
 
@contextlib.contextmanager
1079
 
def skip_if_storing_uncommitted_unsupported():
1080
 
    try:
1081
 
        yield
1082
 
    except errors.StoringUncommittedNotSupported:
1083
 
        raise tests.TestNotApplicable('Cannot store uncommitted changes.')
1084
 
 
1085
 
 
1086
 
class TestUncommittedChanges(per_branch.TestCaseWithBranch):
1087
 
 
1088
 
    def bind(self, branch, master):
1089
 
        try:
1090
 
            branch.bind(master)
1091
 
        except errors.UpgradeRequired:
1092
 
            raise tests.TestNotApplicable('Branch cannot be bound.')
1093
 
 
1094
 
    def test_store_uncommitted(self):
1095
 
        tree = self.make_branch_and_tree('b')
1096
 
        branch = tree.branch
1097
 
        creator = FakeShelfCreator(branch)
1098
 
        with skip_if_storing_uncommitted_unsupported():
1099
 
            self.assertIs(None, branch.get_unshelver(tree))
1100
 
        branch.store_uncommitted(creator)
1101
 
        self.assertIsNot(None, branch.get_unshelver(tree))
1102
 
 
1103
 
    def test_store_uncommitted_bound(self):
1104
 
        tree = self.make_branch_and_tree('b')
1105
 
        branch = tree.branch
1106
 
        master = self.make_branch('master')
1107
 
        self.bind(branch, master)
1108
 
        creator = FakeShelfCreator(tree.branch)
1109
 
        self.assertIs(None, tree.branch.get_unshelver(tree))
1110
 
        self.assertIs(None, master.get_unshelver(tree))
1111
 
        tree.branch.store_uncommitted(creator)
1112
 
        self.assertIsNot(None, master.get_unshelver(tree))
1113
 
 
1114
 
    def test_store_uncommitted_already_stored(self):
1115
 
        branch = self.make_branch('b')
1116
 
        with skip_if_storing_uncommitted_unsupported():
1117
 
            branch.store_uncommitted(FakeShelfCreator(branch))
1118
 
        self.assertRaises(errors.ChangesAlreadyStored,
1119
 
                          branch.store_uncommitted, FakeShelfCreator(branch))
1120
 
 
1121
 
    def test_store_uncommitted_none(self):
1122
 
        branch = self.make_branch('b')
1123
 
        with skip_if_storing_uncommitted_unsupported():
1124
 
            branch.store_uncommitted(FakeShelfCreator(branch))
1125
 
        branch.store_uncommitted(None)
1126
 
        self.assertIs(None, branch.get_unshelver(None))
1127
 
 
1128
 
    def test_get_unshelver(self):
1129
 
        tree = self.make_branch_and_tree('tree')
1130
 
        tree.commit('')
1131
 
        self.build_tree_contents([('tree/file', 'contents1')])
1132
 
        tree.add('file')
1133
 
        with skip_if_storing_uncommitted_unsupported():
1134
 
            tree.store_uncommitted()
1135
 
        unshelver = tree.branch.get_unshelver(tree)
1136
 
        self.assertIsNot(None, unshelver)
1137
 
 
1138
 
    def test_get_unshelver_bound(self):
1139
 
        tree = self.make_branch_and_tree('tree')
1140
 
        tree.commit('')
1141
 
        self.build_tree_contents([('tree/file', 'contents1')])
1142
 
        tree.add('file')
1143
 
        with skip_if_storing_uncommitted_unsupported():
1144
 
            tree.store_uncommitted()
1145
 
        branch = self.make_branch('branch')
1146
 
        self.bind(branch, tree.branch)
1147
 
        unshelver = branch.get_unshelver(tree)
1148
 
        self.assertIsNot(None, unshelver)
 
384
        self.branch.set_push_location('foo')
 
385
        self.assertFileEqual("[%s]\n"
 
386
                             "push_location = foo" % os.getcwdu(),
 
387
                             '.bazaar/branches.conf')
 
388
 
 
389
    # TODO RBC 20051029 test getting a push location from a branch in a 
 
390
    # recursive section - that is, it appends the branch name.