1
# Copyright (C) 2005, 2006 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."""
31
from bzrlib.branch import Branch, needs_read_lock, needs_write_lock
32
from bzrlib.delta import TreeDelta
33
from bzrlib.errors import (FileExists,
36
UninitializableFormat,
39
from bzrlib.osutils import getcwd
40
import bzrlib.revision
41
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
42
from bzrlib.tests.bzrdir_implementations.test_bzrdir import TestCaseWithBzrDir
43
from bzrlib.tests.HttpServer import HttpServer
44
from bzrlib.trace import mutter
45
from bzrlib.transport import get_transport
46
from bzrlib.transport.memory import MemoryServer
47
from bzrlib.upgrade import upgrade
48
from bzrlib.workingtree import WorkingTree
51
class TestCaseWithBranch(TestCaseWithBzrDir):
54
super(TestCaseWithBranch, self).setUp()
58
if self.branch is None:
59
self.branch = self.make_branch('')
62
def make_branch(self, relpath, format=None):
63
repo = self.make_repository(relpath, format=format)
64
# fixme RBC 20060210 this isnt necessarily a fixable thing,
65
# Skipped is the wrong exception to raise.
67
return self.branch_format.initialize(repo.bzrdir)
68
except errors.UninitializableFormat:
69
raise TestSkipped('Uninitializable branch format')
71
def make_repository(self, relpath, shared=False, format=None):
72
made_control = self.make_bzrdir(relpath, format=format)
73
return made_control.create_repository(shared=shared)
76
class TestBranch(TestCaseWithBranch):
78
def test_append_revisions(self):
79
"""Test appending more than one revision"""
80
wt = self.make_branch_and_tree('tree')
81
wt.commit('f', rev_id='rev1')
82
wt.commit('f', rev_id='rev2')
83
wt.commit('f', rev_id='rev3')
85
br = self.get_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)
87
26
br.append_revision("rev1")
88
27
self.assertEquals(br.revision_history(), ["rev1",])
89
28
br.append_revision("rev2", "rev3")
90
29
self.assertEquals(br.revision_history(), ["rev1", "rev2", "rev3"])
91
self.assertRaises(errors.ReservedId, br.append_revision, 'current:')
93
def test_revision_ids_are_utf8(self):
94
wt = self.make_branch_and_tree('tree')
95
wt.commit('f', rev_id='rev1')
96
wt.commit('f', rev_id='rev2')
97
wt.commit('f', rev_id='rev3')
99
br = self.get_branch()
101
br.set_revision_history(['rev1', 'rev2', 'rev3'])
102
rh = br.revision_history()
103
self.assertEqual(['rev1', 'rev2', 'rev3'], rh)
104
for revision_id in rh:
105
self.assertIsInstance(revision_id, str)
106
last = br.last_revision()
107
self.assertEqual('rev3', last)
108
self.assertIsInstance(last, str)
109
revno, last = br.last_revision_info()
110
self.assertEqual(3, revno)
111
self.assertEqual('rev3', last)
112
self.assertIsInstance(last, str)
114
def test_fetch_revisions(self):
115
"""Test fetch-revision operation."""
116
wt = self.make_branch_and_tree('b1')
118
self.build_tree_contents([('b1/foo', 'hello')])
119
wt.add(['foo'], ['foo-id'])
120
wt.commit('lala!', rev_id='revision-1', allow_pointless=False)
122
b2 = self.make_branch('b2')
123
self.assertEqual((1, []), b2.fetch(b1))
125
rev = b2.repository.get_revision('revision-1')
126
tree = b2.repository.revision_tree('revision-1')
127
self.assertEqual(tree.get_file_text('foo-id'), 'hello')
129
def test_get_revision_delta(self):
130
tree_a = self.make_branch_and_tree('a')
131
self.build_tree(['a/foo'])
132
tree_a.add('foo', 'file1')
133
tree_a.commit('rev1', rev_id='rev1')
134
self.build_tree(['a/vla'])
135
tree_a.add('vla', 'file2')
136
tree_a.commit('rev2', rev_id='rev2')
138
delta = tree_a.branch.get_revision_delta(1)
139
self.assertIsInstance(delta, TreeDelta)
140
self.assertEqual([('foo', 'file1', 'file')], delta.added)
141
delta = tree_a.branch.get_revision_delta(2)
142
self.assertIsInstance(delta, TreeDelta)
143
self.assertEqual([('vla', 'file2', 'file')], delta.added)
145
def get_unbalanced_tree_pair(self):
146
"""Return two branches, a and b, with one file in a."""
147
tree_a = self.make_branch_and_tree('a')
148
self.build_tree_contents([('a/b', 'b')])
150
tree_a.commit("silly commit", rev_id='A')
152
tree_b = self.make_branch_and_tree('b')
153
return tree_a, tree_b
155
def get_balanced_branch_pair(self):
156
"""Returns br_a, br_b as with one commit in a, and b has a's stores."""
157
tree_a, tree_b = self.get_unbalanced_tree_pair()
158
tree_b.branch.repository.fetch(tree_a.branch.repository)
159
return tree_a, tree_b
161
def test_clone_partial(self):
162
"""Copy only part of the history of a branch."""
163
# TODO: RBC 20060208 test with a revision not on revision-history.
164
# what should that behaviour be ? Emailed the list.
165
# First, make a branch with two commits.
166
wt_a = self.make_branch_and_tree('a')
167
self.build_tree(['a/one'])
169
wt_a.commit('commit one', rev_id='1')
170
self.build_tree(['a/two'])
172
wt_a.commit('commit two', rev_id='2')
173
# Now make a copy of the repository.
174
repo_b = self.make_repository('b')
175
wt_a.branch.repository.copy_content_into(repo_b)
176
# wt_a might be a lightweight checkout, so get a hold of the actual
177
# branch (because you can't do a partial clone of a lightweight
179
branch = wt_a.branch.bzrdir.open_branch()
180
# Then make a branch where the new repository is, but specify a revision
181
# ID. The new branch's history will stop at the specified revision.
182
br_b = branch.clone(repo_b.bzrdir, revision_id='1')
183
self.assertEqual('1', br_b.last_revision())
185
def test_sprout_partial(self):
186
# test sprouting with a prefix of the revision-history.
187
# also needs not-on-revision-history behaviour defined.
188
wt_a = self.make_branch_and_tree('a')
189
self.build_tree(['a/one'])
191
wt_a.commit('commit one', rev_id='1')
192
self.build_tree(['a/two'])
194
wt_a.commit('commit two', rev_id='2')
195
repo_b = self.make_repository('b')
196
repo_a = wt_a.branch.repository
197
repo_a.copy_content_into(repo_b)
198
br_b = wt_a.branch.sprout(repo_b.bzrdir, revision_id='1')
199
self.assertEqual('1', br_b.last_revision())
201
def get_parented_branch(self):
202
wt_a = self.make_branch_and_tree('a')
203
self.build_tree(['a/one'])
205
wt_a.commit('commit one', rev_id='1')
207
branch_b = wt_a.bzrdir.sprout('b', revision_id='1').open_branch()
208
self.assertEqual(wt_a.branch.base, branch_b.get_parent())
211
def test_clone_branch_nickname(self):
212
# test the nick name is preserved always
213
raise TestSkipped('XXX branch cloning is not yet tested..')
215
def test_clone_branch_parent(self):
216
# test the parent is preserved always
217
branch_b = self.get_parented_branch()
218
repo_c = self.make_repository('c')
219
branch_b.repository.copy_content_into(repo_c)
220
branch_c = branch_b.clone(repo_c.bzrdir)
221
self.assertNotEqual(None, branch_c.get_parent())
222
self.assertEqual(branch_b.get_parent(), branch_c.get_parent())
224
# We can also set a specific parent, and it should be honored
225
random_parent = 'http://bazaar-vcs.org/path/to/branch'
226
branch_b.set_parent(random_parent)
227
repo_d = self.make_repository('d')
228
branch_b.repository.copy_content_into(repo_d)
229
branch_d = branch_b.clone(repo_d.bzrdir)
230
self.assertEqual(random_parent, branch_d.get_parent())
232
def test_sprout_branch_nickname(self):
233
# test the nick name is reset always
234
raise TestSkipped('XXX branch sprouting is not yet tested..')
236
def test_sprout_branch_parent(self):
237
source = self.make_branch('source')
238
target = source.bzrdir.sprout(self.get_url('target')).open_branch()
239
self.assertEqual(source.bzrdir.root_transport.base, target.get_parent())
241
def test_submit_branch(self):
242
"""Submit location can be queried and set"""
243
branch = self.make_branch('branch')
244
self.assertEqual(branch.get_submit_branch(), None)
245
branch.set_submit_branch('sftp://example.com')
246
self.assertEqual(branch.get_submit_branch(), 'sftp://example.com')
247
branch.set_submit_branch('sftp://example.net')
248
self.assertEqual(branch.get_submit_branch(), 'sftp://example.net')
250
def test_public_branch(self):
251
"""public location can be queried and set"""
252
branch = self.make_branch('branch')
253
self.assertEqual(branch.get_public_branch(), None)
254
branch.set_public_branch('sftp://example.com')
255
self.assertEqual(branch.get_public_branch(), 'sftp://example.com')
256
branch.set_public_branch('sftp://example.net')
257
self.assertEqual(branch.get_public_branch(), 'sftp://example.net')
258
branch.set_public_branch(None)
259
self.assertEqual(branch.get_public_branch(), None)
261
def test_record_initial_ghost(self):
262
"""Branches should support having ghosts."""
263
wt = self.make_branch_and_tree('.')
264
wt.set_parent_ids(['non:existent@rev--ision--0--2'],
265
allow_leftmost_as_ghost=True)
266
rev_id = wt.commit('commit against a ghost first parent.')
267
rev = wt.branch.repository.get_revision(rev_id)
268
self.assertEqual(rev.parent_ids, ['non:existent@rev--ision--0--2'])
269
# parent_sha1s is not populated now, WTF. rbc 20051003
270
self.assertEqual(len(rev.parent_sha1s), 0)
272
def test_record_two_ghosts(self):
273
"""Recording with all ghosts works."""
274
wt = self.make_branch_and_tree('.')
276
'foo@azkhazan-123123-abcabc',
277
'wibble@fofof--20050401--1928390812',
279
allow_leftmost_as_ghost=True)
280
rev_id = wt.commit("commit from ghost base with one merge")
281
# the revision should have been committed with two parents
282
rev = wt.branch.repository.get_revision(rev_id)
283
self.assertEqual(['foo@azkhazan-123123-abcabc',
284
'wibble@fofof--20050401--1928390812'],
287
def test_bad_revision(self):
288
self.assertRaises(errors.InvalidRevisionId,
289
self.get_branch().repository.get_revision,
293
# compare the gpg-to-sign info for a commit with a ghost and
294
# an identical tree without a ghost
295
# fetch missing should rewrite the TOC of weaves to list newly available parents.
297
def test_sign_existing_revision(self):
298
wt = self.make_branch_and_tree('.')
300
wt.commit("base", allow_pointless=True, rev_id='A')
301
from bzrlib.testament import Testament
302
strategy = gpg.LoopbackGPGStrategy(None)
303
branch.repository.sign_revision('A', strategy)
304
self.assertEqual('-----BEGIN PSEUDO-SIGNED CONTENT-----\n' +
305
Testament.from_revision(branch.repository,
306
'A').as_short_text() +
307
'-----END PSEUDO-SIGNED CONTENT-----\n',
308
branch.repository.get_signature_text('A'))
310
def test_store_signature(self):
311
wt = self.make_branch_and_tree('.')
313
branch.repository.store_revision_signature(
314
gpg.LoopbackGPGStrategy(None), 'FOO', 'A')
315
self.assertRaises(errors.NoSuchRevision,
316
branch.repository.has_signature_for_revision_id,
318
wt.commit("base", allow_pointless=True, rev_id='A')
319
self.assertEqual('-----BEGIN PSEUDO-SIGNED CONTENT-----\n'
320
'FOO-----END PSEUDO-SIGNED CONTENT-----\n',
321
branch.repository.get_signature_text('A'))
323
def test_branch_keeps_signatures(self):
324
wt = self.make_branch_and_tree('source')
325
wt.commit('A', allow_pointless=True, rev_id='A')
326
repo = wt.branch.repository
327
repo.sign_revision('A', gpg.LoopbackGPGStrategy(None))
328
#FIXME: clone should work to urls,
329
# wt.clone should work to disks.
330
self.build_tree(['target/'])
331
d2 = repo.bzrdir.clone(urlutils.local_path_to_url('target'))
332
self.assertEqual(repo.get_signature_text('A'),
333
d2.open_repository().get_signature_text('A'))
335
def test_nicks(self):
336
"""Test explicit and implicit branch nicknames.
338
Nicknames are implicitly the name of the branch's directory, unless an
339
explicit nickname is set. That is, an explicit nickname always
340
overrides the implicit one.
342
t = get_transport(self.get_url())
343
branch = self.make_branch('bzr.dev')
344
# The nick will be 'bzr.dev', because there is no explicit nick set.
345
self.assertEqual(branch.nick, 'bzr.dev')
346
# Move the branch to a different directory, 'bzr.ab'. Now that branch
347
# will report its nick as 'bzr.ab'.
348
t.move('bzr.dev', 'bzr.ab')
349
branch = Branch.open(self.get_url('bzr.ab'))
350
self.assertEqual(branch.nick, 'bzr.ab')
351
# Set the branch nick explicitly. This will ensure there's a branch
352
# config file in the branch.
353
branch.nick = "Aaron's branch"
354
branch.nick = "Aaron's branch"
356
controlfilename = branch.control_files.controlfilename
357
except AttributeError:
358
# remote branches don't have control_files
362
t.has(t.relpath(controlfilename("branch.conf"))))
363
# Because the nick has been set explicitly, the nick is now always
364
# "Aaron's branch", regardless of directory name.
365
self.assertEqual(branch.nick, "Aaron's branch")
366
t.move('bzr.ab', 'integration')
367
branch = Branch.open(self.get_url('integration'))
368
self.assertEqual(branch.nick, "Aaron's branch")
369
branch.nick = u"\u1234"
370
self.assertEqual(branch.nick, u"\u1234")
372
def test_commit_nicks(self):
373
"""Nicknames are committed to the revision"""
374
wt = self.make_branch_and_tree('bzr.dev')
376
branch.nick = "My happy branch"
377
wt.commit('My commit respect da nick.')
378
committed = branch.repository.get_revision(branch.last_revision())
379
self.assertEqual(committed.properties["branch-nick"],
382
def test_create_open_branch_uses_repository(self):
384
repo = self.make_repository('.', shared=True)
385
except errors.IncompatibleFormat:
387
child_transport = repo.bzrdir.root_transport.clone('child')
388
child_transport.mkdir('.')
389
child_dir = self.bzrdir_format.initialize_on_transport(child_transport)
391
child_branch = self.branch_format.initialize(child_dir)
392
except errors.UninitializableFormat:
393
# branch references are not default init'able.
395
self.assertEqual(repo.bzrdir.root_transport.base,
396
child_branch.repository.bzrdir.root_transport.base)
397
child_branch = branch.Branch.open(self.get_url('child'))
398
self.assertEqual(repo.bzrdir.root_transport.base,
399
child_branch.repository.bzrdir.root_transport.base)
401
def test_format_description(self):
402
tree = self.make_branch_and_tree('tree')
403
text = tree.branch._format.get_format_description()
404
self.failUnless(len(text))
406
def test_check_branch_report_results(self):
407
"""Checking a branch produces results which can be printed"""
408
branch = self.make_branch('.')
409
result = branch.check()
410
# reports results through logging
411
result.report_results(verbose=True)
412
result.report_results(verbose=False)
414
def test_get_commit_builder(self):
415
self.assertIsInstance(self.make_branch(".").get_commit_builder([]),
416
repository.CommitBuilder)
418
def test_generate_revision_history(self):
419
"""Create a fake revision history easily."""
420
tree = self.make_branch_and_tree('.')
421
rev1 = tree.commit('foo')
422
orig_history = tree.branch.revision_history()
423
rev2 = tree.commit('bar', allow_pointless=True)
424
tree.branch.generate_revision_history(rev1)
425
self.assertEqual(orig_history, tree.branch.revision_history())
427
def test_generate_revision_history_NULL_REVISION(self):
428
tree = self.make_branch_and_tree('.')
429
rev1 = tree.commit('foo')
430
tree.branch.generate_revision_history(bzrlib.revision.NULL_REVISION)
431
self.assertEqual([], tree.branch.revision_history())
433
def test_create_checkout(self):
434
tree_a = self.make_branch_and_tree('a')
435
branch_a = tree_a.branch
436
checkout_b = branch_a.create_checkout('b')
437
self.assertEqual(None, checkout_b.last_revision())
438
checkout_b.commit('rev1', rev_id='rev1')
439
self.assertEqual('rev1', branch_a.last_revision())
440
self.assertNotEqual(checkout_b.branch.base, branch_a.base)
442
checkout_c = branch_a.create_checkout('c', lightweight=True)
443
self.assertEqual('rev1', checkout_c.last_revision())
444
checkout_c.commit('rev2', rev_id='rev2')
445
self.assertEqual('rev2', branch_a.last_revision())
446
self.assertEqual(checkout_c.branch.base, branch_a.base)
449
checkout_d = branch_a.create_checkout('d', lightweight=True)
450
self.assertEqual('rev2', checkout_d.last_revision())
452
checkout_e = branch_a.create_checkout('e')
453
self.assertEqual('rev2', checkout_e.last_revision())
455
def test_create_anonymous_lightweight_checkout(self):
456
"""A lightweight checkout from a readonly branch should succeed."""
457
tree_a = self.make_branch_and_tree('a')
458
rev_id = tree_a.commit('put some content in the branch')
459
# open the branch via a readonly transport
460
source_branch = bzrlib.branch.Branch.open(self.get_readonly_url('a'))
461
# sanity check that the test will be valid
462
self.assertRaises((errors.LockError, errors.TransportNotPossible),
463
source_branch.lock_write)
464
checkout = source_branch.create_checkout('c', lightweight=True)
465
self.assertEqual(rev_id, checkout.last_revision())
467
def test_create_anonymous_heavyweight_checkout(self):
468
"""A regular checkout from a readonly branch should succeed."""
469
tree_a = self.make_branch_and_tree('a')
470
rev_id = tree_a.commit('put some content in the branch')
471
# open the branch via a readonly transport
472
source_branch = bzrlib.branch.Branch.open(self.get_readonly_url('a'))
473
# sanity check that the test will be valid
474
self.assertRaises((errors.LockError, errors.TransportNotPossible),
475
source_branch.lock_write)
476
checkout = source_branch.create_checkout('c')
477
self.assertEqual(rev_id, checkout.last_revision())
479
def test_set_revision_history(self):
480
tree = self.make_branch_and_tree('a')
481
tree.commit('a commit', rev_id='rev1')
483
br.set_revision_history(["rev1"])
484
self.assertEquals(br.revision_history(), ["rev1"])
485
br.set_revision_history([])
486
self.assertEquals(br.revision_history(), [])
489
class ChrootedTests(TestCaseWithBranch):
490
"""A support class that provides readonly urls outside the local namespace.
492
This is done by checking if self.transport_server is a MemoryServer. if it
493
is then we are chrooted already, if it is not then an HttpServer is used
498
super(ChrootedTests, self).setUp()
499
if not self.vfs_transport_factory == MemoryServer:
500
self.transport_readonly_server = HttpServer
502
def test_open_containing(self):
503
self.assertRaises(NotBranchError, Branch.open_containing,
504
self.get_readonly_url(''))
505
self.assertRaises(NotBranchError, Branch.open_containing,
506
self.get_readonly_url('g/p/q'))
507
branch = self.make_branch('.')
508
branch, relpath = Branch.open_containing(self.get_readonly_url(''))
509
self.assertEqual('', relpath)
510
branch, relpath = Branch.open_containing(self.get_readonly_url('g/p/q'))
511
self.assertEqual('g/p/q', relpath)
514
class InstrumentedTransaction(object):
517
self.calls.append('finish')
523
class TestDecorator(object):
529
self._calls.append('lr')
531
def lock_write(self):
532
self._calls.append('lw')
535
self._calls.append('ul')
538
def do_with_read(self):
542
def except_with_read(self):
546
def do_with_write(self):
550
def except_with_write(self):
554
class TestDecorators(TestCase):
556
def test_needs_read_lock(self):
557
branch = TestDecorator()
558
self.assertEqual(1, branch.do_with_read())
559
self.assertEqual(['lr', 'ul'], branch._calls)
561
def test_excepts_in_read_lock(self):
562
branch = TestDecorator()
563
self.assertRaises(RuntimeError, branch.except_with_read)
564
self.assertEqual(['lr', 'ul'], branch._calls)
566
def test_needs_write_lock(self):
567
branch = TestDecorator()
568
self.assertEqual(2, branch.do_with_write())
569
self.assertEqual(['lw', 'ul'], branch._calls)
571
def test_excepts_in_write_lock(self):
572
branch = TestDecorator()
573
self.assertRaises(RuntimeError, branch.except_with_write)
574
self.assertEqual(['lw', 'ul'], branch._calls)
577
class TestBranchPushLocations(TestCaseWithBranch):
579
def test_get_push_location_unset(self):
580
self.assertEqual(None, self.get_branch().get_push_location())
582
def test_get_push_location_exact(self):
583
from bzrlib.config import (locations_config_filename,
584
ensure_config_dir_exists)
585
ensure_config_dir_exists()
586
fn = locations_config_filename()
587
print >> open(fn, 'wt'), ("[%s]\n"
588
"push_location=foo" %
589
self.get_branch().base[:-1])
590
self.assertEqual("foo", self.get_branch().get_push_location())
592
def test_set_push_location(self):
593
branch = self.get_branch()
594
branch.set_push_location('foo')
595
self.assertEqual('foo', branch.get_push_location())
598
class TestFormat(TestCaseWithBranch):
599
"""Tests for the format itself."""
601
def test_format_initialize_find_open(self):
602
# loopback test to check the current format initializes to itself.
603
if not self.branch_format.is_supported():
604
# unsupported formats are not loopback testable
605
# because the default open will not open them and
606
# they may not be initializable.
608
# supported formats must be able to init and open
609
t = get_transport(self.get_url())
610
readonly_t = get_transport(self.get_readonly_url())
611
made_branch = self.make_branch('.')
612
self.failUnless(isinstance(made_branch, branch.Branch))
614
# find it via bzrdir opening:
615
opened_control = bzrdir.BzrDir.open(readonly_t.base)
616
direct_opened_branch = opened_control.open_branch()
617
self.assertEqual(direct_opened_branch.__class__, made_branch.__class__)
618
self.assertEqual(opened_control, direct_opened_branch.bzrdir)
619
self.failUnless(isinstance(direct_opened_branch._format,
620
self.branch_format.__class__))
622
# find it via Branch.open
623
opened_branch = branch.Branch.open(readonly_t.base)
624
self.failUnless(isinstance(opened_branch, made_branch.__class__))
625
self.assertEqual(made_branch._format.__class__,
626
opened_branch._format.__class__)
627
# if it has a unique id string, can we probe for it ?
629
self.branch_format.get_format_string()
630
except NotImplementedError:
632
self.assertEqual(self.branch_format,
633
branch.BranchFormat.find_format(opened_control))
636
class TestBound(TestCaseWithBranch):
638
def test_bind_unbind(self):
639
branch = self.make_branch('1')
640
branch2 = self.make_branch('2')
643
except errors.UpgradeRequired:
644
raise TestSkipped('Format does not support binding')
645
self.assertTrue(branch.unbind())
646
self.assertFalse(branch.unbind())
647
self.assertIs(None, branch.get_bound_location())
649
def test_old_bound_location(self):
650
branch = self.make_branch('branch1')
652
self.assertIs(None, branch.get_old_bound_location())
653
except errors.UpgradeRequired:
654
raise TestSkipped('Format does not store old bound locations')
655
branch2 = self.make_branch('branch2')
657
self.assertIs(None, branch.get_old_bound_location())
659
self.assertContainsRe(branch.get_old_bound_location(), '\/branch2\/$')
662
class TestStrict(TestCaseWithBranch):
664
def test_strict_history(self):
665
tree1 = self.make_branch_and_tree('tree1')
667
tree1.branch.set_append_revisions_only(True)
668
except errors.UpgradeRequired:
669
raise TestSkipped('Format does not support strict history')
670
tree1.commit('empty commit')
671
tree2 = tree1.bzrdir.sprout('tree2').open_workingtree()
672
tree2.commit('empty commit 2')
673
tree1.pull(tree2.branch)
674
tree1.commit('empty commit 3')
675
tree2.commit('empty commit 4')
676
self.assertRaises(errors.DivergedBranches, tree1.pull, tree2.branch)
677
tree2.merge_from_branch(tree1.branch)
678
tree2.commit('empty commit 5')
679
self.assertRaises(errors.AppendRevisionsOnlyViolation, tree1.pull,
681
tree3 = tree1.bzrdir.sprout('tree3').open_workingtree()
682
tree3.merge_from_branch(tree2.branch)
683
tree3.commit('empty commit 6')
684
tree2.pull(tree3.branch)