1
# (C) 2005, 2006 Canonical Ltd
1
# Copyright (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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
import bzrlib.bzrdir as bzrdir
24
31
from bzrlib.branch import Branch, needs_read_lock, needs_write_lock
25
from bzrlib.commit import commit
26
import bzrlib.errors as errors
32
from bzrlib.delta import TreeDelta
27
33
from bzrlib.errors import (FileExists,
30
36
UninitializableFormat,
34
39
from bzrlib.osutils import getcwd
40
import bzrlib.revision
35
41
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
36
42
from bzrlib.tests.bzrdir_implementations.test_bzrdir import TestCaseWithBzrDir
43
from bzrlib.tests.HttpServer import HttpServer
37
44
from bzrlib.trace import mutter
38
import bzrlib.transactions as transactions
39
45
from bzrlib.transport import get_transport
40
from bzrlib.transport.http import HttpServer
41
46
from bzrlib.transport.memory import MemoryServer
42
47
from bzrlib.upgrade import upgrade
43
48
from bzrlib.workingtree import WorkingTree
78
83
def test_append_revisions(self):
79
84
"""Test appending more than one revision"""
85
wt = self.make_branch_and_tree('tree')
86
wt.commit('f', rev_id='rev1')
87
wt.commit('f', rev_id='rev2')
88
wt.commit('f', rev_id='rev3')
80
90
br = self.get_branch()
81
92
br.append_revision("rev1")
82
93
self.assertEquals(br.revision_history(), ["rev1",])
83
94
br.append_revision("rev2", "rev3")
84
95
self.assertEquals(br.revision_history(), ["rev1", "rev2", "rev3"])
96
self.assertRaises(errors.ReservedId, br.append_revision, 'current:')
98
def test_revision_ids_are_utf8(self):
99
wt = self.make_branch_and_tree('tree')
100
wt.commit('f', rev_id='rev1')
101
wt.commit('f', rev_id='rev2')
102
wt.commit('f', rev_id='rev3')
104
br = self.get_branch()
106
br.set_revision_history(['rev1', 'rev2', 'rev3'])
107
rh = br.revision_history()
108
self.assertEqual(['rev1', 'rev2', 'rev3'], rh)
109
for revision_id in rh:
110
self.assertIsInstance(revision_id, str)
111
last = br.last_revision()
112
self.assertEqual('rev3', last)
113
self.assertIsInstance(last, str)
114
revno, last = br.last_revision_info()
115
self.assertEqual(3, revno)
116
self.assertEqual('rev3', last)
117
self.assertIsInstance(last, str)
86
119
def test_fetch_revisions(self):
87
120
"""Test fetch-revision operation."""
101
134
tree = b2.repository.revision_tree('revision-1')
102
135
self.assertEqual(tree.get_file_text('foo-id'), 'hello')
137
def test_get_revision_delta(self):
138
tree_a = self.make_branch_and_tree('a')
139
self.build_tree(['a/foo'])
140
tree_a.add('foo', 'file1')
141
tree_a.commit('rev1', rev_id='rev1')
142
self.build_tree(['a/vla'])
143
tree_a.add('vla', 'file2')
144
tree_a.commit('rev2', rev_id='rev2')
146
delta = tree_a.branch.get_revision_delta(1)
147
self.assertIsInstance(delta, TreeDelta)
148
self.assertEqual([('foo', 'file1', 'file')], delta.added)
149
delta = tree_a.branch.get_revision_delta(2)
150
self.assertIsInstance(delta, TreeDelta)
151
self.assertEqual([('vla', 'file2', 'file')], delta.added)
104
153
def get_unbalanced_tree_pair(self):
105
154
"""Return two branches, a and b, with one file in a."""
106
155
get_transport(self.get_url()).mkdir('a')
158
207
repo_b = self.make_repository('b')
159
208
wt_a.bzrdir.open_repository().copy_content_into(repo_b)
160
209
br_b = wt_a.bzrdir.open_branch().sprout(repo_b.bzrdir, revision_id='1')
161
self.assertEqual(br_b.last_revision(), '1')
210
self.assertEqual('1', br_b.last_revision())
212
def get_parented_branch(self):
213
wt_a = self.make_branch_and_tree('a')
214
self.build_tree(['a/one'])
216
wt_a.commit('commit one', rev_id='1')
218
branch_b = wt_a.bzrdir.sprout('b', revision_id='1').open_branch()
219
self.assertEqual(wt_a.branch.base, branch_b.get_parent())
163
222
def test_clone_branch_nickname(self):
164
223
# test the nick name is preserved always
167
226
def test_clone_branch_parent(self):
168
227
# test the parent is preserved always
169
raise TestSkipped('XXX branch cloning is not yet tested..')
228
branch_b = self.get_parented_branch()
229
repo_c = self.make_repository('c')
230
branch_b.repository.copy_content_into(repo_c)
231
branch_c = branch_b.clone(repo_c.bzrdir)
232
self.assertNotEqual(None, branch_c.get_parent())
233
self.assertEqual(branch_b.get_parent(), branch_c.get_parent())
235
# We can also set a specific parent, and it should be honored
236
random_parent = 'http://bazaar-vcs.org/path/to/branch'
237
branch_b.set_parent(random_parent)
238
repo_d = self.make_repository('d')
239
branch_b.repository.copy_content_into(repo_d)
240
branch_d = branch_b.clone(repo_d.bzrdir)
241
self.assertEqual(random_parent, branch_d.get_parent())
243
def test_copy_content_incomplete(self):
244
tree = self.make_branch_and_tree('commit_tree')
245
self.build_tree(['foo'], transport=tree.bzrdir.root_transport)
247
tree.commit('revision 1', rev_id='1')
248
source = self.make_branch_and_tree('source')
249
# this gives us an incomplete repository
250
tree.bzrdir.open_repository().copy_content_into(
251
source.branch.repository)
252
tree.commit('revision 2', rev_id='2', allow_pointless=True)
253
tree.bzrdir.open_branch().copy_content_into(source.branch)
171
256
def test_sprout_branch_nickname(self):
172
257
# test the nick name is reset always
173
258
raise TestSkipped('XXX branch sprouting is not yet tested..')
176
261
source = self.make_branch('source')
177
262
target = source.bzrdir.sprout(self.get_url('target')).open_branch()
178
263
self.assertEqual(source.bzrdir.root_transport.base, target.get_parent())
265
def test_submit_branch(self):
266
"""Submit location can be queried and set"""
267
branch = self.make_branch('branch')
268
self.assertEqual(branch.get_submit_branch(), None)
269
branch.set_submit_branch('sftp://example.com')
270
self.assertEqual(branch.get_submit_branch(), 'sftp://example.com')
271
branch.set_submit_branch('sftp://example.net')
272
self.assertEqual(branch.get_submit_branch(), 'sftp://example.net')
180
def test_record_initial_ghost_merge(self):
181
"""A pending merge with no revision present is still a merge."""
274
def test_record_initial_ghost(self):
275
"""Branches should support having ghosts."""
182
276
wt = self.make_branch_and_tree('.')
184
wt.add_pending_merge('non:existent@rev--ision--0--2')
185
wt.commit('pretend to merge nonexistent-revision', rev_id='first')
186
rev = branch.repository.get_revision(branch.last_revision())
187
self.assertEqual(len(rev.parent_ids), 1)
277
wt.set_parent_ids(['non:existent@rev--ision--0--2'],
278
allow_leftmost_as_ghost=True)
279
rev_id = wt.commit('commit against a ghost first parent.')
280
rev = wt.branch.repository.get_revision(rev_id)
281
self.assertEqual(rev.parent_ids, ['non:existent@rev--ision--0--2'])
188
282
# parent_sha1s is not populated now, WTF. rbc 20051003
189
283
self.assertEqual(len(rev.parent_sha1s), 0)
190
self.assertEqual(rev.parent_ids[0], 'non:existent@rev--ision--0--2')
285
def test_record_two_ghosts(self):
286
"""Recording with all ghosts works."""
287
wt = self.make_branch_and_tree('.')
289
'foo@azkhazan-123123-abcabc',
290
'wibble@fofof--20050401--1928390812',
292
allow_leftmost_as_ghost=True)
293
rev_id = wt.commit("commit from ghost base with one merge")
294
# the revision should have been committed with two parents
295
rev = wt.branch.repository.get_revision(rev_id)
296
self.assertEqual(['foo@azkhazan-123123-abcabc',
297
'wibble@fofof--20050401--1928390812'],
192
300
def test_bad_revision(self):
193
301
self.assertRaises(errors.InvalidRevisionId,
199
307
# an identical tree without a ghost
200
308
# fetch missing should rewrite the TOC of weaves to list newly available parents.
202
def test_pending_merges(self):
203
"""Tracking pending-merged revisions."""
204
wt = self.make_branch_and_tree('.')
206
self.assertEquals(wt.pending_merges(), [])
207
wt.add_pending_merge('foo@azkhazan-123123-abcabc')
208
self.assertEquals(wt.pending_merges(), ['foo@azkhazan-123123-abcabc'])
209
wt.add_pending_merge('foo@azkhazan-123123-abcabc')
210
self.assertEquals(wt.pending_merges(), ['foo@azkhazan-123123-abcabc'])
211
wt.add_pending_merge('wibble@fofof--20050401--1928390812')
212
self.assertEquals(wt.pending_merges(),
213
['foo@azkhazan-123123-abcabc',
214
'wibble@fofof--20050401--1928390812'])
215
wt.commit("commit from base with two merges")
216
rev = b.repository.get_revision(b.revision_history()[0])
217
self.assertEquals(len(rev.parent_ids), 2)
218
self.assertEquals(rev.parent_ids[0],
219
'foo@azkhazan-123123-abcabc')
220
self.assertEquals(rev.parent_ids[1],
221
'wibble@fofof--20050401--1928390812')
222
# list should be cleared when we do a commit
223
self.assertEquals(wt.pending_merges(), [])
225
310
def test_sign_existing_revision(self):
226
311
wt = self.make_branch_and_tree('.')
227
312
branch = wt.branch
228
313
wt.commit("base", allow_pointless=True, rev_id='A')
229
314
from bzrlib.testament import Testament
230
strategy = bzrlib.gpg.LoopbackGPGStrategy(None)
315
strategy = gpg.LoopbackGPGStrategy(None)
231
316
branch.repository.sign_revision('A', strategy)
232
317
self.assertEqual(Testament.from_revision(branch.repository,
233
318
'A').as_short_text(),
282
367
branch.nick = u"\u1234"
283
368
self.assertEqual(branch.nick, u"\u1234")
285
def test_commit_nicks(self):
286
"""Nicknames are committed to the revision"""
287
get_transport(self.get_url()).mkdir('bzr.dev')
288
wt = self.make_branch_and_tree('bzr.dev')
290
branch.nick = "My happy branch"
291
wt.commit('My commit respect da nick.')
292
committed = branch.repository.get_revision(branch.last_revision())
293
self.assertEqual(committed.properties["branch-nick"],
296
370
def test_create_open_branch_uses_repository(self):
298
372
repo = self.make_repository('.', shared=True)
316
390
text = tree.branch._format.get_format_description()
317
391
self.failUnless(len(text))
393
def test_check_branch_report_results(self):
394
"""Checking a branch produces results which can be printed"""
395
branch = self.make_branch('.')
396
result = branch.check()
397
# reports results through logging
398
result.report_results(verbose=True)
399
result.report_results(verbose=False)
401
def test_get_commit_builder(self):
402
self.assertIsInstance(self.make_branch(".").get_commit_builder([]),
403
repository.CommitBuilder)
405
def test_generate_revision_history(self):
406
"""Create a fake revision history easily."""
407
tree = self.make_branch_and_tree('.')
408
rev1 = tree.commit('foo')
409
orig_history = tree.branch.revision_history()
410
rev2 = tree.commit('bar', allow_pointless=True)
411
tree.branch.generate_revision_history(rev1)
412
self.assertEqual(orig_history, tree.branch.revision_history())
414
def test_generate_revision_history_NULL_REVISION(self):
415
tree = self.make_branch_and_tree('.')
416
rev1 = tree.commit('foo')
417
tree.branch.generate_revision_history(bzrlib.revision.NULL_REVISION)
418
self.assertEqual([], tree.branch.revision_history())
420
def test_create_checkout(self):
421
tree_a = self.make_branch_and_tree('a')
422
branch_a = tree_a.branch
423
checkout_b = branch_a.create_checkout('b')
424
self.assertEqual(None, checkout_b.last_revision())
425
checkout_b.commit('rev1', rev_id='rev1')
426
self.assertEqual('rev1', branch_a.last_revision())
427
self.assertNotEqual(checkout_b.branch.base, branch_a.base)
429
checkout_c = branch_a.create_checkout('c', lightweight=True)
430
self.assertEqual('rev1', checkout_c.last_revision())
431
checkout_c.commit('rev2', rev_id='rev2')
432
self.assertEqual('rev2', branch_a.last_revision())
433
self.assertEqual(checkout_c.branch.base, branch_a.base)
436
checkout_d = branch_a.create_checkout('d', lightweight=True)
437
self.assertEqual('rev2', checkout_d.last_revision())
439
checkout_e = branch_a.create_checkout('e')
440
self.assertEqual('rev2', checkout_e.last_revision())
442
def test_create_anonymous_lightweight_checkout(self):
443
"""A lightweight checkout from a readonly branch should succeed."""
444
tree_a = self.make_branch_and_tree('a')
445
rev_id = tree_a.commit('put some content in the branch')
446
source_branch = bzrlib.branch.Branch.open(
447
'readonly+' + tree_a.bzrdir.root_transport.base)
448
# sanity check that the test will be valid
449
self.assertRaises((errors.LockError, errors.TransportNotPossible),
450
source_branch.lock_write)
451
checkout = source_branch.create_checkout('c', lightweight=True)
452
self.assertEqual(rev_id, checkout.last_revision())
454
def test_create_anonymous_heavyweight_checkout(self):
455
"""A regular checkout from a readonly branch should succeed."""
456
tree_a = self.make_branch_and_tree('a')
457
rev_id = tree_a.commit('put some content in the branch')
458
source_branch = bzrlib.branch.Branch.open(
459
'readonly+' + tree_a.bzrdir.root_transport.base)
460
# sanity check that the test will be valid
461
self.assertRaises((errors.LockError, errors.TransportNotPossible),
462
source_branch.lock_write)
463
checkout = source_branch.create_checkout('c')
464
self.assertEqual(rev_id, checkout.last_revision())
320
467
class ChrootedTests(TestCaseWithBranch):
321
468
"""A support class that provides readonly urls outside the local namespace.
341
488
branch, relpath = Branch.open_containing(self.get_readonly_url('g/p/q'))
342
489
self.assertEqual('g/p/q', relpath)
344
# TODO: rewrite this as a regular unittest, without relying on the displayed output
345
# >>> from bzrlib.commit import commit
346
# >>> bzrlib.trace.silent = True
347
# >>> br1 = ScratchBranch(files=['foo', 'bar'])
348
# >>> br1.working_tree().add('foo')
349
# >>> br1.working_tree().add('bar')
350
# >>> commit(br1, "lala!", rev_id="REVISION-ID-1", verbose=False)
351
# >>> br2 = ScratchBranch()
352
# >>> br2.update_revisions(br1)
354
# Added 1 inventories.
356
# >>> br2.revision_history()
358
# >>> br2.update_revisions(br1)
360
# >>> br1.text_store.total_size() == br2.text_store.total_size()
363
492
class InstrumentedTransaction(object):
478
607
self.assertEqual(None, self.get_branch().get_push_location())
480
609
def test_get_push_location_exact(self):
481
from bzrlib.config import (branches_config_filename,
610
from bzrlib.config import (locations_config_filename,
482
611
ensure_config_dir_exists)
483
612
ensure_config_dir_exists()
484
fn = branches_config_filename()
613
fn = locations_config_filename()
485
614
print >> open(fn, 'wt'), ("[%s]\n"
486
615
"push_location=foo" %
487
616
self.get_branch().base[:-1])
488
617
self.assertEqual("foo", self.get_branch().get_push_location())
490
619
def test_set_push_location(self):
491
from bzrlib.config import (branches_config_filename,
492
ensure_config_dir_exists)
493
ensure_config_dir_exists()
494
fn = branches_config_filename()
495
self.get_branch().set_push_location('foo')
496
self.assertFileEqual("[%s]\n"
497
"push_location = foo" % self.get_branch().base[:-1],
500
# TODO RBC 20051029 test getting a push location from a branch in a
501
# recursive section - that is, it appends the branch name.
620
branch = self.get_branch()
621
branch.set_push_location('foo')
622
self.assertEqual('foo', branch.get_push_location())
504
625
class TestFormat(TestCaseWithBranch):
536
657
except NotImplementedError:
538
659
self.assertEqual(self.branch_format,
539
bzrlib.branch.BranchFormat.find_format(opened_control))
660
branch.BranchFormat.find_format(opened_control))
663
class TestBound(TestCaseWithBranch):
665
def test_bind_unbind(self):
666
branch = self.make_branch('1')
667
branch2 = self.make_branch('2')
670
except errors.UpgradeRequired:
671
raise TestSkipped('Format does not support binding')
672
self.assertTrue(branch.unbind())
673
self.assertFalse(branch.unbind())
674
self.assertIs(None, branch.get_bound_location())
676
def test_old_bound_location(self):
677
branch = self.make_branch('branch1')
679
self.assertIs(None, branch.get_old_bound_location())
680
except errors.UpgradeRequired:
681
raise TestSkipped('Format does not store old bound locations')
682
branch2 = self.make_branch('branch2')
684
self.assertIs(None, branch.get_old_bound_location())
686
self.assertContainsRe(branch.get_old_bound_location(), '\/branch2\/$')
689
class TestStrict(TestCaseWithBranch):
691
def test_strict_history(self):
692
tree1 = self.make_branch_and_tree('tree1')
694
tree1.branch.set_append_revisions_only(True)
695
except errors.UpgradeRequired:
696
raise TestSkipped('Format does not support strict history')
697
tree1.commit('empty commit')
698
tree2 = tree1.bzrdir.sprout('tree2').open_workingtree()
699
tree2.commit('empty commit 2')
700
tree1.pull(tree2.branch)
701
tree1.commit('empty commit 3')
702
tree2.commit('empty commit 4')
703
self.assertRaises(errors.DivergedBranches, tree1.pull, tree2.branch)
704
tree2.merge_from_branch(tree1.branch)
705
tree2.commit('empty commit 5')
706
self.assertRaises(errors.AppendRevisionsOnlyViolation, tree1.pull,
708
tree3 = tree1.bzrdir.sprout('tree3').open_workingtree()
709
tree3.merge_from_branch(tree2.branch)
710
tree3.commit('empty commit 6')
711
tree2.pull(tree3.branch)