1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
1
# (C) 2005, 2006 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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
"""Tests for branch implementations - tests a branch format."""
20
branch as _mod_branch,
34
from bzrlib.symbol_versioning import deprecated_in
35
from bzrlib.tests import (
39
from bzrlib.tests.http_server import HttpServer
40
from bzrlib.transport.memory import MemoryServer
43
class TestTestCaseWithBranch(per_branch.TestCaseWithBranch):
45
def test_branch_format_matches_bzrdir_branch_format(self):
46
bzrdir_branch_format = self.bzrdir_format.get_branch_format()
47
self.assertIs(self.branch_format.__class__,
48
bzrdir_branch_format.__class__)
50
def test_make_branch_gets_expected_format(self):
51
branch = self.make_branch('.')
52
self.assertIs(self.branch_format.__class__,
53
branch._format.__class__)
56
class TestBranch(per_branch.TestCaseWithBranch):
58
def test_create_tree_with_merge(self):
59
tree = self.create_tree_with_merge()
61
self.addCleanup(tree.unlock)
62
graph = tree.branch.repository.get_graph()
63
ancestry_graph = graph.get_parent_map(
64
tree.branch.repository.all_revision_ids())
65
self.assertEqual({'rev-1':('null:',),
67
'rev-1.1.1':('rev-1', ),
68
'rev-3':('rev-2', 'rev-1.1.1', ),
71
def test_revision_ids_are_utf8(self):
72
wt = self.make_branch_and_tree('tree')
73
wt.commit('f', rev_id='rev1')
74
wt.commit('f', rev_id='rev2')
75
wt.commit('f', rev_id='rev3')
22
import bzrlib.branch as branch
23
from bzrlib.branch import Branch, needs_read_lock, needs_write_lock
24
from bzrlib.clone import copy_branch
25
from bzrlib.commit import commit
26
import bzrlib.errors as errors
27
from bzrlib.errors import (NoSuchRevision,
29
UninitializableFormat,
33
from bzrlib.osutils import getcwd
34
from bzrlib.tests import TestCase, TestCaseInTempDir, TestSkipped
35
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
36
from bzrlib.trace import mutter
37
import bzrlib.transactions as transactions
38
from bzrlib.transport import get_transport
39
from bzrlib.revision import NULL_REVISION
41
# TODO: Make a branch using basis branch, and check that it
42
# doesn't request any files that could have been avoided, by
43
# hooking into the Transport.
46
class TestCaseWithBranch(TestCaseInTempDir):
49
super(TestCaseWithBranch, self).setUp()
53
if self.branch is None:
54
self.branch = self.make_branch('.')
57
def make_branch(self, relpath):
59
return self.branch_format.initialize(relpath)
60
except UninitializableFormat:
61
raise TestSkipped("Format %s is not initializable.")
64
class TestBranch(TestCaseWithBranch):
66
def test_append_revisions(self):
67
"""Test appending more than one revision"""
77
68
br = self.get_branch()
79
br.set_revision_history(['rev1', 'rev2', 'rev3'])
80
rh = br.revision_history()
81
self.assertEqual(['rev1', 'rev2', 'rev3'], rh)
82
for revision_id in rh:
83
self.assertIsInstance(revision_id, str)
84
last = br.last_revision()
85
self.assertEqual('rev3', last)
86
self.assertIsInstance(last, str)
87
revno, last = br.last_revision_info()
88
self.assertEqual(3, revno)
89
self.assertEqual('rev3', last)
90
self.assertIsInstance(last, str)
69
br.append_revision("rev1")
70
self.assertEquals(br.revision_history(), ["rev1",])
71
br.append_revision("rev2", "rev3")
72
self.assertEquals(br.revision_history(), ["rev1", "rev2", "rev3"])
92
74
def test_fetch_revisions(self):
93
75
"""Test fetch-revision operation."""
94
wt = self.make_branch_and_tree('b1')
96
self.build_tree_contents([('b1/foo', 'hello')])
97
wt.add(['foo'], ['foo-id'])
98
wt.commit('lala!', rev_id='revision-1', allow_pointless=False)
76
from bzrlib.fetch import Fetcher
79
b1 = self.make_branch('b1')
100
80
b2 = self.make_branch('b2')
103
rev = b2.repository.get_revision('revision-1')
104
tree = b2.repository.revision_tree('revision-1')
106
self.addCleanup(tree.unlock)
107
self.assertEqual(tree.get_file_text('foo-id'), 'hello')
109
def test_get_revision_delta(self):
110
tree_a = self.make_branch_and_tree('a')
111
self.build_tree(['a/foo'])
112
tree_a.add('foo', 'file1')
113
tree_a.commit('rev1', rev_id='rev1')
114
self.build_tree(['a/vla'])
115
tree_a.add('vla', 'file2')
116
tree_a.commit('rev2', rev_id='rev2')
118
delta = tree_a.branch.get_revision_delta(1)
119
self.assertIsInstance(delta, _mod_delta.TreeDelta)
120
self.assertEqual([('foo', 'file1', 'file')], delta.added)
121
delta = tree_a.branch.get_revision_delta(2)
122
self.assertIsInstance(delta, _mod_delta.TreeDelta)
123
self.assertEqual([('vla', 'file2', 'file')], delta.added)
125
def get_unbalanced_tree_pair(self):
81
file('b1/foo', 'w').write('hello')
82
b1.working_tree().add(['foo'], ['foo-id'])
83
b1.working_tree().commit('lala!', rev_id='revision-1', allow_pointless=False)
86
f = Fetcher(from_branch=b1, to_branch=b2)
87
eq = self.assertEquals
89
eq(f.last_revision, 'revision-1')
91
rev = b2.get_revision('revision-1')
92
tree = b2.revision_tree('revision-1')
93
eq(tree.get_file_text('foo-id'), 'hello')
95
def test_revision_tree(self):
96
b1 = self.get_branch()
97
b1.working_tree().commit('lala!', rev_id='revision-1', allow_pointless=True)
98
tree = b1.revision_tree('revision-1')
99
tree = b1.revision_tree(None)
100
self.assertEqual(len(tree.list_files()), 0)
101
tree = b1.revision_tree(NULL_REVISION)
102
self.assertEqual(len(tree.list_files()), 0)
104
def get_unbalanced_branch_pair(self):
126
105
"""Return two branches, a and b, with one file in a."""
127
tree_a = self.make_branch_and_tree('a')
128
self.build_tree_contents([('a/b', 'b')])
130
tree_a.commit("silly commit", rev_id='A')
132
tree_b = self.make_branch_and_tree('b')
133
return tree_a, tree_b
107
br_a = self.make_branch('a')
108
file('a/b', 'wb').write('b')
109
br_a.working_tree().add('b')
110
commit(br_a, "silly commit", rev_id='A')
112
br_b = self.make_branch('b')
135
115
def get_balanced_branch_pair(self):
136
116
"""Returns br_a, br_b as with one commit in a, and b has a's stores."""
137
tree_a, tree_b = self.get_unbalanced_tree_pair()
138
tree_b.branch.repository.fetch(tree_a.branch.repository)
139
return tree_a, tree_b
141
def test_clone_partial(self):
117
br_a, br_b = self.get_unbalanced_branch_pair()
118
br_a.push_stores(br_b)
121
def test_push_stores(self):
122
"""Copy the stores from one branch to another"""
123
br_a, br_b = self.get_unbalanced_branch_pair()
124
# ensure the revision is missing.
125
self.assertRaises(NoSuchRevision, br_b.get_revision,
126
br_a.revision_history()[0])
127
br_a.push_stores(br_b)
128
# check that b now has all the data from a's first commit.
129
rev = br_b.get_revision(br_a.revision_history()[0])
130
tree = br_b.revision_tree(br_a.revision_history()[0])
132
if tree.inventory[file_id].kind == "file":
133
tree.get_file(file_id).read()
136
def test_copy_branch(self):
137
"""Copy the stores from one branch to another"""
138
br_a, br_b = self.get_balanced_branch_pair()
139
commit(br_b, "silly commit")
141
br_c = copy_branch(br_a, 'c', basis_branch=br_b)
142
self.assertEqual(br_a.revision_history(), br_c.revision_history())
144
def test_copy_partial(self):
142
145
"""Copy only part of the history of a branch."""
143
# TODO: RBC 20060208 test with a revision not on revision-history.
144
# what should that behaviour be ? Emailed the list.
145
# First, make a branch with two commits.
146
wt_a = self.make_branch_and_tree('a')
147
self.build_tree(['a/one'])
149
wt_a.commit('commit one', rev_id='1')
146
self.build_tree(['a/', 'a/one'])
147
br_a = self.make_branch('a')
148
br_a.working_tree().add(['one'])
149
br_a.working_tree().commit('commit one', rev_id='u@d-1')
150
150
self.build_tree(['a/two'])
152
wt_a.commit('commit two', rev_id='2')
153
# Now make a copy of the repository.
154
repo_b = self.make_repository('b')
155
wt_a.branch.repository.copy_content_into(repo_b)
156
# wt_a might be a lightweight checkout, so get a hold of the actual
157
# branch (because you can't do a partial clone of a lightweight
159
branch = wt_a.branch.bzrdir.open_branch()
160
# Then make a branch where the new repository is, but specify a revision
161
# ID. The new branch's history will stop at the specified revision.
162
br_b = branch.clone(repo_b.bzrdir, revision_id='1')
163
self.assertEqual('1', br_b.last_revision())
165
def get_parented_branch(self):
166
wt_a = self.make_branch_and_tree('a')
167
self.build_tree(['a/one'])
169
wt_a.commit('commit one', rev_id='1')
171
branch_b = wt_a.branch.bzrdir.sprout('b', revision_id='1').open_branch()
172
self.assertEqual(wt_a.branch.base, branch_b.get_parent())
175
def test_clone_branch_nickname(self):
176
# test the nick name is preserved always
177
raise tests.TestSkipped('XXX branch cloning is not yet tested.')
179
def test_clone_branch_parent(self):
180
# test the parent is preserved always
181
branch_b = self.get_parented_branch()
182
repo_c = self.make_repository('c')
183
branch_b.repository.copy_content_into(repo_c)
184
branch_c = branch_b.clone(repo_c.bzrdir)
185
self.assertNotEqual(None, branch_c.get_parent())
186
self.assertEqual(branch_b.get_parent(), branch_c.get_parent())
188
# We can also set a specific parent, and it should be honored
189
random_parent = 'http://bazaar-vcs.org/path/to/branch'
190
branch_b.set_parent(random_parent)
191
repo_d = self.make_repository('d')
192
branch_b.repository.copy_content_into(repo_d)
193
branch_d = branch_b.clone(repo_d.bzrdir)
194
self.assertEqual(random_parent, branch_d.get_parent())
196
def test_submit_branch(self):
197
"""Submit location can be queried and set"""
198
branch = self.make_branch('branch')
199
self.assertEqual(branch.get_submit_branch(), None)
200
branch.set_submit_branch('sftp://example.com')
201
self.assertEqual(branch.get_submit_branch(), 'sftp://example.com')
202
branch.set_submit_branch('sftp://example.net')
203
self.assertEqual(branch.get_submit_branch(), 'sftp://example.net')
205
def test_public_branch(self):
206
"""public location can be queried and set"""
207
branch = self.make_branch('branch')
208
self.assertEqual(branch.get_public_branch(), None)
209
branch.set_public_branch('sftp://example.com')
210
self.assertEqual(branch.get_public_branch(), 'sftp://example.com')
211
branch.set_public_branch('sftp://example.net')
212
self.assertEqual(branch.get_public_branch(), 'sftp://example.net')
213
branch.set_public_branch(None)
214
self.assertEqual(branch.get_public_branch(), None)
216
def test_record_initial_ghost(self):
217
"""Branches should support having ghosts."""
218
wt = self.make_branch_and_tree('.')
219
wt.set_parent_ids(['non:existent@rev--ision--0--2'],
220
allow_leftmost_as_ghost=True)
221
self.assertEqual(['non:existent@rev--ision--0--2'],
223
rev_id = wt.commit('commit against a ghost first parent.')
224
rev = wt.branch.repository.get_revision(rev_id)
225
self.assertEqual(rev.parent_ids, ['non:existent@rev--ision--0--2'])
151
br_a.working_tree().add(['two'])
152
br_a.working_tree().commit('commit two', rev_id='u@d-2')
153
br_b = copy_branch(br_a, 'b', revision='u@d-1')
154
self.assertEqual(br_b.last_revision(), 'u@d-1')
155
self.assertTrue(os.path.exists('b/one'))
156
self.assertFalse(os.path.exists('b/two'))
158
def test_record_initial_ghost_merge(self):
159
"""A pending merge with no revision present is still a merge."""
160
branch = self.get_branch()
161
branch.working_tree().add_pending_merge('non:existent@rev--ision--0--2')
162
branch.working_tree().commit('pretend to merge nonexistent-revision', rev_id='first')
163
rev = branch.get_revision(branch.last_revision())
164
self.assertEqual(len(rev.parent_ids), 1)
226
165
# parent_sha1s is not populated now, WTF. rbc 20051003
227
166
self.assertEqual(len(rev.parent_sha1s), 0)
229
def test_record_two_ghosts(self):
230
"""Recording with all ghosts works."""
231
wt = self.make_branch_and_tree('.')
233
'foo@azkhazan-123123-abcabc',
234
'wibble@fofof--20050401--1928390812',
236
allow_leftmost_as_ghost=True)
237
rev_id = wt.commit("commit from ghost base with one merge")
238
# the revision should have been committed with two parents
239
rev = wt.branch.repository.get_revision(rev_id)
240
self.assertEqual(['foo@azkhazan-123123-abcabc',
241
'wibble@fofof--20050401--1928390812'],
167
self.assertEqual(rev.parent_ids[0], 'non:existent@rev--ision--0--2')
244
169
def test_bad_revision(self):
245
self.assertRaises(errors.InvalidRevisionId,
246
self.get_branch().repository.get_revision,
170
self.assertRaises(errors.InvalidRevisionId, self.get_branch().get_revision, None)
249
172
# TODO 20051003 RBC:
250
# compare the gpg-to-sign info for a commit with a ghost and
173
# compare the gpg-to-sign info for a commit with a ghost and
251
174
# an identical tree without a ghost
252
175
# fetch missing should rewrite the TOC of weaves to list newly available parents.
177
def test_pending_merges(self):
178
"""Tracking pending-merged revisions."""
179
b = self.get_branch()
180
wt = b.working_tree()
181
self.assertEquals(wt.pending_merges(), [])
182
wt.add_pending_merge('foo@azkhazan-123123-abcabc')
183
self.assertEquals(wt.pending_merges(), ['foo@azkhazan-123123-abcabc'])
184
wt.add_pending_merge('foo@azkhazan-123123-abcabc')
185
self.assertEquals(wt.pending_merges(), ['foo@azkhazan-123123-abcabc'])
186
wt.add_pending_merge('wibble@fofof--20050401--1928390812')
187
self.assertEquals(wt.pending_merges(),
188
['foo@azkhazan-123123-abcabc',
189
'wibble@fofof--20050401--1928390812'])
190
b.working_tree().commit("commit from base with two merges")
191
rev = b.get_revision(b.revision_history()[0])
192
self.assertEquals(len(rev.parent_ids), 2)
193
self.assertEquals(rev.parent_ids[0],
194
'foo@azkhazan-123123-abcabc')
195
self.assertEquals(rev.parent_ids[1],
196
'wibble@fofof--20050401--1928390812')
197
# list should be cleared when we do a commit
198
self.assertEquals(wt.pending_merges(), [])
254
200
def test_sign_existing_revision(self):
255
wt = self.make_branch_and_tree('.')
257
wt.commit("base", allow_pointless=True, rev_id='A')
201
branch = self.get_branch()
202
branch.working_tree().commit("base", allow_pointless=True, rev_id='A')
258
203
from bzrlib.testament import Testament
259
strategy = gpg.LoopbackGPGStrategy(None)
260
branch.repository.lock_write()
261
branch.repository.start_write_group()
262
branch.repository.sign_revision('A', strategy)
263
branch.repository.commit_write_group()
264
branch.repository.unlock()
265
self.assertEqual('-----BEGIN PSEUDO-SIGNED CONTENT-----\n' +
266
Testament.from_revision(branch.repository,
267
'A').as_short_text() +
268
'-----END PSEUDO-SIGNED CONTENT-----\n',
269
branch.repository.get_signature_text('A'))
204
branch.sign_revision('A', bzrlib.gpg.LoopbackGPGStrategy(None))
205
self.assertEqual(Testament.from_revision(branch, 'A').as_short_text(),
206
branch.revision_store.get('A', 'sig').read())
271
208
def test_store_signature(self):
272
wt = self.make_branch_and_tree('.')
276
branch.repository.start_write_group()
278
branch.repository.store_revision_signature(
279
gpg.LoopbackGPGStrategy(None), 'FOO', 'A')
281
branch.repository.abort_write_group()
284
branch.repository.commit_write_group()
287
# A signature without a revision should not be accessible.
288
self.assertRaises(errors.NoSuchRevision,
289
branch.repository.has_signature_for_revision_id,
291
wt.commit("base", allow_pointless=True, rev_id='A')
292
self.assertEqual('-----BEGIN PSEUDO-SIGNED CONTENT-----\n'
293
'FOO-----END PSEUDO-SIGNED CONTENT-----\n',
294
branch.repository.get_signature_text('A'))
296
def test_branch_keeps_signatures(self):
297
wt = self.make_branch_and_tree('source')
298
wt.commit('A', allow_pointless=True, rev_id='A')
299
repo = wt.branch.repository
301
repo.start_write_group()
302
repo.sign_revision('A', gpg.LoopbackGPGStrategy(None))
303
repo.commit_write_group()
305
#FIXME: clone should work to urls,
306
# wt.clone should work to disks.
307
self.build_tree(['target/'])
308
d2 = repo.bzrdir.clone(urlutils.local_path_to_url('target'))
309
self.assertEqual(repo.get_signature_text('A'),
310
d2.open_repository().get_signature_text('A'))
312
def test_missing_revisions(self):
313
t1 = self.make_branch_and_tree('b1')
314
rev1 = t1.commit('one')
315
t2 = t1.bzrdir.sprout('b2').open_workingtree()
316
rev2 = t1.commit('two')
317
rev3 = t1.commit('three')
319
self.assertEqual([rev2, rev3],
320
self.applyDeprecated(deprecated_in((1, 6, 0)),
321
t2.branch.missing_revisions, t1.branch))
324
self.applyDeprecated(deprecated_in((1, 6, 0)),
325
t2.branch.missing_revisions, t1.branch, stop_revision=1))
326
self.assertEqual([rev2],
327
self.applyDeprecated(deprecated_in((1, 6, 0)),
328
t2.branch.missing_revisions, t1.branch, stop_revision=2))
329
self.assertEqual([rev2, rev3],
330
self.applyDeprecated(deprecated_in((1, 6, 0)),
331
t2.branch.missing_revisions, t1.branch, stop_revision=3))
333
self.assertRaises(errors.NoSuchRevision,
334
self.applyDeprecated, deprecated_in((1, 6, 0)),
335
t2.branch.missing_revisions, t1.branch, stop_revision=4)
337
rev4 = t2.commit('four')
338
self.assertRaises(errors.DivergedBranches,
339
self.applyDeprecated, deprecated_in((1, 6, 0)),
340
t2.branch.missing_revisions, t1.branch)
209
branch = self.get_branch()
210
branch.store_revision_signature(bzrlib.gpg.LoopbackGPGStrategy(None),
212
self.assertEqual('FOO', branch.revision_store.get('A', 'sig').read())
214
def test__relcontrolfilename(self):
215
self.assertEqual('.bzr/%25', self.get_branch()._rel_controlfilename('%'))
217
def test__relcontrolfilename_empty(self):
218
self.assertEqual('.bzr', self.get_branch()._rel_controlfilename(''))
342
220
def test_nicks(self):
343
"""Test explicit and implicit branch nicknames.
345
Nicknames are implicitly the name of the branch's directory, unless an
346
explicit nickname is set. That is, an explicit nickname always
347
overrides the implicit one.
349
t = transport.get_transport(self.get_url())
221
"""Branch nicknames"""
350
223
branch = self.make_branch('bzr.dev')
351
# The nick will be 'bzr.dev', because there is no explicit nick set.
352
224
self.assertEqual(branch.nick, 'bzr.dev')
353
# Move the branch to a different directory, 'bzr.ab'. Now that branch
354
# will report its nick as 'bzr.ab'.
355
t.move('bzr.dev', 'bzr.ab')
356
branch = _mod_branch.Branch.open(self.get_url('bzr.ab'))
225
os.rename('bzr.dev', 'bzr.ab')
226
branch = Branch.open('bzr.ab')
357
227
self.assertEqual(branch.nick, 'bzr.ab')
358
# Set the branch nick explicitly. This will ensure there's a branch
359
# config file in the branch.
360
branch.nick = "Aaron's branch"
361
if not isinstance(branch, remote.RemoteBranch):
362
self.failUnless(branch._transport.has("branch.conf"))
363
# Because the nick has been set explicitly, the nick is now always
364
# "Aaron's branch", regardless of directory name.
228
branch.nick = "Aaron's branch"
229
branch.nick = "Aaron's branch"
230
self.failUnless(os.path.exists(branch.controlfilename("branch.conf")))
365
231
self.assertEqual(branch.nick, "Aaron's branch")
366
t.move('bzr.ab', 'integration')
367
branch = _mod_branch.Branch.open(self.get_url('integration'))
232
os.rename('bzr.ab', 'integration')
233
branch = Branch.open('integration')
368
234
self.assertEqual(branch.nick, "Aaron's branch")
369
235
branch.nick = u"\u1234"
370
236
self.assertEqual(branch.nick, u"\u1234")
372
238
def test_commit_nicks(self):
373
239
"""Nicknames are committed to the revision"""
374
wt = self.make_branch_and_tree('bzr.dev')
241
branch = self.get_branch()
376
242
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"],
243
branch.working_tree().commit('My commit respect da nick.')
244
committed = branch.get_revision(branch.last_revision())
245
self.assertEqual(committed.properties["branch-nick"],
380
246
"My happy branch")
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 = _mod_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_get_commit_builder(self):
407
branch = self.make_branch(".")
409
builder = branch.get_commit_builder([])
410
self.assertIsInstance(builder, repository.CommitBuilder)
411
branch.repository.commit_write_group()
414
def test_generate_revision_history(self):
415
"""Create a fake revision history easily."""
416
tree = self.make_branch_and_tree('.')
417
rev1 = tree.commit('foo')
418
orig_history = tree.branch.revision_history()
419
rev2 = tree.commit('bar', allow_pointless=True)
420
tree.branch.generate_revision_history(rev1)
421
self.assertEqual(orig_history, tree.branch.revision_history())
423
def test_generate_revision_history_NULL_REVISION(self):
424
tree = self.make_branch_and_tree('.')
425
rev1 = tree.commit('foo')
426
tree.branch.generate_revision_history(revision.NULL_REVISION)
427
self.assertEqual([], tree.branch.revision_history())
429
def test_create_checkout(self):
430
tree_a = self.make_branch_and_tree('a')
431
branch_a = tree_a.branch
432
checkout_b = branch_a.create_checkout('b')
433
self.assertEqual('null:', checkout_b.last_revision())
434
checkout_b.commit('rev1', rev_id='rev1')
435
self.assertEqual('rev1', branch_a.last_revision())
436
self.assertNotEqual(checkout_b.branch.base, branch_a.base)
438
checkout_c = branch_a.create_checkout('c', lightweight=True)
439
self.assertEqual('rev1', checkout_c.last_revision())
440
checkout_c.commit('rev2', rev_id='rev2')
441
self.assertEqual('rev2', branch_a.last_revision())
442
self.assertEqual(checkout_c.branch.base, branch_a.base)
444
checkout_d = branch_a.create_checkout('d', lightweight=True)
445
self.assertEqual('rev2', checkout_d.last_revision())
446
checkout_e = branch_a.create_checkout('e')
447
self.assertEqual('rev2', checkout_e.last_revision())
449
def test_create_anonymous_lightweight_checkout(self):
450
"""A lightweight checkout from a readonly branch should succeed."""
451
tree_a = self.make_branch_and_tree('a')
452
rev_id = tree_a.commit('put some content in the branch')
453
# open the branch via a readonly transport
454
source_branch = _mod_branch.Branch.open(self.get_readonly_url('a'))
455
# sanity check that the test will be valid
456
self.assertRaises((errors.LockError, errors.TransportNotPossible),
457
source_branch.lock_write)
458
checkout = source_branch.create_checkout('c', lightweight=True)
459
self.assertEqual(rev_id, checkout.last_revision())
461
def test_create_anonymous_heavyweight_checkout(self):
462
"""A regular checkout from a readonly branch should succeed."""
463
tree_a = self.make_branch_and_tree('a')
464
rev_id = tree_a.commit('put some content in the branch')
465
# open the branch via a readonly transport
466
source_branch = _mod_branch.Branch.open(self.get_readonly_url('a'))
467
# sanity check that the test will be valid
468
self.assertRaises((errors.LockError, errors.TransportNotPossible),
469
source_branch.lock_write)
470
checkout = source_branch.create_checkout('c')
471
self.assertEqual(rev_id, checkout.last_revision())
473
def test_set_revision_history(self):
474
tree = self.make_branch_and_tree('a')
475
tree.commit('a commit', rev_id='rev1')
477
br.set_revision_history(["rev1"])
478
self.assertEquals(br.revision_history(), ["rev1"])
479
br.set_revision_history([])
480
self.assertEquals(br.revision_history(), [])
483
class TestBranchFormat(per_branch.TestCaseWithBranch):
485
def test_branch_format_network_name(self):
486
br = self.make_branch('.')
488
network_name = format.network_name()
489
self.assertIsInstance(network_name, str)
490
# We want to test that the network_name matches the actual format on
491
# disk. For local branches that means that using network_name as a key
492
# in the registry gives back the same format. For remote branches we
493
# check that the network_name of the RemoteBranchFormat we have locally
494
# matches the actual format present on disk.
495
if isinstance(format, remote.RemoteBranchFormat):
497
real_branch = br._real_branch
498
self.assertEqual(real_branch._format.network_name(), network_name)
500
registry = _mod_branch.network_format_registry
501
looked_up_format = registry.get(network_name)
502
self.assertEqual(format.__class__, looked_up_format.__class__)
505
class ChrootedTests(per_branch.TestCaseWithBranch):
506
"""A support class that provides readonly urls outside the local namespace.
508
This is done by checking if self.transport_server is a MemoryServer. if it
509
is then we are chrooted already, if it is not then an HttpServer is used
514
super(ChrootedTests, self).setUp()
515
if not self.vfs_transport_factory == MemoryServer:
516
self.transport_readonly_server = HttpServer
249
class TestRemote(TestCaseWithWebserver):
518
251
def test_open_containing(self):
519
self.assertRaises(errors.NotBranchError,
520
_mod_branch.Branch.open_containing,
521
self.get_readonly_url(''))
522
self.assertRaises(errors.NotBranchError,
523
_mod_branch.Branch.open_containing,
524
self.get_readonly_url('g/p/q'))
525
branch = self.make_branch('.')
526
branch, relpath = _mod_branch.Branch.open_containing(
527
self.get_readonly_url(''))
252
self.assertRaises(NotBranchError, Branch.open_containing,
253
self.get_remote_url(''))
254
self.assertRaises(NotBranchError, Branch.open_containing,
255
self.get_remote_url('g/p/q'))
257
branch = self.branch_format.initialize('.')
258
except UninitializableFormat:
259
raise TestSkipped("Format %s is not initializable.")
260
branch, relpath = Branch.open_containing(self.get_remote_url(''))
528
261
self.assertEqual('', relpath)
529
branch, relpath = _mod_branch.Branch.open_containing(
530
self.get_readonly_url('g/p/q'))
262
branch, relpath = Branch.open_containing(self.get_remote_url('g/p/q'))
531
263
self.assertEqual('g/p/q', relpath)
265
# TODO: rewrite this as a regular unittest, without relying on the displayed output
266
# >>> from bzrlib.commit import commit
267
# >>> bzrlib.trace.silent = True
268
# >>> br1 = ScratchBranch(files=['foo', 'bar'])
269
# >>> br1.working_tree().add('foo')
270
# >>> br1.working_tree().add('bar')
271
# >>> commit(br1, "lala!", rev_id="REVISION-ID-1", verbose=False)
272
# >>> br2 = ScratchBranch()
273
# >>> br2.update_revisions(br1)
275
# Added 1 inventories.
277
# >>> br2.revision_history()
279
# >>> br2.update_revisions(br1)
281
# >>> br1.text_store.total_size() == br2.text_store.total_size()
534
284
class InstrumentedTransaction(object):
667
433
# they may not be initializable.
669
435
# supported formats must be able to init and open
670
t = transport.get_transport(self.get_url())
671
readonly_t = transport.get_transport(self.get_readonly_url())
672
made_branch = self.make_branch('.')
673
self.failUnless(isinstance(made_branch, _mod_branch.Branch))
675
# find it via bzrdir opening:
676
opened_control = bzrdir.BzrDir.open(readonly_t.base)
677
direct_opened_branch = opened_control.open_branch()
678
self.assertEqual(direct_opened_branch.__class__, made_branch.__class__)
679
self.assertEqual(opened_control, direct_opened_branch.bzrdir)
680
self.failUnless(isinstance(direct_opened_branch._format,
681
self.branch_format.__class__))
683
# find it via Branch.open
684
opened_branch = _mod_branch.Branch.open(readonly_t.base)
685
self.failUnless(isinstance(opened_branch, made_branch.__class__))
686
self.assertEqual(made_branch._format.__class__,
687
opened_branch._format.__class__)
688
# if it has a unique id string, can we probe for it ?
690
self.branch_format.get_format_string()
691
except NotImplementedError:
436
t = get_transport('.')
437
made_branch = self.branch_format.initialize(t.base)
438
self.failUnless(isinstance(made_branch, branch.Branch))
693
439
self.assertEqual(self.branch_format,
694
opened_control.find_branch_format())
697
class TestBound(per_branch.TestCaseWithBranch):
699
def test_bind_unbind(self):
700
branch = self.make_branch('1')
701
branch2 = self.make_branch('2')
704
except errors.UpgradeRequired:
705
raise tests.TestNotApplicable('Format does not support binding')
706
self.assertTrue(branch.unbind())
707
self.assertFalse(branch.unbind())
708
self.assertIs(None, branch.get_bound_location())
710
def test_old_bound_location(self):
711
branch = self.make_branch('branch1')
713
self.assertIs(None, branch.get_old_bound_location())
714
except errors.UpgradeRequired:
715
raise tests.TestNotApplicable(
716
'Format does not store old bound locations')
717
branch2 = self.make_branch('branch2')
719
self.assertIs(None, branch.get_old_bound_location())
721
self.assertContainsRe(branch.get_old_bound_location(), '\/branch2\/$')
723
def test_bind_diverged(self):
724
tree_a = self.make_branch_and_tree('tree_a')
725
tree_a.commit('rev1a')
726
tree_b = tree_a.bzrdir.sprout('tree_b').open_workingtree()
727
tree_a.commit('rev2a')
728
tree_b.commit('rev2b')
730
tree_b.branch.bind(tree_a.branch)
731
except errors.UpgradeRequired:
732
raise tests.TestNotApplicable('Format does not support binding')
735
class TestStrict(per_branch.TestCaseWithBranch):
737
def test_strict_history(self):
738
tree1 = self.make_branch_and_tree('tree1')
740
tree1.branch.set_append_revisions_only(True)
741
except errors.UpgradeRequired:
742
raise tests.TestSkipped('Format does not support strict history')
743
tree1.commit('empty commit')
744
tree2 = tree1.bzrdir.sprout('tree2').open_workingtree()
745
tree2.commit('empty commit 2')
746
tree1.pull(tree2.branch)
747
tree1.commit('empty commit 3')
748
tree2.commit('empty commit 4')
749
self.assertRaises(errors.DivergedBranches, tree1.pull, tree2.branch)
750
tree2.merge_from_branch(tree1.branch)
751
tree2.commit('empty commit 5')
752
self.assertRaises(errors.AppendRevisionsOnlyViolation, tree1.pull,
754
tree3 = tree1.bzrdir.sprout('tree3').open_workingtree()
755
tree3.merge_from_branch(tree2.branch)
756
tree3.commit('empty commit 6')
757
tree2.pull(tree3.branch)
760
class TestIgnoreFallbacksParameter(per_branch.TestCaseWithBranch):
762
def make_branch_with_fallback(self):
763
fallback = self.make_branch('fallback')
764
if not fallback._format.supports_stacking():
765
raise tests.TestNotApplicable("format does not support stacking")
766
stacked = self.make_branch('stacked')
767
stacked.set_stacked_on_url(fallback.base)
770
def test_fallbacks_not_opened(self):
771
stacked = self.make_branch_with_fallback()
772
self.get_transport('').rename('fallback', 'moved')
773
reopened = stacked.bzrdir.open_branch(ignore_fallbacks=True)
774
self.assertEqual([], reopened.repository._fallback_repositories)
776
def test_fallbacks_are_opened(self):
777
stacked = self.make_branch_with_fallback()
778
reopened = stacked.bzrdir.open_branch(ignore_fallbacks=False)
779
self.assertLength(1, reopened.repository._fallback_repositories)
782
class TestReferenceLocation(per_branch.TestCaseWithBranch):
784
def test_reference_parent(self):
785
tree = self.make_branch_and_tree('tree')
786
subtree = self.make_branch_and_tree('tree/subtree')
787
subtree.set_root_id('subtree-id')
789
tree.add_reference(subtree)
790
except errors.UnsupportedOperation:
791
raise tests.TestNotApplicable('Tree cannot hold references.')
792
reference_parent = tree.branch.reference_parent('subtree-id',
794
self.assertEqual(subtree.branch.base, reference_parent.base)
796
def test_reference_parent_accepts_possible_transports(self):
797
tree = self.make_branch_and_tree('tree')
798
subtree = self.make_branch_and_tree('tree/subtree')
799
subtree.set_root_id('subtree-id')
801
tree.add_reference(subtree)
802
except errors.UnsupportedOperation:
803
raise tests.TestNotApplicable('Tree cannot hold references.')
804
reference_parent = tree.branch.reference_parent('subtree-id',
805
'subtree', possible_transports=[subtree.bzrdir.root_transport])
807
def test_get_reference_info(self):
808
branch = self.make_branch('branch')
810
path, loc = branch.get_reference_info('file-id')
811
except errors.UnsupportedOperation:
812
raise tests.TestNotApplicable('Branch cannot hold references.')
813
self.assertIs(None, path)
814
self.assertIs(None, loc)
816
def test_set_reference_info(self):
817
branch = self.make_branch('branch')
819
branch.set_reference_info('file-id', 'path/to/location',
821
except errors.UnsupportedOperation:
822
raise tests.TestNotApplicable('Branch cannot hold references.')
824
def test_set_get_reference_info(self):
825
branch = self.make_branch('branch')
827
branch.set_reference_info('file-id', 'path/to/file',
829
except errors.UnsupportedOperation:
830
raise tests.TestNotApplicable('Branch cannot hold references.')
831
# Create a new instance to ensure storage is permanent
832
branch = _mod_branch.Branch.open('branch')
833
tree_path, branch_location = branch.get_reference_info('file-id')
834
self.assertEqual('path/to/location', branch_location)
836
def test_set_null_reference_info(self):
837
branch = self.make_branch('branch')
839
branch.set_reference_info('file-id', 'path/to/file',
841
except errors.UnsupportedOperation:
842
raise tests.TestNotApplicable('Branch cannot hold references.')
843
branch.set_reference_info('file-id', None, None)
844
tree_path, branch_location = branch.get_reference_info('file-id')
845
self.assertIs(None, tree_path)
846
self.assertIs(None, branch_location)
848
def test_set_null_reference_info_when_null(self):
849
branch = self.make_branch('branch')
851
tree_path, branch_location = branch.get_reference_info('file-id')
852
except errors.UnsupportedOperation:
853
raise tests.TestNotApplicable('Branch cannot hold references.')
854
self.assertIs(None, tree_path)
855
self.assertIs(None, branch_location)
856
branch.set_reference_info('file-id', None, None)
858
def test_set_null_requires_two_nones(self):
859
branch = self.make_branch('branch')
861
e = self.assertRaises(ValueError, branch.set_reference_info,
862
'file-id', 'path', None)
863
except errors.UnsupportedOperation:
864
raise tests.TestNotApplicable('Branch cannot hold references.')
865
self.assertEqual('tree_path must be None when branch_location is'
867
e = self.assertRaises(ValueError, branch.set_reference_info,
868
'file-id', None, 'location')
869
self.assertEqual('branch_location must be None when tree_path is'
872
def make_branch_with_reference(self, location, reference_location,
874
branch = self.make_branch(location)
876
branch.set_reference_info(file_id, 'path/to/file',
878
except errors.UnsupportedOperation:
879
raise tests.TestNotApplicable('Branch cannot hold references.')
882
def test_reference_parent_from_reference_info_(self):
883
referenced_branch = self.make_branch('reference_branch')
884
branch = self.make_branch_with_reference('branch',
885
referenced_branch.base)
886
parent = branch.reference_parent('file-id', 'path/to/file')
887
self.assertEqual(parent.base, referenced_branch.base)
889
def test_branch_relative_reference_location(self):
890
branch = self.make_branch('branch')
892
branch.set_reference_info('file-id', 'path/to/file',
893
'../reference_branch')
894
except errors.UnsupportedOperation:
895
raise tests.TestNotApplicable('Branch cannot hold references.')
896
referenced_branch = self.make_branch('reference_branch')
897
parent = branch.reference_parent('file-id', 'path/to/file')
898
self.assertEqual(parent.base, referenced_branch.base)
900
def test_sprout_copies_reference_location(self):
901
branch = self.make_branch_with_reference('branch', '../reference')
902
new_branch = branch.bzrdir.sprout('new-branch').open_branch()
903
self.assertEqual('../reference',
904
new_branch.get_reference_info('file-id')[1])
906
def test_clone_copies_reference_location(self):
907
branch = self.make_branch_with_reference('branch', '../reference')
908
new_branch = branch.bzrdir.clone('new-branch').open_branch()
909
self.assertEqual('../reference',
910
new_branch.get_reference_info('file-id')[1])
912
def test_copied_locations_are_rebased(self):
913
branch = self.make_branch_with_reference('branch', 'reference')
914
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
915
self.assertEqual('../reference',
916
new_branch.get_reference_info('file-id')[1])
918
def test_update_references_retains_old_references(self):
919
branch = self.make_branch_with_reference('branch', 'reference')
920
new_branch = self.make_branch_with_reference(
921
'new_branch', 'reference', 'file-id2')
922
new_branch.update_references(branch)
923
self.assertEqual('reference',
924
branch.get_reference_info('file-id')[1])
926
def test_update_references_retains_known_references(self):
927
branch = self.make_branch_with_reference('branch', 'reference')
928
new_branch = self.make_branch_with_reference(
929
'new_branch', 'reference2')
930
new_branch.update_references(branch)
931
self.assertEqual('reference',
932
branch.get_reference_info('file-id')[1])
934
def test_update_references_skips_known_references(self):
935
branch = self.make_branch_with_reference('branch', 'reference')
936
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
937
new_branch.set_reference_info('file-id', '../foo', '../foo')
938
new_branch.update_references(branch)
939
self.assertEqual('reference',
940
branch.get_reference_info('file-id')[1])
942
def test_pull_updates_references(self):
943
branch = self.make_branch_with_reference('branch', 'reference')
944
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
945
new_branch.set_reference_info('file-id2', '../foo', '../foo')
946
branch.pull(new_branch)
947
self.assertEqual('foo',
948
branch.get_reference_info('file-id2')[1])
950
def test_push_updates_references(self):
951
branch = self.make_branch_with_reference('branch', 'reference')
952
new_branch = branch.bzrdir.sprout('branch/new-branch').open_branch()
953
new_branch.set_reference_info('file-id2', '../foo', '../foo')
954
new_branch.push(branch)
955
self.assertEqual('foo',
956
branch.get_reference_info('file-id2')[1])
958
def test_merge_updates_references(self):
959
branch = self.make_branch_with_reference('branch', 'reference')
960
tree = self.make_branch_and_tree('tree')
962
branch.pull(tree.branch)
963
checkout = branch.create_checkout('checkout', lightweight=True)
964
checkout.commit('bar')
966
self.addCleanup(tree.unlock)
967
merger = merge.Merger.from_revision_ids(None, tree,
968
branch.last_revision(),
970
merger.merge_type = merge.Merge3Merger
972
self.assertEqual('../branch/reference',
973
tree.branch.get_reference_info('file-id')[1])
440
branch.BzrBranchFormat.find_format(t))
441
direct_opened_branch = self.branch_format.open(t)
442
opened_branch = branch.Branch.open(t.base)
443
self.assertEqual(made_branch._branch_format,
444
opened_branch._branch_format)
445
self.assertEqual(direct_opened_branch._branch_format,
446
opened_branch._branch_format)
447
self.failUnless(isinstance(opened_branch, branch.Branch))
449
def test_open_not_branch(self):
450
self.assertRaises(NoSuchFile,
451
self.branch_format.open,