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
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Tests for branch implementations - tests a branch format."""
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34
19
from bzrlib.branch import Branch, needs_read_lock, needs_write_lock
35
from bzrlib.delta import TreeDelta
36
from bzrlib.errors import (FileExists,
39
UninitializableFormat,
42
from bzrlib.osutils import getcwd
43
import bzrlib.revision
44
from bzrlib.symbol_versioning import deprecated_in
45
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
46
from bzrlib.tests.http_server import HttpServer
47
from bzrlib.tests.per_branch import TestCaseWithBranch
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
25
from bzrlib.selftest import TestCase, TestCaseInTempDir
26
from bzrlib.selftest.HTTPTestUtil import TestCaseWithWebserver
48
27
from bzrlib.trace import mutter
49
from bzrlib.transport import get_transport
50
from bzrlib.transport.memory import MemoryServer
51
from bzrlib.upgrade import upgrade
52
from bzrlib.workingtree import WorkingTree
55
class TestTestCaseWithBranch(TestCaseWithBranch):
57
def test_branch_format_matches_bzrdir_branch_format(self):
58
bzrdir_branch_format = self.bzrdir_format.get_branch_format()
59
self.assertIs(self.branch_format.__class__,
60
bzrdir_branch_format.__class__)
62
def test_make_branch_gets_expected_format(self):
63
branch = self.make_branch('.')
64
self.assertIs(self.branch_format.__class__,
65
branch._format.__class__)
68
class TestBranch(TestCaseWithBranch):
70
def test_create_tree_with_merge(self):
71
tree = self.create_tree_with_merge()
73
self.addCleanup(tree.unlock)
74
graph = tree.branch.repository.get_graph()
75
ancestry_graph = graph.get_parent_map(
76
tree.branch.repository.all_revision_ids())
77
self.assertEqual({'rev-1':('null:',),
79
'rev-1.1.1':('rev-1', ),
80
'rev-3':('rev-2', 'rev-1.1.1', ),
83
def test_revision_ids_are_utf8(self):
84
wt = self.make_branch_and_tree('tree')
85
wt.commit('f', rev_id='rev1')
86
wt.commit('f', rev_id='rev2')
87
wt.commit('f', rev_id='rev3')
89
br = self.get_branch()
91
br.set_revision_history(['rev1', 'rev2', 'rev3'])
92
rh = br.revision_history()
93
self.assertEqual(['rev1', 'rev2', 'rev3'], rh)
94
for revision_id in rh:
95
self.assertIsInstance(revision_id, str)
96
last = br.last_revision()
97
self.assertEqual('rev3', last)
98
self.assertIsInstance(last, str)
99
revno, last = br.last_revision_info()
100
self.assertEqual(3, revno)
101
self.assertEqual('rev3', last)
102
self.assertIsInstance(last, str)
28
import bzrlib.transactions as transactions
29
from bzrlib.revision import NULL_REVISION
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.
35
class TestBranch(TestCaseInTempDir):
37
def test_append_revisions(self):
38
"""Test appending more than one revision"""
39
br = Branch.initialize(".")
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"])
104
45
def test_fetch_revisions(self):
105
46
"""Test fetch-revision operation."""
106
wt = self.make_branch_and_tree('b1')
108
self.build_tree_contents([('b1/foo', 'hello')])
109
wt.add(['foo'], ['foo-id'])
110
wt.commit('lala!', rev_id='revision-1', allow_pointless=False)
112
b2 = self.make_branch('b2')
115
rev = b2.repository.get_revision('revision-1')
116
tree = b2.repository.revision_tree('revision-1')
118
self.addCleanup(tree.unlock)
119
self.assertEqual(tree.get_file_text('foo-id'), 'hello')
121
def test_get_revision_delta(self):
122
tree_a = self.make_branch_and_tree('a')
123
self.build_tree(['a/foo'])
124
tree_a.add('foo', 'file1')
125
tree_a.commit('rev1', rev_id='rev1')
126
self.build_tree(['a/vla'])
127
tree_a.add('vla', 'file2')
128
tree_a.commit('rev2', rev_id='rev2')
130
delta = tree_a.branch.get_revision_delta(1)
131
self.assertIsInstance(delta, TreeDelta)
132
self.assertEqual([('foo', 'file1', 'file')], delta.added)
133
delta = tree_a.branch.get_revision_delta(2)
134
self.assertIsInstance(delta, TreeDelta)
135
self.assertEqual([('vla', 'file2', 'file')], delta.added)
137
def get_unbalanced_tree_pair(self):
47
from bzrlib.fetch import Fetcher
50
b1 = Branch.initialize('b1')
51
b2 = Branch.initialize('b2')
52
file(os.sep.join(['b1', 'foo']), 'w').write('hello')
53
b1.add(['foo'], ['foo-id'])
54
b1.working_tree().commit('lala!', rev_id='revision-1', allow_pointless=False)
57
f = Fetcher(from_branch=b1, to_branch=b2)
58
eq = self.assertEquals
60
eq(f.last_revision, 'revision-1')
62
rev = b2.get_revision('revision-1')
63
tree = b2.revision_tree('revision-1')
64
eq(tree.get_file_text('foo-id'), 'hello')
66
def test_revision_tree(self):
67
b1 = Branch.initialize('.')
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)
75
def get_unbalanced_branch_pair(self):
138
76
"""Return two branches, a and b, with one file in a."""
139
tree_a = self.make_branch_and_tree('a')
140
self.build_tree_contents([('a/b', 'b')])
142
tree_a.commit("silly commit", rev_id='A')
144
tree_b = self.make_branch_and_tree('b')
145
return tree_a, tree_b
78
br_a = Branch.initialize("a")
79
file('a/b', 'wb').write('b')
81
commit(br_a, "silly commit", rev_id='A')
83
br_b = Branch.initialize("b")
147
86
def get_balanced_branch_pair(self):
148
87
"""Returns br_a, br_b as with one commit in a, and b has a's stores."""
149
tree_a, tree_b = self.get_unbalanced_tree_pair()
150
tree_b.branch.repository.fetch(tree_a.branch.repository)
151
return tree_a, tree_b
153
def test_clone_partial(self):
88
br_a, br_b = self.get_unbalanced_branch_pair()
89
br_a.push_stores(br_b)
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])
103
if tree.inventory[file_id].kind == "file":
104
tree.get_file(file_id).read()
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")
112
br_c = copy_branch(br_a, 'c', basis_branch=br_b)
113
self.assertEqual(br_a.revision_history(), br_c.revision_history())
115
def test_copy_partial(self):
154
116
"""Copy only part of the history of a branch."""
155
# TODO: RBC 20060208 test with a revision not on revision-history.
156
# what should that behaviour be ? Emailed the list.
157
# First, make a branch with two commits.
158
wt_a = self.make_branch_and_tree('a')
159
self.build_tree(['a/one'])
161
wt_a.commit('commit one', rev_id='1')
117
self.build_tree(['a/', 'a/one'])
118
br_a = Branch.initialize('a')
120
br_a.working_tree().commit('commit one', rev_id='u@d-1')
162
121
self.build_tree(['a/two'])
164
wt_a.commit('commit two', rev_id='2')
165
# Now make a copy of the repository.
166
repo_b = self.make_repository('b')
167
wt_a.branch.repository.copy_content_into(repo_b)
168
# wt_a might be a lightweight checkout, so get a hold of the actual
169
# branch (because you can't do a partial clone of a lightweight
171
branch = wt_a.branch.bzrdir.open_branch()
172
# Then make a branch where the new repository is, but specify a revision
173
# ID. The new branch's history will stop at the specified revision.
174
br_b = branch.clone(repo_b.bzrdir, revision_id='1')
175
self.assertEqual('1', br_b.last_revision())
177
def get_parented_branch(self):
178
wt_a = self.make_branch_and_tree('a')
179
self.build_tree(['a/one'])
181
wt_a.commit('commit one', rev_id='1')
183
branch_b = wt_a.branch.bzrdir.sprout('b', revision_id='1').open_branch()
184
self.assertEqual(wt_a.branch.base, branch_b.get_parent())
187
def test_clone_branch_nickname(self):
188
# test the nick name is preserved always
189
raise TestSkipped('XXX branch cloning is not yet tested.')
191
def test_clone_branch_parent(self):
192
# test the parent is preserved always
193
branch_b = self.get_parented_branch()
194
repo_c = self.make_repository('c')
195
branch_b.repository.copy_content_into(repo_c)
196
branch_c = branch_b.clone(repo_c.bzrdir)
197
self.assertNotEqual(None, branch_c.get_parent())
198
self.assertEqual(branch_b.get_parent(), branch_c.get_parent())
200
# We can also set a specific parent, and it should be honored
201
random_parent = 'http://bazaar-vcs.org/path/to/branch'
202
branch_b.set_parent(random_parent)
203
repo_d = self.make_repository('d')
204
branch_b.repository.copy_content_into(repo_d)
205
branch_d = branch_b.clone(repo_d.bzrdir)
206
self.assertEqual(random_parent, branch_d.get_parent())
208
def test_submit_branch(self):
209
"""Submit location can be queried and set"""
210
branch = self.make_branch('branch')
211
self.assertEqual(branch.get_submit_branch(), None)
212
branch.set_submit_branch('sftp://example.com')
213
self.assertEqual(branch.get_submit_branch(), 'sftp://example.com')
214
branch.set_submit_branch('sftp://example.net')
215
self.assertEqual(branch.get_submit_branch(), 'sftp://example.net')
217
def test_public_branch(self):
218
"""public location can be queried and set"""
219
branch = self.make_branch('branch')
220
self.assertEqual(branch.get_public_branch(), None)
221
branch.set_public_branch('sftp://example.com')
222
self.assertEqual(branch.get_public_branch(), 'sftp://example.com')
223
branch.set_public_branch('sftp://example.net')
224
self.assertEqual(branch.get_public_branch(), 'sftp://example.net')
225
branch.set_public_branch(None)
226
self.assertEqual(branch.get_public_branch(), None)
228
def test_record_initial_ghost(self):
229
"""Branches should support having ghosts."""
230
wt = self.make_branch_and_tree('.')
231
wt.set_parent_ids(['non:existent@rev--ision--0--2'],
232
allow_leftmost_as_ghost=True)
233
self.assertEqual(['non:existent@rev--ision--0--2'],
235
rev_id = wt.commit('commit against a ghost first parent.')
236
rev = wt.branch.repository.get_revision(rev_id)
237
self.assertEqual(rev.parent_ids, ['non:existent@rev--ision--0--2'])
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'))
129
def test_record_initial_ghost_merge(self):
130
"""A pending merge with no revision present is still a merge."""
131
branch = Branch.initialize('.')
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)
238
136
# parent_sha1s is not populated now, WTF. rbc 20051003
239
137
self.assertEqual(len(rev.parent_sha1s), 0)
241
def test_record_two_ghosts(self):
242
"""Recording with all ghosts works."""
243
wt = self.make_branch_and_tree('.')
245
'foo@azkhazan-123123-abcabc',
246
'wibble@fofof--20050401--1928390812',
248
allow_leftmost_as_ghost=True)
249
rev_id = wt.commit("commit from ghost base with one merge")
250
# the revision should have been committed with two parents
251
rev = wt.branch.repository.get_revision(rev_id)
252
self.assertEqual(['foo@azkhazan-123123-abcabc',
253
'wibble@fofof--20050401--1928390812'],
138
self.assertEqual(rev.parent_ids[0], 'non:existent@rev--ision--0--2')
256
140
def test_bad_revision(self):
257
self.assertRaises(errors.InvalidRevisionId,
258
self.get_branch().repository.get_revision,
141
branch = Branch.initialize('.')
142
self.assertRaises(errors.InvalidRevisionId, branch.get_revision, None)
261
144
# TODO 20051003 RBC:
262
# compare the gpg-to-sign info for a commit with a ghost and
145
# compare the gpg-to-sign info for a commit with a ghost and
263
146
# an identical tree without a ghost
264
147
# fetch missing should rewrite the TOC of weaves to list newly available parents.
149
def test_pending_merges(self):
150
"""Tracking pending-merged revisions."""
151
b = Branch.initialize('.')
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(), [])
266
172
def test_sign_existing_revision(self):
267
wt = self.make_branch_and_tree('.')
269
wt.commit("base", allow_pointless=True, rev_id='A')
173
branch = Branch.initialize('.')
174
branch.working_tree().commit("base", allow_pointless=True, rev_id='A')
270
175
from bzrlib.testament import Testament
271
strategy = gpg.LoopbackGPGStrategy(None)
272
branch.repository.lock_write()
273
branch.repository.start_write_group()
274
branch.repository.sign_revision('A', strategy)
275
branch.repository.commit_write_group()
276
branch.repository.unlock()
277
self.assertEqual('-----BEGIN PSEUDO-SIGNED CONTENT-----\n' +
278
Testament.from_revision(branch.repository,
279
'A').as_short_text() +
280
'-----END PSEUDO-SIGNED CONTENT-----\n',
281
branch.repository.get_signature_text('A'))
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())
283
180
def test_store_signature(self):
284
wt = self.make_branch_and_tree('.')
288
branch.repository.start_write_group()
290
branch.repository.store_revision_signature(
291
gpg.LoopbackGPGStrategy(None), 'FOO', 'A')
293
branch.repository.abort_write_group()
296
branch.repository.commit_write_group()
299
# A signature without a revision should not be accessible.
300
self.assertRaises(errors.NoSuchRevision,
301
branch.repository.has_signature_for_revision_id,
303
wt.commit("base", allow_pointless=True, rev_id='A')
304
self.assertEqual('-----BEGIN PSEUDO-SIGNED CONTENT-----\n'
305
'FOO-----END PSEUDO-SIGNED CONTENT-----\n',
306
branch.repository.get_signature_text('A'))
308
def test_branch_keeps_signatures(self):
309
self.thisFailsStrictLockCheck()
310
wt = self.make_branch_and_tree('source')
311
wt.commit('A', allow_pointless=True, rev_id='A')
312
repo = wt.branch.repository
314
repo.start_write_group()
315
repo.sign_revision('A', gpg.LoopbackGPGStrategy(None))
316
repo.commit_write_group()
318
#FIXME: clone should work to urls,
319
# wt.clone should work to disks.
320
self.build_tree(['target/'])
321
d2 = repo.bzrdir.clone(urlutils.local_path_to_url('target'))
322
self.assertEqual(repo.get_signature_text('A'),
323
d2.open_repository().get_signature_text('A'))
325
def test_missing_revisions(self):
326
t1 = self.make_branch_and_tree('b1')
327
rev1 = t1.commit('one')
328
t2 = t1.bzrdir.sprout('b2').open_workingtree()
329
rev2 = t1.commit('two')
330
rev3 = t1.commit('three')
332
self.assertEqual([rev2, rev3],
333
self.applyDeprecated(deprecated_in((1, 6, 0)),
334
t2.branch.missing_revisions, t1.branch))
337
self.applyDeprecated(deprecated_in((1, 6, 0)),
338
t2.branch.missing_revisions, t1.branch, stop_revision=1))
339
self.assertEqual([rev2],
340
self.applyDeprecated(deprecated_in((1, 6, 0)),
341
t2.branch.missing_revisions, t1.branch, stop_revision=2))
342
self.assertEqual([rev2, rev3],
343
self.applyDeprecated(deprecated_in((1, 6, 0)),
344
t2.branch.missing_revisions, t1.branch, stop_revision=3))
346
self.assertRaises(errors.NoSuchRevision,
347
self.applyDeprecated, deprecated_in((1, 6, 0)),
348
t2.branch.missing_revisions, t1.branch, stop_revision=4)
350
rev4 = t2.commit('four')
351
self.assertRaises(errors.DivergedBranches,
352
self.applyDeprecated, deprecated_in((1, 6, 0)),
353
t2.branch.missing_revisions, t1.branch)
181
branch = Branch.initialize('.')
182
branch.store_revision_signature(bzrlib.gpg.LoopbackGPGStrategy(None),
184
self.assertEqual('FOO', branch.revision_store.get('A', 'sig').read())
186
def test__relcontrolfilename(self):
187
branch = Branch.initialize('.')
188
self.assertEqual('.bzr/%25', branch._rel_controlfilename('%'))
190
def test__relcontrolfilename_empty(self):
191
branch = Branch.initialize('.')
192
self.assertEqual('.bzr', branch._rel_controlfilename(''))
355
194
def test_nicks(self):
356
"""Test explicit and implicit branch nicknames.
358
Nicknames are implicitly the name of the branch's directory, unless an
359
explicit nickname is set. That is, an explicit nickname always
360
overrides the implicit one.
362
t = get_transport(self.get_url())
363
branch = self.make_branch('bzr.dev')
364
# The nick will be 'bzr.dev', because there is no explicit nick set.
195
"""Branch nicknames"""
197
branch = Branch.initialize('bzr.dev')
365
198
self.assertEqual(branch.nick, 'bzr.dev')
366
# Move the branch to a different directory, 'bzr.ab'. Now that branch
367
# will report its nick as 'bzr.ab'.
368
t.move('bzr.dev', 'bzr.ab')
369
branch = Branch.open(self.get_url('bzr.ab'))
199
os.rename('bzr.dev', 'bzr.ab')
200
branch = Branch.open('bzr.ab')
370
201
self.assertEqual(branch.nick, 'bzr.ab')
371
# Set the branch nick explicitly. This will ensure there's a branch
372
# config file in the branch.
373
branch.nick = "Aaron's branch"
374
if not isinstance(branch, remote.RemoteBranch):
375
self.failUnless(branch._transport.has("branch.conf"))
376
# Because the nick has been set explicitly, the nick is now always
377
# "Aaron's branch", regardless of directory name.
202
branch.nick = "Aaron's branch"
203
branch.nick = "Aaron's branch"
204
self.failUnless(os.path.exists(branch.controlfilename("branch.conf")))
378
205
self.assertEqual(branch.nick, "Aaron's branch")
379
t.move('bzr.ab', 'integration')
380
branch = Branch.open(self.get_url('integration'))
206
os.rename('bzr.ab', 'integration')
207
branch = Branch.open('integration')
381
208
self.assertEqual(branch.nick, "Aaron's branch")
382
209
branch.nick = u"\u1234"
383
210
self.assertEqual(branch.nick, u"\u1234")
385
212
def test_commit_nicks(self):
386
213
"""Nicknames are committed to the revision"""
387
wt = self.make_branch_and_tree('bzr.dev')
215
branch = Branch.initialize('bzr.dev')
389
216
branch.nick = "My happy branch"
390
wt.commit('My commit respect da nick.')
391
committed = branch.repository.get_revision(branch.last_revision())
392
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"],
393
220
"My happy branch")
395
def test_create_open_branch_uses_repository(self):
397
repo = self.make_repository('.', shared=True)
398
except errors.IncompatibleFormat:
400
child_transport = repo.bzrdir.root_transport.clone('child')
401
child_transport.mkdir('.')
402
child_dir = self.bzrdir_format.initialize_on_transport(child_transport)
404
child_branch = self.branch_format.initialize(child_dir)
405
except errors.UninitializableFormat:
406
# branch references are not default init'able.
408
self.assertEqual(repo.bzrdir.root_transport.base,
409
child_branch.repository.bzrdir.root_transport.base)
410
child_branch = branch.Branch.open(self.get_url('child'))
411
self.assertEqual(repo.bzrdir.root_transport.base,
412
child_branch.repository.bzrdir.root_transport.base)
414
def test_format_description(self):
415
tree = self.make_branch_and_tree('tree')
416
text = tree.branch._format.get_format_description()
417
self.failUnless(len(text))
419
def test_get_commit_builder(self):
420
branch = self.make_branch(".")
422
builder = branch.get_commit_builder([])
423
self.assertIsInstance(builder, repository.CommitBuilder)
424
branch.repository.commit_write_group()
427
def test_generate_revision_history(self):
428
"""Create a fake revision history easily."""
429
tree = self.make_branch_and_tree('.')
430
rev1 = tree.commit('foo')
431
orig_history = tree.branch.revision_history()
432
rev2 = tree.commit('bar', allow_pointless=True)
433
tree.branch.generate_revision_history(rev1)
434
self.assertEqual(orig_history, tree.branch.revision_history())
436
def test_generate_revision_history_NULL_REVISION(self):
437
tree = self.make_branch_and_tree('.')
438
rev1 = tree.commit('foo')
439
tree.branch.generate_revision_history(bzrlib.revision.NULL_REVISION)
440
self.assertEqual([], tree.branch.revision_history())
442
def test_create_checkout(self):
443
tree_a = self.make_branch_and_tree('a')
444
branch_a = tree_a.branch
445
checkout_b = branch_a.create_checkout('b')
446
self.assertEqual('null:', checkout_b.last_revision())
447
checkout_b.commit('rev1', rev_id='rev1')
448
self.assertEqual('rev1', branch_a.last_revision())
449
self.assertNotEqual(checkout_b.branch.base, branch_a.base)
451
checkout_c = branch_a.create_checkout('c', lightweight=True)
452
self.assertEqual('rev1', checkout_c.last_revision())
453
checkout_c.commit('rev2', rev_id='rev2')
454
self.assertEqual('rev2', branch_a.last_revision())
455
self.assertEqual(checkout_c.branch.base, branch_a.base)
458
checkout_d = branch_a.create_checkout('d', lightweight=True)
459
self.assertEqual('rev2', checkout_d.last_revision())
461
checkout_e = branch_a.create_checkout('e')
462
self.assertEqual('rev2', checkout_e.last_revision())
464
def test_create_anonymous_lightweight_checkout(self):
465
"""A lightweight checkout from a readonly branch should succeed."""
466
tree_a = self.make_branch_and_tree('a')
467
rev_id = tree_a.commit('put some content in the branch')
468
# open the branch via a readonly transport
469
source_branch = bzrlib.branch.Branch.open(self.get_readonly_url('a'))
470
# sanity check that the test will be valid
471
self.assertRaises((errors.LockError, errors.TransportNotPossible),
472
source_branch.lock_write)
473
checkout = source_branch.create_checkout('c', lightweight=True)
474
self.assertEqual(rev_id, checkout.last_revision())
476
def test_create_anonymous_heavyweight_checkout(self):
477
"""A regular checkout from a readonly branch should succeed."""
478
tree_a = self.make_branch_and_tree('a')
479
rev_id = tree_a.commit('put some content in the branch')
480
# open the branch via a readonly transport
481
source_branch = bzrlib.branch.Branch.open(self.get_readonly_url('a'))
482
# sanity check that the test will be valid
483
self.assertRaises((errors.LockError, errors.TransportNotPossible),
484
source_branch.lock_write)
485
checkout = source_branch.create_checkout('c')
486
self.assertEqual(rev_id, checkout.last_revision())
488
def test_set_revision_history(self):
489
tree = self.make_branch_and_tree('a')
490
tree.commit('a commit', rev_id='rev1')
492
br.set_revision_history(["rev1"])
493
self.assertEquals(br.revision_history(), ["rev1"])
494
br.set_revision_history([])
495
self.assertEquals(br.revision_history(), [])
498
class TestBranchFormat(TestCaseWithBranch):
500
def test_branch_format_network_name(self):
501
br = self.make_branch('.')
503
network_name = format.network_name()
504
self.assertIsInstance(network_name, str)
505
# We want to test that the network_name matches the actual format on
506
# disk. For local branches that means that using network_name as a key
507
# in the registry gives back the same format. For remote branches we
508
# check that the network_name of the RemoteBranchFormat we have locally
509
# matches the actual format present on disk.
510
if isinstance(format, remote.RemoteBranchFormat):
512
real_branch = br._real_branch
513
self.assertEqual(real_branch._format.network_name(), network_name)
515
registry = branch.network_format_registry
516
looked_up_format = registry.get(network_name)
517
self.assertEqual(format.__class__, looked_up_format.__class__)
520
class ChrootedTests(TestCaseWithBranch):
521
"""A support class that provides readonly urls outside the local namespace.
523
This is done by checking if self.transport_server is a MemoryServer. if it
524
is then we are chrooted already, if it is not then an HttpServer is used
529
super(ChrootedTests, self).setUp()
530
if not self.vfs_transport_factory == MemoryServer:
531
self.transport_readonly_server = HttpServer
223
class TestRemote(TestCaseWithWebserver):
533
225
def test_open_containing(self):
534
226
self.assertRaises(NotBranchError, Branch.open_containing,
535
self.get_readonly_url(''))
227
self.get_remote_url(''))
536
228
self.assertRaises(NotBranchError, Branch.open_containing,
537
self.get_readonly_url('g/p/q'))
538
branch = self.make_branch('.')
539
branch, relpath = Branch.open_containing(self.get_readonly_url(''))
229
self.get_remote_url('g/p/q'))
230
b = Branch.initialize('.')
231
branch, relpath = Branch.open_containing(self.get_remote_url(''))
540
232
self.assertEqual('', relpath)
541
branch, relpath = Branch.open_containing(self.get_readonly_url('g/p/q'))
233
branch, relpath = Branch.open_containing(self.get_remote_url('g/p/q'))
542
234
self.assertEqual('g/p/q', relpath)
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'])
242
# >>> commit(br1, "lala!", rev_id="REVISION-ID-1", verbose=False)
243
# >>> br2 = ScratchBranch()
244
# >>> br2.update_revisions(br1)
246
# Added 1 inventories.
248
# >>> br2.revision_history()
250
# >>> br2.update_revisions(br1)
252
# >>> br1.text_store.total_size() == br2.text_store.total_size()
545
255
class InstrumentedTransaction(object):
605
315
self.assertEqual(['lw', 'ul'], branch._calls)
608
class TestBranchPushLocations(TestCaseWithBranch):
318
class TestBranchTransaction(TestCaseInTempDir):
321
super(TestBranchTransaction, self).setUp()
322
self.branch = Branch.initialize('.')
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))
329
def test__set_new_transaction(self):
330
self.branch._set_transaction(transactions.ReadOnlyTransaction())
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())
338
def test_finish_no_transaction_raises(self):
339
self.assertRaises(errors.LockError, self.branch._finish_transaction)
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)
346
def test_unlock_calls_finish(self):
347
self.branch.lock_read()
348
transaction = InstrumentedTransaction()
349
self.branch._transaction = transaction
351
self.assertEqual(['finish'], transaction.calls)
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))
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))
367
class TestBranchPushLocations(TestCaseInTempDir):
370
super(TestBranchPushLocations, self).setUp()
371
self.branch = Branch.initialize('.')
610
373
def test_get_push_location_unset(self):
611
self.assertEqual(None, self.get_branch().get_push_location())
374
self.assertEqual(None, self.branch.get_push_location())
613
376
def test_get_push_location_exact(self):
614
from bzrlib.config import (locations_config_filename,
615
ensure_config_dir_exists)
616
ensure_config_dir_exists()
617
fn = locations_config_filename()
618
open(fn, 'wt').write(("[%s]\n"
619
"push_location=foo\n" %
620
self.get_branch().base[:-1]))
621
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" %
381
self.assertEqual("foo", self.branch.get_push_location())
623
383
def test_set_push_location(self):
624
branch = self.get_branch()
625
branch.set_push_location('foo')
626
self.assertEqual('foo', branch.get_push_location())
629
class TestChildSubmitFormats(TestCaseWithBranch):
631
def test_get_child_submit_format_default(self):
632
self.assertEqual(None, self.get_branch().get_child_submit_format())
634
def test_get_child_submit_format(self):
635
branch = self.get_branch()
636
branch.get_config().set_user_option('child_submit_format', '10')
637
branch = self.get_branch()
638
self.assertEqual('10', branch.get_child_submit_format())
641
class TestFormat(TestCaseWithBranch):
642
"""Tests for the format itself."""
644
def test_get_reference(self):
645
"""get_reference on all regular branches should return None."""
646
if not self.branch_format.is_supported():
647
# unsupported formats are not loopback testable
648
# because the default open will not open them and
649
# they may not be initializable.
651
made_branch = self.make_branch('.')
652
self.assertEqual(None,
653
made_branch._format.get_reference(made_branch.bzrdir))
655
def test_set_reference(self):
656
"""set_reference on all regular branches should be callable."""
657
if not self.branch_format.is_supported():
658
# unsupported formats are not loopback testable
659
# because the default open will not open them and
660
# they may not be initializable.
662
this_branch = self.make_branch('this')
663
other_branch = self.make_branch('other')
665
this_branch._format.set_reference(this_branch.bzrdir, other_branch)
666
except NotImplementedError:
670
ref = this_branch._format.get_reference(this_branch.bzrdir)
671
self.assertEqual(ref, other_branch.base)
673
def test_format_initialize_find_open(self):
674
# loopback test to check the current format initializes to itself.
675
if not self.branch_format.is_supported():
676
# unsupported formats are not loopback testable
677
# because the default open will not open them and
678
# they may not be initializable.
680
# supported formats must be able to init and open
681
t = get_transport(self.get_url())
682
readonly_t = get_transport(self.get_readonly_url())
683
made_branch = self.make_branch('.')
684
self.failUnless(isinstance(made_branch, branch.Branch))
686
# find it via bzrdir opening:
687
opened_control = bzrdir.BzrDir.open(readonly_t.base)
688
direct_opened_branch = opened_control.open_branch()
689
self.assertEqual(direct_opened_branch.__class__, made_branch.__class__)
690
self.assertEqual(opened_control, direct_opened_branch.bzrdir)
691
self.failUnless(isinstance(direct_opened_branch._format,
692
self.branch_format.__class__))
694
# find it via Branch.open
695
opened_branch = branch.Branch.open(readonly_t.base)
696
self.failUnless(isinstance(opened_branch, made_branch.__class__))
697
self.assertEqual(made_branch._format.__class__,
698
opened_branch._format.__class__)
699
# if it has a unique id string, can we probe for it ?
701
self.branch_format.get_format_string()
702
except NotImplementedError:
704
self.assertEqual(self.branch_format,
705
opened_control.find_branch_format())
708
class TestBound(TestCaseWithBranch):
710
def test_bind_unbind(self):
711
branch = self.make_branch('1')
712
branch2 = self.make_branch('2')
715
except errors.UpgradeRequired:
716
raise tests.TestNotApplicable('Format does not support binding')
717
self.assertTrue(branch.unbind())
718
self.assertFalse(branch.unbind())
719
self.assertIs(None, branch.get_bound_location())
721
def test_old_bound_location(self):
722
branch = self.make_branch('branch1')
724
self.assertIs(None, branch.get_old_bound_location())
725
except errors.UpgradeRequired:
726
raise tests.TestNotApplicable(
727
'Format does not store old bound locations')
728
branch2 = self.make_branch('branch2')
730
self.assertIs(None, branch.get_old_bound_location())
732
self.assertContainsRe(branch.get_old_bound_location(), '\/branch2\/$')
734
def test_bind_diverged(self):
735
tree_a = self.make_branch_and_tree('tree_a')
736
tree_a.commit('rev1a')
737
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
738
tree_a.commit('rev2a')
739
tree_b.commit('rev2b')
741
tree_b.branch.bind(tree_a.branch)
742
except errors.UpgradeRequired:
743
raise tests.TestNotApplicable('Format does not support binding')
746
class TestStrict(TestCaseWithBranch):
748
def test_strict_history(self):
749
tree1 = self.make_branch_and_tree('tree1')
751
tree1.branch.set_append_revisions_only(True)
752
except errors.UpgradeRequired:
753
raise TestSkipped('Format does not support strict history')
754
tree1.commit('empty commit')
755
tree2 = tree1.bzrdir.sprout('tree2').open_workingtree()
756
tree2.commit('empty commit 2')
757
tree1.pull(tree2.branch)
758
tree1.commit('empty commit 3')
759
tree2.commit('empty commit 4')
760
self.assertRaises(errors.DivergedBranches, tree1.pull, tree2.branch)
761
tree2.merge_from_branch(tree1.branch)
762
tree2.commit('empty commit 5')
763
self.assertRaises(errors.AppendRevisionsOnlyViolation, tree1.pull,
765
tree3 = tree1.bzrdir.sprout('tree3').open_workingtree()
766
tree3.merge_from_branch(tree2.branch)
767
tree3.commit('empty commit 6')
768
tree2.pull(tree3.branch)
771
class TestIgnoreFallbacksParameter(TestCaseWithBranch):
773
def make_branch_with_fallback(self):
774
fallback = self.make_branch('fallback')
775
if not fallback._format.supports_stacking():
776
raise tests.TestNotApplicable("format does not support stacking")
777
stacked = self.make_branch('stacked')
778
stacked.set_stacked_on_url(fallback.base)
781
def test_fallbacks_not_opened(self):
782
stacked = self.make_branch_with_fallback()
783
self.get_transport('').rename('fallback', 'moved')
784
reopened = stacked.bzrdir.open_branch(ignore_fallbacks=True)
785
self.assertEqual([], reopened.repository._fallback_repositories)
787
def test_fallbacks_are_opened(self):
788
stacked = self.make_branch_with_fallback()
789
reopened = stacked.bzrdir.open_branch(ignore_fallbacks=False)
790
self.assertLength(1, reopened.repository._fallback_repositories)
793
class TestReferenceLocation(TestCaseWithBranch):
795
def test_reference_parent(self):
796
tree = self.make_branch_and_tree('tree')
797
subtree = self.make_branch_and_tree('tree/subtree')
798
subtree.set_root_id('subtree-id')
800
tree.add_reference(subtree)
801
except bzrlib.errors.UnsupportedOperation:
802
raise tests.TestNotApplicable('Tree cannot hold references.')
803
reference_parent = tree.branch.reference_parent('subtree-id',
805
self.assertEqual(subtree.branch.base, reference_parent.base)
807
def test_reference_parent_accepts_possible_transports(self):
808
tree = self.make_branch_and_tree('tree')
809
subtree = self.make_branch_and_tree('tree/subtree')
810
subtree.set_root_id('subtree-id')
812
tree.add_reference(subtree)
813
except bzrlib.errors.UnsupportedOperation:
814
raise tests.TestNotApplicable('Tree cannot hold references.')
815
reference_parent = tree.branch.reference_parent('subtree-id',
816
'subtree', possible_transports=[subtree.bzrdir.root_transport])
818
def test_get_reference_info(self):
819
branch = self.make_branch('branch')
821
path, loc = branch.get_reference_info('file-id')
822
except bzrlib.errors.UnsupportedOperation:
823
raise tests.TestNotApplicable('Branch cannot hold references.')
824
self.assertIs(None, path)
825
self.assertIs(None, loc)
827
def test_set_reference_info(self):
828
branch = self.make_branch('branch')
830
branch.set_reference_info('file-id', 'path/to/location',
832
except bzrlib.errors.UnsupportedOperation:
833
raise tests.TestNotApplicable('Branch cannot hold references.')
835
def test_set_get_reference_info(self):
836
branch = self.make_branch('branch')
838
branch.set_reference_info('file-id', 'path/to/file',
840
except bzrlib.errors.UnsupportedOperation:
841
raise tests.TestNotApplicable('Branch cannot hold references.')
842
# Create a new instance to ensure storage is permanent
843
branch = Branch.open('branch')
844
tree_path, branch_location = branch.get_reference_info('file-id')
845
self.assertEqual('path/to/location', branch_location)
847
def test_set_null_reference_info(self):
848
branch = self.make_branch('branch')
850
branch.set_reference_info('file-id', 'path/to/file',
852
except bzrlib.errors.UnsupportedOperation:
853
raise tests.TestNotApplicable('Branch cannot hold references.')
854
branch.set_reference_info('file-id', None, None)
855
tree_path, branch_location = branch.get_reference_info('file-id')
856
self.assertIs(None, tree_path)
857
self.assertIs(None, branch_location)
859
def test_set_null_reference_info_when_null(self):
860
branch = self.make_branch('branch')
862
tree_path, branch_location = branch.get_reference_info('file-id')
863
except bzrlib.errors.UnsupportedOperation:
864
raise tests.TestNotApplicable('Branch cannot hold references.')
865
self.assertIs(None, tree_path)
866
self.assertIs(None, branch_location)
867
branch.set_reference_info('file-id', None, None)
869
def test_set_null_requires_two_nones(self):
870
branch = self.make_branch('branch')
872
e = self.assertRaises(ValueError, branch.set_reference_info,
873
'file-id', 'path', None)
874
except bzrlib.errors.UnsupportedOperation:
875
raise tests.TestNotApplicable('Branch cannot hold references.')
876
self.assertEqual('tree_path must be None when branch_location is'
878
e = self.assertRaises(ValueError, branch.set_reference_info,
879
'file-id', None, 'location')
880
self.assertEqual('branch_location must be None when tree_path is'
883
def make_branch_with_reference(self, location, reference_location,
885
branch = self.make_branch(location)
887
branch.set_reference_info(file_id, 'path/to/file',
889
except bzrlib.errors.UnsupportedOperation:
890
raise tests.TestNotApplicable('Branch cannot hold references.')
893
def test_reference_parent_from_reference_info_(self):
894
referenced_branch = self.make_branch('reference_branch')
895
branch = self.make_branch_with_reference('branch',
896
referenced_branch.base)
897
parent = branch.reference_parent('file-id', 'path/to/file')
898
self.assertEqual(parent.base, referenced_branch.base)
900
def test_branch_relative_reference_location(self):
901
branch = self.make_branch('branch')
903
branch.set_reference_info('file-id', 'path/to/file',
904
'../reference_branch')
905
except bzrlib.errors.UnsupportedOperation:
906
raise tests.TestNotApplicable('Branch cannot hold references.')
907
referenced_branch = self.make_branch('reference_branch')
908
parent = branch.reference_parent('file-id', 'path/to/file')
909
self.assertEqual(parent.base, referenced_branch.base)
911
def test_sprout_copies_reference_location(self):
912
branch = self.make_branch_with_reference('branch', '../reference')
913
new_branch = branch.bzrdir.sprout('new-branch').open_branch()
914
self.assertEqual('../reference',
915
new_branch.get_reference_info('file-id')[1])
917
def test_clone_copies_reference_location(self):
918
branch = self.make_branch_with_reference('branch', '../reference')
919
new_branch = branch.bzrdir.clone('new-branch').open_branch()
920
self.assertEqual('../reference',
921
new_branch.get_reference_info('file-id')[1])
923
def test_copied_locations_are_rebased(self):
924
branch = self.make_branch_with_reference('branch', 'reference')
925
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
926
self.assertEqual('../reference',
927
new_branch.get_reference_info('file-id')[1])
929
def test_update_references_retains_old_references(self):
930
branch = self.make_branch_with_reference('branch', 'reference')
931
new_branch = self.make_branch_with_reference(
932
'new_branch', 'reference', 'file-id2')
933
new_branch.update_references(branch)
934
self.assertEqual('reference',
935
branch.get_reference_info('file-id')[1])
937
def test_update_references_retains_known_references(self):
938
branch = self.make_branch_with_reference('branch', 'reference')
939
new_branch = self.make_branch_with_reference(
940
'new_branch', 'reference2')
941
new_branch.update_references(branch)
942
self.assertEqual('reference',
943
branch.get_reference_info('file-id')[1])
945
def test_update_references_skips_known_references(self):
946
branch = self.make_branch_with_reference('branch', 'reference')
947
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
948
new_branch.set_reference_info('file-id', '../foo', '../foo')
949
new_branch.update_references(branch)
950
self.assertEqual('reference',
951
branch.get_reference_info('file-id')[1])
953
def test_pull_updates_references(self):
954
branch = self.make_branch_with_reference('branch', 'reference')
955
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
956
new_branch.set_reference_info('file-id2', '../foo', '../foo')
957
branch.pull(new_branch)
958
self.assertEqual('foo',
959
branch.get_reference_info('file-id2')[1])
961
def test_push_updates_references(self):
962
branch = self.make_branch_with_reference('branch', 'reference')
963
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
964
new_branch.set_reference_info('file-id2', '../foo', '../foo')
965
new_branch.push(branch)
966
self.assertEqual('foo',
967
branch.get_reference_info('file-id2')[1])
969
def test_merge_updates_references(self):
970
branch = self.make_branch_with_reference('branch', 'reference')
971
tree = self.make_branch_and_tree('tree')
973
branch.pull(tree.branch)
974
checkout = branch.create_checkout('checkout', lightweight=True)
975
checkout.commit('bar')
977
self.addCleanup(tree.unlock)
978
merger = merge.Merger.from_revision_ids(None, tree,
979
branch.last_revision(),
981
merger.merge_type = merge.Merge3Merger
983
self.assertEqual('../branch/reference',
984
tree.branch.get_reference_info('file-id')[1])
384
self.branch.set_push_location('foo')
385
self.assertFileEqual("[%s]\n"
386
"push_location = foo" % os.getcwdu(),
387
'.bazaar/branches.conf')
389
# TODO RBC 20051029 test getting a push location from a branch in a
390
# recursive section - that is, it appends the branch name.