1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
1
# (C) 2005 Canonical Ltd
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.
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.
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""Tests for branch implementations - tests a branch format."""
33
from bzrlib.branch import Branch, needs_read_lock, needs_write_lock
34
from bzrlib.delta import TreeDelta
35
from bzrlib.errors import (FileExists,
38
UninitializableFormat,
41
from bzrlib.osutils import getcwd
42
import bzrlib.revision
43
from bzrlib.symbol_versioning import deprecated_in
44
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
45
from bzrlib.tests.branch_implementations import TestCaseWithBranch
46
from bzrlib.tests.http_server import HttpServer
47
from bzrlib.trace import mutter
48
from bzrlib.transport import get_transport
49
from bzrlib.transport.memory import MemoryServer
50
from bzrlib.upgrade import upgrade
51
from bzrlib.workingtree import WorkingTree
54
class TestTestCaseWithBranch(TestCaseWithBranch):
56
def test_branch_format_matches_bzrdir_branch_format(self):
57
bzrdir_branch_format = self.bzrdir_format.get_branch_format()
58
self.assertIs(self.branch_format.__class__,
59
bzrdir_branch_format.__class__)
61
def test_make_branch_gets_expected_format(self):
62
branch = self.make_branch('.')
63
self.assertIs(self.branch_format.__class__,
64
branch._format.__class__)
67
class TestBranch(TestCaseWithBranch):
69
def test_create_tree_with_merge(self):
70
tree = self.create_tree_with_merge()
72
self.addCleanup(tree.unlock)
73
graph = tree.branch.repository.get_graph()
74
ancestry_graph = graph.get_parent_map(
75
tree.branch.repository.all_revision_ids())
76
self.assertEqual({'rev-1':('null:',),
78
'rev-1.1.1':('rev-1', ),
79
'rev-3':('rev-2', 'rev-1.1.1', ),
82
def test_revision_ids_are_utf8(self):
83
wt = self.make_branch_and_tree('tree')
84
wt.commit('f', rev_id='rev1')
85
wt.commit('f', rev_id='rev2')
86
wt.commit('f', rev_id='rev3')
88
br = self.get_branch()
90
br.set_revision_history(['rev1', 'rev2', 'rev3'])
91
rh = br.revision_history()
92
self.assertEqual(['rev1', 'rev2', 'rev3'], rh)
93
for revision_id in rh:
94
self.assertIsInstance(revision_id, str)
95
last = br.last_revision()
96
self.assertEqual('rev3', last)
97
self.assertIsInstance(last, str)
98
revno, last = br.last_revision_info()
99
self.assertEqual(3, revno)
100
self.assertEqual('rev3', last)
101
self.assertIsInstance(last, str)
103
def test_fetch_revisions(self):
104
"""Test fetch-revision operation."""
105
wt = self.make_branch_and_tree('b1')
107
self.build_tree_contents([('b1/foo', 'hello')])
108
wt.add(['foo'], ['foo-id'])
109
wt.commit('lala!', rev_id='revision-1', allow_pointless=False)
111
b2 = self.make_branch('b2')
114
rev = b2.repository.get_revision('revision-1')
115
tree = b2.repository.revision_tree('revision-1')
117
self.addCleanup(tree.unlock)
118
self.assertEqual(tree.get_file_text('foo-id'), 'hello')
120
def test_get_revision_delta(self):
121
tree_a = self.make_branch_and_tree('a')
122
self.build_tree(['a/foo'])
123
tree_a.add('foo', 'file1')
124
tree_a.commit('rev1', rev_id='rev1')
125
self.build_tree(['a/vla'])
126
tree_a.add('vla', 'file2')
127
tree_a.commit('rev2', rev_id='rev2')
129
delta = tree_a.branch.get_revision_delta(1)
130
self.assertIsInstance(delta, TreeDelta)
131
self.assertEqual([('foo', 'file1', 'file')], delta.added)
132
delta = tree_a.branch.get_revision_delta(2)
133
self.assertIsInstance(delta, TreeDelta)
134
self.assertEqual([('vla', 'file2', 'file')], delta.added)
136
def get_unbalanced_tree_pair(self):
137
"""Return two branches, a and b, with one file in a."""
138
tree_a = self.make_branch_and_tree('a')
139
self.build_tree_contents([('a/b', 'b')])
141
tree_a.commit("silly commit", rev_id='A')
143
tree_b = self.make_branch_and_tree('b')
144
return tree_a, tree_b
146
def get_balanced_branch_pair(self):
147
"""Returns br_a, br_b as with one commit in a, and b has a's stores."""
148
tree_a, tree_b = self.get_unbalanced_tree_pair()
149
tree_b.branch.repository.fetch(tree_a.branch.repository)
150
return tree_a, tree_b
152
def test_clone_partial(self):
153
"""Copy only part of the history of a branch."""
154
# TODO: RBC 20060208 test with a revision not on revision-history.
155
# what should that behaviour be ? Emailed the list.
156
# First, make a branch with two commits.
157
wt_a = self.make_branch_and_tree('a')
158
self.build_tree(['a/one'])
160
wt_a.commit('commit one', rev_id='1')
161
self.build_tree(['a/two'])
163
wt_a.commit('commit two', rev_id='2')
164
# Now make a copy of the repository.
165
repo_b = self.make_repository('b')
166
wt_a.branch.repository.copy_content_into(repo_b)
167
# wt_a might be a lightweight checkout, so get a hold of the actual
168
# branch (because you can't do a partial clone of a lightweight
170
branch = wt_a.branch.bzrdir.open_branch()
171
# Then make a branch where the new repository is, but specify a revision
172
# ID. The new branch's history will stop at the specified revision.
173
br_b = branch.clone(repo_b.bzrdir, revision_id='1')
174
self.assertEqual('1', br_b.last_revision())
176
def get_parented_branch(self):
177
wt_a = self.make_branch_and_tree('a')
178
self.build_tree(['a/one'])
180
wt_a.commit('commit one', rev_id='1')
182
branch_b = wt_a.branch.bzrdir.sprout('b', revision_id='1').open_branch()
183
self.assertEqual(wt_a.branch.base, branch_b.get_parent())
186
def test_clone_branch_nickname(self):
187
# test the nick name is preserved always
188
raise TestSkipped('XXX branch cloning is not yet tested.')
190
def test_clone_branch_parent(self):
191
# test the parent is preserved always
192
branch_b = self.get_parented_branch()
193
repo_c = self.make_repository('c')
194
branch_b.repository.copy_content_into(repo_c)
195
branch_c = branch_b.clone(repo_c.bzrdir)
196
self.assertNotEqual(None, branch_c.get_parent())
197
self.assertEqual(branch_b.get_parent(), branch_c.get_parent())
199
# We can also set a specific parent, and it should be honored
200
random_parent = 'http://bazaar-vcs.org/path/to/branch'
201
branch_b.set_parent(random_parent)
202
repo_d = self.make_repository('d')
203
branch_b.repository.copy_content_into(repo_d)
204
branch_d = branch_b.clone(repo_d.bzrdir)
205
self.assertEqual(random_parent, branch_d.get_parent())
207
def test_submit_branch(self):
208
"""Submit location can be queried and set"""
209
branch = self.make_branch('branch')
210
self.assertEqual(branch.get_submit_branch(), None)
211
branch.set_submit_branch('sftp://example.com')
212
self.assertEqual(branch.get_submit_branch(), 'sftp://example.com')
213
branch.set_submit_branch('sftp://example.net')
214
self.assertEqual(branch.get_submit_branch(), 'sftp://example.net')
216
def test_public_branch(self):
217
"""public location can be queried and set"""
218
branch = self.make_branch('branch')
219
self.assertEqual(branch.get_public_branch(), None)
220
branch.set_public_branch('sftp://example.com')
221
self.assertEqual(branch.get_public_branch(), 'sftp://example.com')
222
branch.set_public_branch('sftp://example.net')
223
self.assertEqual(branch.get_public_branch(), 'sftp://example.net')
224
branch.set_public_branch(None)
225
self.assertEqual(branch.get_public_branch(), None)
227
def test_record_initial_ghost(self):
228
"""Branches should support having ghosts."""
229
wt = self.make_branch_and_tree('.')
230
wt.set_parent_ids(['non:existent@rev--ision--0--2'],
231
allow_leftmost_as_ghost=True)
232
self.assertEqual(['non:existent@rev--ision--0--2'],
234
rev_id = wt.commit('commit against a ghost first parent.')
235
rev = wt.branch.repository.get_revision(rev_id)
236
self.assertEqual(rev.parent_ids, ['non:existent@rev--ision--0--2'])
237
# parent_sha1s is not populated now, WTF. rbc 20051003
238
self.assertEqual(len(rev.parent_sha1s), 0)
240
def test_record_two_ghosts(self):
241
"""Recording with all ghosts works."""
242
wt = self.make_branch_and_tree('.')
244
'foo@azkhazan-123123-abcabc',
245
'wibble@fofof--20050401--1928390812',
247
allow_leftmost_as_ghost=True)
248
rev_id = wt.commit("commit from ghost base with one merge")
249
# the revision should have been committed with two parents
250
rev = wt.branch.repository.get_revision(rev_id)
251
self.assertEqual(['foo@azkhazan-123123-abcabc',
252
'wibble@fofof--20050401--1928390812'],
255
def test_bad_revision(self):
256
self.assertRaises(errors.InvalidRevisionId,
257
self.get_branch().repository.get_revision,
261
# compare the gpg-to-sign info for a commit with a ghost and
262
# an identical tree without a ghost
263
# fetch missing should rewrite the TOC of weaves to list newly available parents.
265
def test_sign_existing_revision(self):
266
wt = self.make_branch_and_tree('.')
268
wt.commit("base", allow_pointless=True, rev_id='A')
269
from bzrlib.testament import Testament
270
strategy = gpg.LoopbackGPGStrategy(None)
271
branch.repository.lock_write()
272
branch.repository.start_write_group()
273
branch.repository.sign_revision('A', strategy)
274
branch.repository.commit_write_group()
275
branch.repository.unlock()
276
self.assertEqual('-----BEGIN PSEUDO-SIGNED CONTENT-----\n' +
277
Testament.from_revision(branch.repository,
278
'A').as_short_text() +
279
'-----END PSEUDO-SIGNED CONTENT-----\n',
280
branch.repository.get_signature_text('A'))
282
def test_store_signature(self):
283
wt = self.make_branch_and_tree('.')
287
branch.repository.start_write_group()
289
branch.repository.store_revision_signature(
290
gpg.LoopbackGPGStrategy(None), 'FOO', 'A')
292
branch.repository.abort_write_group()
295
branch.repository.commit_write_group()
298
# A signature without a revision should not be accessible.
299
self.assertRaises(errors.NoSuchRevision,
300
branch.repository.has_signature_for_revision_id,
302
wt.commit("base", allow_pointless=True, rev_id='A')
303
self.assertEqual('-----BEGIN PSEUDO-SIGNED CONTENT-----\n'
304
'FOO-----END PSEUDO-SIGNED CONTENT-----\n',
305
branch.repository.get_signature_text('A'))
307
def test_branch_keeps_signatures(self):
308
wt = self.make_branch_and_tree('source')
309
wt.commit('A', allow_pointless=True, rev_id='A')
310
repo = wt.branch.repository
312
repo.start_write_group()
313
repo.sign_revision('A', gpg.LoopbackGPGStrategy(None))
314
repo.commit_write_group()
316
#FIXME: clone should work to urls,
317
# wt.clone should work to disks.
318
self.build_tree(['target/'])
319
d2 = repo.bzrdir.clone(urlutils.local_path_to_url('target'))
320
self.assertEqual(repo.get_signature_text('A'),
321
d2.open_repository().get_signature_text('A'))
323
def test_missing_revisions(self):
324
t1 = self.make_branch_and_tree('b1')
325
rev1 = t1.commit('one')
326
t2 = t1.bzrdir.sprout('b2').open_workingtree()
327
rev2 = t1.commit('two')
328
rev3 = t1.commit('three')
330
self.assertEqual([rev2, rev3],
331
self.applyDeprecated(deprecated_in((1, 6, 0)),
332
t2.branch.missing_revisions, t1.branch))
335
self.applyDeprecated(deprecated_in((1, 6, 0)),
336
t2.branch.missing_revisions, t1.branch, stop_revision=1))
337
self.assertEqual([rev2],
338
self.applyDeprecated(deprecated_in((1, 6, 0)),
339
t2.branch.missing_revisions, t1.branch, stop_revision=2))
340
self.assertEqual([rev2, rev3],
341
self.applyDeprecated(deprecated_in((1, 6, 0)),
342
t2.branch.missing_revisions, t1.branch, stop_revision=3))
344
self.assertRaises(errors.NoSuchRevision,
345
self.applyDeprecated, deprecated_in((1, 6, 0)),
346
t2.branch.missing_revisions, t1.branch, stop_revision=4)
348
rev4 = t2.commit('four')
349
self.assertRaises(errors.DivergedBranches,
350
self.applyDeprecated, deprecated_in((1, 6, 0)),
351
t2.branch.missing_revisions, t1.branch)
353
def test_nicks(self):
354
"""Test explicit and implicit branch nicknames.
356
Nicknames are implicitly the name of the branch's directory, unless an
357
explicit nickname is set. That is, an explicit nickname always
358
overrides the implicit one.
360
t = get_transport(self.get_url())
361
branch = self.make_branch('bzr.dev')
362
# The nick will be 'bzr.dev', because there is no explicit nick set.
363
self.assertEqual(branch.nick, 'bzr.dev')
364
# Move the branch to a different directory, 'bzr.ab'. Now that branch
365
# will report its nick as 'bzr.ab'.
366
t.move('bzr.dev', 'bzr.ab')
367
branch = Branch.open(self.get_url('bzr.ab'))
368
self.assertEqual(branch.nick, 'bzr.ab')
369
# Set the branch nick explicitly. This will ensure there's a branch
370
# config file in the branch.
371
branch.nick = "Aaron's branch"
372
if not isinstance(branch, remote.RemoteBranch):
373
self.failUnless(branch._transport.has("branch.conf"))
374
# Because the nick has been set explicitly, the nick is now always
375
# "Aaron's branch", regardless of directory name.
376
self.assertEqual(branch.nick, "Aaron's branch")
377
t.move('bzr.ab', 'integration')
378
branch = Branch.open(self.get_url('integration'))
379
self.assertEqual(branch.nick, "Aaron's branch")
380
branch.nick = u"\u1234"
381
self.assertEqual(branch.nick, u"\u1234")
383
def test_commit_nicks(self):
384
"""Nicknames are committed to the revision"""
385
wt = self.make_branch_and_tree('bzr.dev')
387
branch.nick = "My happy branch"
388
wt.commit('My commit respect da nick.')
389
committed = branch.repository.get_revision(branch.last_revision())
390
self.assertEqual(committed.properties["branch-nick"],
393
def test_create_open_branch_uses_repository(self):
395
repo = self.make_repository('.', shared=True)
396
except errors.IncompatibleFormat:
398
child_transport = repo.bzrdir.root_transport.clone('child')
399
child_transport.mkdir('.')
400
child_dir = self.bzrdir_format.initialize_on_transport(child_transport)
402
child_branch = self.branch_format.initialize(child_dir)
403
except errors.UninitializableFormat:
404
# branch references are not default init'able.
406
self.assertEqual(repo.bzrdir.root_transport.base,
407
child_branch.repository.bzrdir.root_transport.base)
408
child_branch = branch.Branch.open(self.get_url('child'))
409
self.assertEqual(repo.bzrdir.root_transport.base,
410
child_branch.repository.bzrdir.root_transport.base)
412
def test_format_description(self):
413
tree = self.make_branch_and_tree('tree')
414
text = tree.branch._format.get_format_description()
415
self.failUnless(len(text))
417
def test_get_commit_builder(self):
418
branch = self.make_branch(".")
420
builder = branch.get_commit_builder([])
421
self.assertIsInstance(builder, repository.CommitBuilder)
422
branch.repository.commit_write_group()
425
def test_generate_revision_history(self):
426
"""Create a fake revision history easily."""
427
tree = self.make_branch_and_tree('.')
428
rev1 = tree.commit('foo')
429
orig_history = tree.branch.revision_history()
430
rev2 = tree.commit('bar', allow_pointless=True)
431
tree.branch.generate_revision_history(rev1)
432
self.assertEqual(orig_history, tree.branch.revision_history())
434
def test_generate_revision_history_NULL_REVISION(self):
435
tree = self.make_branch_and_tree('.')
436
rev1 = tree.commit('foo')
437
tree.branch.generate_revision_history(bzrlib.revision.NULL_REVISION)
438
self.assertEqual([], tree.branch.revision_history())
440
def test_create_checkout(self):
441
tree_a = self.make_branch_and_tree('a')
442
branch_a = tree_a.branch
443
checkout_b = branch_a.create_checkout('b')
444
self.assertEqual('null:', checkout_b.last_revision())
445
checkout_b.commit('rev1', rev_id='rev1')
446
self.assertEqual('rev1', branch_a.last_revision())
447
self.assertNotEqual(checkout_b.branch.base, branch_a.base)
449
checkout_c = branch_a.create_checkout('c', lightweight=True)
450
self.assertEqual('rev1', checkout_c.last_revision())
451
checkout_c.commit('rev2', rev_id='rev2')
452
self.assertEqual('rev2', branch_a.last_revision())
453
self.assertEqual(checkout_c.branch.base, branch_a.base)
456
checkout_d = branch_a.create_checkout('d', lightweight=True)
457
self.assertEqual('rev2', checkout_d.last_revision())
459
checkout_e = branch_a.create_checkout('e')
460
self.assertEqual('rev2', checkout_e.last_revision())
462
def test_create_anonymous_lightweight_checkout(self):
463
"""A lightweight checkout from a readonly branch should succeed."""
464
tree_a = self.make_branch_and_tree('a')
465
rev_id = tree_a.commit('put some content in the branch')
466
# open the branch via a readonly transport
467
source_branch = bzrlib.branch.Branch.open(self.get_readonly_url('a'))
468
# sanity check that the test will be valid
469
self.assertRaises((errors.LockError, errors.TransportNotPossible),
470
source_branch.lock_write)
471
checkout = source_branch.create_checkout('c', lightweight=True)
472
self.assertEqual(rev_id, checkout.last_revision())
474
def test_create_anonymous_heavyweight_checkout(self):
475
"""A regular checkout from a readonly branch should succeed."""
476
tree_a = self.make_branch_and_tree('a')
477
rev_id = tree_a.commit('put some content in the branch')
478
# open the branch via a readonly transport
479
source_branch = bzrlib.branch.Branch.open(self.get_readonly_url('a'))
480
# sanity check that the test will be valid
481
self.assertRaises((errors.LockError, errors.TransportNotPossible),
482
source_branch.lock_write)
483
checkout = source_branch.create_checkout('c')
484
self.assertEqual(rev_id, checkout.last_revision())
486
def test_set_revision_history(self):
487
tree = self.make_branch_and_tree('a')
488
tree.commit('a commit', rev_id='rev1')
490
br.set_revision_history(["rev1"])
491
self.assertEquals(br.revision_history(), ["rev1"])
492
br.set_revision_history([])
493
self.assertEquals(br.revision_history(), [])
496
class TestBranchFormat(TestCaseWithBranch):
498
def test_branch_format_network_name(self):
499
br = self.make_branch('.')
501
network_name = format.network_name()
502
self.assertIsInstance(network_name, str)
503
# We want to test that the network_name matches the actual format on
504
# disk. For local branches that means that using network_name as a key
505
# in the registry gives back the same format. For remote branches we
506
# check that the network_name of the RemoteBranchFormat we have locally
507
# matches the actual format present on disk.
508
if isinstance(format, remote.RemoteBranchFormat):
510
real_branch = br._real_branch
511
self.assertEqual(real_branch._format.network_name(), network_name)
513
registry = branch.network_format_registry
514
looked_up_format = registry.get(network_name)
515
self.assertEqual(format.__class__, looked_up_format.__class__)
518
class ChrootedTests(TestCaseWithBranch):
519
"""A support class that provides readonly urls outside the local namespace.
521
This is done by checking if self.transport_server is a MemoryServer. if it
522
is then we are chrooted already, if it is not then an HttpServer is used
527
super(ChrootedTests, self).setUp()
528
if not self.vfs_transport_factory == MemoryServer:
529
self.transport_readonly_server = HttpServer
531
def test_open_containing(self):
532
self.assertRaises(NotBranchError, Branch.open_containing,
533
self.get_readonly_url(''))
534
self.assertRaises(NotBranchError, Branch.open_containing,
535
self.get_readonly_url('g/p/q'))
536
branch = self.make_branch('.')
537
branch, relpath = Branch.open_containing(self.get_readonly_url(''))
538
self.assertEqual('', relpath)
539
branch, relpath = Branch.open_containing(self.get_readonly_url('g/p/q'))
540
self.assertEqual('g/p/q', relpath)
543
class InstrumentedTransaction(object):
546
self.calls.append('finish')
552
class TestDecorator(object):
558
self._calls.append('lr')
560
def lock_write(self):
561
self._calls.append('lw')
564
self._calls.append('ul')
567
def do_with_read(self):
571
def except_with_read(self):
575
def do_with_write(self):
579
def except_with_write(self):
583
class TestDecorators(TestCase):
585
def test_needs_read_lock(self):
586
branch = TestDecorator()
587
self.assertEqual(1, branch.do_with_read())
588
self.assertEqual(['lr', 'ul'], branch._calls)
590
def test_excepts_in_read_lock(self):
591
branch = TestDecorator()
592
self.assertRaises(RuntimeError, branch.except_with_read)
593
self.assertEqual(['lr', 'ul'], branch._calls)
595
def test_needs_write_lock(self):
596
branch = TestDecorator()
597
self.assertEqual(2, branch.do_with_write())
598
self.assertEqual(['lw', 'ul'], branch._calls)
600
def test_excepts_in_write_lock(self):
601
branch = TestDecorator()
602
self.assertRaises(RuntimeError, branch.except_with_write)
603
self.assertEqual(['lw', 'ul'], branch._calls)
606
class TestBranchPushLocations(TestCaseWithBranch):
608
def test_get_push_location_unset(self):
609
self.assertEqual(None, self.get_branch().get_push_location())
611
def test_get_push_location_exact(self):
612
from bzrlib.config import (locations_config_filename,
613
ensure_config_dir_exists)
614
ensure_config_dir_exists()
615
fn = locations_config_filename()
616
open(fn, 'wt').write(("[%s]\n"
617
"push_location=foo\n" %
618
self.get_branch().base[:-1]))
619
self.assertEqual("foo", self.get_branch().get_push_location())
621
def test_set_push_location(self):
622
branch = self.get_branch()
623
branch.set_push_location('foo')
624
self.assertEqual('foo', branch.get_push_location())
627
class TestFormat(TestCaseWithBranch):
628
"""Tests for the format itself."""
630
def test_get_reference(self):
631
"""get_reference on all regular branches should return None."""
632
if not self.branch_format.is_supported():
633
# unsupported formats are not loopback testable
634
# because the default open will not open them and
635
# they may not be initializable.
637
made_branch = self.make_branch('.')
638
self.assertEqual(None,
639
made_branch._format.get_reference(made_branch.bzrdir))
641
def test_set_reference(self):
642
"""set_reference on all regular branches should be callable."""
643
if not self.branch_format.is_supported():
644
# unsupported formats are not loopback testable
645
# because the default open will not open them and
646
# they may not be initializable.
648
this_branch = self.make_branch('this')
649
other_branch = self.make_branch('other')
651
this_branch._format.set_reference(this_branch.bzrdir, other_branch)
652
except NotImplementedError:
656
ref = this_branch._format.get_reference(this_branch.bzrdir)
657
self.assertEqual(ref, other_branch.base)
659
def test_format_initialize_find_open(self):
660
# loopback test to check the current format initializes to itself.
661
if not self.branch_format.is_supported():
662
# unsupported formats are not loopback testable
663
# because the default open will not open them and
664
# they may not be initializable.
666
# supported formats must be able to init and open
667
t = get_transport(self.get_url())
668
readonly_t = get_transport(self.get_readonly_url())
669
made_branch = self.make_branch('.')
670
self.failUnless(isinstance(made_branch, branch.Branch))
672
# find it via bzrdir opening:
673
opened_control = bzrdir.BzrDir.open(readonly_t.base)
674
direct_opened_branch = opened_control.open_branch()
675
self.assertEqual(direct_opened_branch.__class__, made_branch.__class__)
676
self.assertEqual(opened_control, direct_opened_branch.bzrdir)
677
self.failUnless(isinstance(direct_opened_branch._format,
678
self.branch_format.__class__))
680
# find it via Branch.open
681
opened_branch = branch.Branch.open(readonly_t.base)
682
self.failUnless(isinstance(opened_branch, made_branch.__class__))
683
self.assertEqual(made_branch._format.__class__,
684
opened_branch._format.__class__)
685
# if it has a unique id string, can we probe for it ?
687
self.branch_format.get_format_string()
688
except NotImplementedError:
690
self.assertEqual(self.branch_format,
691
opened_control.find_branch_format())
694
class TestBound(TestCaseWithBranch):
696
def test_bind_unbind(self):
697
branch = self.make_branch('1')
698
branch2 = self.make_branch('2')
701
except errors.UpgradeRequired:
702
raise tests.TestNotApplicable('Format does not support binding')
703
self.assertTrue(branch.unbind())
704
self.assertFalse(branch.unbind())
705
self.assertIs(None, branch.get_bound_location())
707
def test_old_bound_location(self):
708
branch = self.make_branch('branch1')
710
self.assertIs(None, branch.get_old_bound_location())
711
except errors.UpgradeRequired:
712
raise tests.TestNotApplicable(
713
'Format does not store old bound locations')
714
branch2 = self.make_branch('branch2')
716
self.assertIs(None, branch.get_old_bound_location())
718
self.assertContainsRe(branch.get_old_bound_location(), '\/branch2\/$')
720
def test_bind_diverged(self):
721
tree_a = self.make_branch_and_tree('tree_a')
722
tree_a.commit('rev1a')
723
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
724
tree_a.commit('rev2a')
725
tree_b.commit('rev2b')
727
tree_b.branch.bind(tree_a.branch)
728
except errors.UpgradeRequired:
729
raise tests.TestNotApplicable('Format does not support binding')
732
class TestStrict(TestCaseWithBranch):
734
def test_strict_history(self):
735
tree1 = self.make_branch_and_tree('tree1')
737
tree1.branch.set_append_revisions_only(True)
738
except errors.UpgradeRequired:
739
raise TestSkipped('Format does not support strict history')
740
tree1.commit('empty commit')
741
tree2 = tree1.bzrdir.sprout('tree2').open_workingtree()
742
tree2.commit('empty commit 2')
743
tree1.pull(tree2.branch)
744
tree1.commit('empty commit 3')
745
tree2.commit('empty commit 4')
746
self.assertRaises(errors.DivergedBranches, tree1.pull, tree2.branch)
747
tree2.merge_from_branch(tree1.branch)
748
tree2.commit('empty commit 5')
749
self.assertRaises(errors.AppendRevisionsOnlyViolation, tree1.pull,
751
tree3 = tree1.bzrdir.sprout('tree3').open_workingtree()
752
tree3.merge_from_branch(tree2.branch)
753
tree3.commit('empty commit 6')
754
tree2.pull(tree3.branch)
17
from bzrlib.selftest import InTempDir
21
class TestAppendRevisions(InTempDir):
22
"""Test appending more than one revision"""
24
from bzrlib.branch import Branch
25
br = Branch(".", init=True)
26
br.append_revision("rev1")
27
self.assertEquals(br.revision_history(), ["rev1",])
28
br.append_revision("rev2", "rev3")
29
self.assertEquals(br.revision_history(), ["rev1", "rev2", "rev3"])