19
19
from cStringIO import StringIO
24
23
from bzrlib import (
30
revision as _mod_revision,
34
from bzrlib.errors import (NotBranchError, NotVersionedError,
35
UnsupportedOperation, PathsNotVersionedError)
36
from bzrlib.errors import (
38
PathsNotVersionedError,
36
40
from bzrlib.inventory import Inventory
41
from bzrlib.mutabletree import MutableTree
37
42
from bzrlib.osutils import pathjoin, getcwd, has_symlinks
38
from bzrlib.tests import TestSkipped, TestNotApplicable
43
from bzrlib.tests import (
39
48
from bzrlib.tests.per_workingtree import TestCaseWithWorkingTree
40
from bzrlib.trace import mutter
41
from bzrlib.workingtree import (TreeEntry, TreeDirectory, TreeFile, TreeLink,
42
WorkingTree, WorkingTree2)
49
from bzrlib.workingtree import (
43
56
from bzrlib.conflicts import ConflictList, TextConflict, ContentsConflict
46
59
class TestWorkingTree(TestCaseWithWorkingTree):
61
def requireBranchReference(self):
62
test_branch = self.make_branch('test-branch')
64
# if there is a working tree now, this is not supported.
65
test_branch.bzrdir.open_workingtree()
66
raise TestNotApplicable("only on trees that can be separate"
67
" from their branch.")
68
except (errors.NoWorkingTree, errors.NotLocalUrl):
71
def test_branch_builder(self):
72
# Just a smoke test that we get a branch at the specified relpath
73
builder = self.make_branch_builder('foobar')
74
br = branch.Branch.open(self.get_url('foobar'))
48
76
def test_list_files(self):
49
77
tree = self.make_branch_and_tree('.')
50
78
self.build_tree(['dir/', 'file'])
104
132
self.assertEqual(('filename', 'V', 'directory', 'file-id'),
135
def test_get_config_stack(self):
136
# Smoke test that all working trees succeed getting a config
137
wt = self.make_branch_and_tree('.')
138
conf = wt.get_config_stack()
139
self.assertIsInstance(conf, config.Stack)
107
141
def test_open_containing(self):
108
branch = self.make_branch_and_tree('.').branch
109
local_base = urlutils.local_path_from_url(branch.base)
142
local_wt = self.make_branch_and_tree('.')
143
local_url = local_wt.bzrdir.root_transport.base
144
local_base = urlutils.local_path_from_url(local_url)
111
147
# Empty opens '.'
112
148
wt, relpath = WorkingTree.open_containing()
145
181
def test_lock_locks_branch(self):
146
182
tree = self.make_branch_and_tree('.')
183
self.assertEqual(None, tree.branch.peek_lock_mode())
148
185
self.assertEqual('r', tree.branch.peek_lock_mode())
158
195
tree = self.make_branch_and_tree('.')
160
197
self.build_tree(['hello.txt'])
161
file('hello.txt', 'w').write('initial hello')
198
with file('hello.txt', 'w') as f: f.write('initial hello')
163
200
self.assertRaises(PathsNotVersionedError,
164
201
tree.revert, ['hello.txt'])
166
203
tree.commit('create initial hello.txt')
168
205
self.check_file_contents('hello.txt', 'initial hello')
169
file('hello.txt', 'w').write('new hello')
206
with file('hello.txt', 'w') as f: f.write('new hello')
170
207
self.check_file_contents('hello.txt', 'new hello')
172
209
# revert file modified since last revision
180
217
self.check_file_contents('hello.txt.~1~', 'new hello')
182
219
# backup files are numbered
183
file('hello.txt', 'w').write('new hello2')
220
with file('hello.txt', 'w') as f: f.write('new hello2')
184
221
tree.revert(['hello.txt'])
185
222
self.check_file_contents('hello.txt', 'initial hello')
186
223
self.check_file_contents('hello.txt.~1~', 'new hello')
189
226
def test_revert_missing(self):
190
227
# Revert a file that has been deleted since last commit
191
228
tree = self.make_branch_and_tree('.')
192
file('hello.txt', 'w').write('initial hello')
229
with file('hello.txt', 'w') as f: f.write('initial hello')
193
230
tree.add('hello.txt')
194
231
tree.commit('added hello.txt')
195
232
os.unlink('hello.txt')
196
233
tree.remove('hello.txt')
197
234
tree.revert(['hello.txt'])
198
self.failUnlessExists('hello.txt')
235
self.assertPathExists('hello.txt')
200
237
def test_versioned_files_not_unknown(self):
201
238
tree = self.make_branch_and_tree('.')
202
239
self.build_tree(['hello.txt'])
203
240
tree.add('hello.txt')
204
self.assertEquals(list(tree.unknowns()),
241
self.assertEqual(list(tree.unknowns()),
207
244
def test_unknowns(self):
210
247
'hello.txt.~1~'])
211
248
self.build_tree_contents([('.bzrignore', '*.~*\n')])
212
249
tree.add('.bzrignore')
213
self.assertEquals(list(tree.unknowns()),
250
self.assertEqual(list(tree.unknowns()),
216
253
def test_initialize(self):
234
271
wt.commit('create initial state')
236
revid = b.revision_history()[0]
273
revid = b.last_revision()
237
274
self.log('first revision_id is {%s}' % revid)
239
inv = b.repository.get_inventory(revid)
240
self.log('contents of inventory: %r' % inv.entries())
276
tree = b.repository.revision_tree(revid)
277
self.log('contents of tree: %r' % list(tree.iter_entries_by_dir()))
242
self.check_inventory_shape(inv,
243
['dir/', 'dir/sub/', 'dir/sub/file'])
279
self.check_tree_shape(tree, ['dir/', 'dir/sub/', 'dir/sub/file'])
244
280
wt.rename_one('dir', 'newdir')
247
self.check_inventory_shape(wt.inventory,
283
self.check_tree_shape(wt,
248
284
['newdir/', 'newdir/sub/', 'newdir/sub/file'])
250
286
wt.rename_one('newdir/sub', 'newdir/newsub')
252
self.check_inventory_shape(wt.inventory,
253
['newdir/', 'newdir/newsub/',
288
self.check_tree_shape(wt, ['newdir/', 'newdir/newsub/',
254
289
'newdir/newsub/file'])
264
299
wt = self.make_branch_and_tree('.')
265
300
self.build_tree(['foo/',
267
self.assertRaises(NotVersionedError,
302
if not wt._format.supports_versioned_directories:
305
self.assertRaises(NotVersionedError,
271
309
def test_add_missing(self):
272
310
# adding a msising file -> NoSuchFile
295
333
cloned = cloned_dir.open_workingtree()
296
334
self.assertEqual(cloned.get_parent_ids(), wt.get_parent_ids())
336
def test_clone_empty(self):
337
wt = self.make_branch_and_tree('source')
338
cloned_dir = wt.bzrdir.clone('target', revision_id=_mod_revision.NULL_REVISION)
339
cloned = cloned_dir.open_workingtree()
340
self.assertEqual(cloned.get_parent_ids(), wt.get_parent_ids())
298
342
def test_last_revision(self):
299
343
wt = self.make_branch_and_tree('source')
300
344
self.assertEqual([], wt.get_parent_ids())
312
356
wt.set_last_revision('null:')
313
357
wt.commit('A', allow_pointless=True, rev_id='A')
314
358
self.assertEqual(['A'], wt.get_parent_ids())
315
# None is aways in the branch
359
# null: is aways in the branch
316
360
wt.set_last_revision('null:')
317
361
self.assertEqual([], wt.get_parent_ids())
318
362
# and now we can set it to 'A'
319
363
# because some formats mutate the branch to set it on the tree
320
364
# we need to alter the branch to let this pass.
322
wt.branch.set_revision_history(['A', 'B'])
323
except errors.NoSuchRevision, e:
324
self.assertEqual('B', e.revision)
365
if getattr(wt.branch, "_set_revision_history", None) is None:
325
366
raise TestSkipped("Branch format does not permit arbitrary"
368
wt.branch._set_revision_history(['A', 'B'])
327
369
wt.set_last_revision('A')
328
370
self.assertEqual(['A'], wt.get_parent_ids())
329
371
self.assertRaises(errors.ReservedId, wt.set_last_revision, 'A:')
337
379
# that formats where initialising a branch does not initialise a
338
380
# tree - and thus have separable entities - support skewing the
340
branch = self.make_branch('tree')
342
# if there is a working tree now, this is not supported.
343
branch.bzrdir.open_workingtree()
345
except errors.NoWorkingTree:
347
wt = branch.bzrdir.create_workingtree()
382
self.requireBranchReference()
383
wt = self.make_branch_and_tree('tree')
348
384
wt.commit('A', allow_pointless=True, rev_id='A')
349
385
wt.set_last_revision(None)
350
386
self.assertEqual([], wt.get_parent_ids())
390
426
wt.set_parent_ids(['B'])
391
427
tree = wt.basis_tree()
393
self.failUnless(tree.has_filename('bar'))
429
self.assertTrue(tree.has_filename('bar'))
395
431
wt.set_parent_ids(['A'])
396
432
tree = wt.basis_tree()
398
self.failUnless(tree.has_filename('foo'))
434
self.assertTrue(tree.has_filename('foo'))
401
437
def test_clone_tree_revision(self):
428
464
self.assertEqual(['a'], made_tree.get_parent_ids())
466
def test_post_build_tree_hook(self):
468
def track_post_build_tree(tree):
469
calls.append(tree.last_revision())
470
source = self.make_branch_and_tree('source')
471
source.commit('a', rev_id='a', allow_pointless=True)
472
source.commit('b', rev_id='b', allow_pointless=True)
473
self.build_tree(['new/'])
474
made_control = self.bzrdir_format.initialize('new')
475
source.branch.repository.clone(made_control)
476
source.branch.clone(made_control)
477
MutableTree.hooks.install_named_hook("post_build_tree",
478
track_post_build_tree, "Test")
479
made_tree = self.workingtree_format.initialize(made_control,
481
self.assertEqual(['a'], calls)
430
483
def test_update_sets_last_revision(self):
431
484
# working tree formats from the meta-dir format and newer support
432
485
# setting the last revision on a tree independently of that on the
436
489
# that formats where initialising a branch does not initialise a
437
490
# tree - and thus have separable entities - support skewing the
439
main_branch = self.make_branch('tree')
441
# if there is a working tree now, this is not supported.
442
main_branch.bzrdir.open_workingtree()
444
except errors.NoWorkingTree:
446
wt = main_branch.bzrdir.create_workingtree()
492
self.requireBranchReference()
493
wt = self.make_branch_and_tree('tree')
447
494
# create an out of date working tree by making a checkout in this
449
496
self.build_tree(['checkout/', 'tree/file'])
450
497
checkout = bzrdir.BzrDirMetaFormat1().initialize('checkout')
451
branch.BranchReferenceFormat().initialize(checkout,
452
target_branch=main_branch)
498
checkout.set_branch_reference(wt.branch)
453
499
old_tree = self.workingtree_format.initialize(checkout)
454
500
# now commit to 'tree'
456
502
wt.commit('A', rev_id='A')
457
503
# and update old_tree
458
504
self.assertEqual(0, old_tree.update())
459
self.failUnlessExists('checkout/file')
505
self.assertPathExists('checkout/file')
460
506
self.assertEqual(['A'], old_tree.get_parent_ids())
462
508
def test_update_sets_root_id(self):
477
523
wt.commit('A', rev_id='A')
478
524
# and update checkout
479
525
self.assertEqual(0, checkout.update())
480
self.failUnlessExists('checkout/file')
526
self.assertPathExists('checkout/file')
481
527
self.assertEqual(wt.get_root_id(), checkout.get_root_id())
482
528
self.assertNotEqual(None, wt.get_root_id())
504
550
# that formats where initialising a branch does not initialise a
505
551
# tree - and thus have separable entities - support skewing the
507
main_branch = self.make_branch('tree')
509
# if there is a working tree now, this is not supported.
510
main_branch.bzrdir.open_workingtree()
512
except errors.NoWorkingTree:
514
wt = main_branch.bzrdir.create_workingtree()
553
self.requireBranchReference()
554
wt = self.make_branch_and_tree('tree')
515
555
# create an out of date working tree by making a checkout in this
517
557
self.build_tree(['checkout/', 'tree/file'])
518
558
checkout = bzrdir.BzrDirMetaFormat1().initialize('checkout')
519
branch.BranchReferenceFormat().initialize(checkout,
520
target_branch=main_branch)
559
checkout.set_branch_reference(wt.branch)
521
560
old_tree = self.workingtree_format.initialize(checkout)
522
561
# now commit to 'tree'
560
599
self.assertFileEqual('a test\n', 'b1/a')
561
self.failUnlessExists('b1/b.~1~')
562
self.failIfExists('b1/c')
563
self.failIfExists('b1/a.~1~')
564
self.failUnlessExists('b1/d')
600
self.assertPathExists('b1/b.~1~')
601
self.assertPathDoesNotExist('b1/c')
602
self.assertPathDoesNotExist('b1/a.~1~')
603
self.assertPathExists('b1/d')
566
605
def test_update_updates_bound_branch_no_local_commits(self):
567
606
# doing an update in a tree updates the branch its bound to too.
605
644
# which should have pivoted the local tip into a merge
606
645
self.assertEqual([master_tip, 'bar'], tree.get_parent_ids())
607
646
# and the local branch history should match the masters now.
608
self.assertEqual(master_tree.branch.revision_history(),
609
tree.branch.revision_history())
647
self.assertEqual(master_tree.branch.last_revision(),
648
tree.branch.last_revision())
611
650
def test_update_takes_revision_parameter(self):
612
651
wt = self.make_branch_and_tree('wt')
626
665
# FIXME: This doesn't really test that it works; also this is not
627
666
# implementation-independent. mbp 20070226
628
667
tree = self.make_branch_and_tree('master')
668
if not isinstance(tree, InventoryWorkingTree):
669
raise TestNotApplicable("merge-hashes is specific to bzr "
629
671
tree._transport.put_bytes('merge-hashes', 'asdfasdf')
630
672
self.assertRaises(errors.MergeModifiedFormatError, tree.merge_modified)
639
681
tree.add(['somefile'], ['file-id'])
640
682
tree.set_merge_modified(d)
641
683
mm = tree.merge_modified()
642
self.assertEquals(mm, d)
684
self.assertEqual(mm, d)
645
687
mm = tree.merge_modified()
646
self.assertEquals(mm, d)
688
self.assertEqual(mm, d)
648
690
def test_conflicts(self):
649
691
from bzrlib.tests.test_conflicts import example_conflicts
665
707
def make_merge_conflicts(self):
666
708
from bzrlib.merge import merge_inner
667
709
tree = self.make_branch_and_tree('mine')
668
file('mine/bloo', 'wb').write('one')
669
file('mine/blo', 'wb').write('on')
710
with file('mine/bloo', 'wb') as f: f.write('one')
711
with file('mine/blo', 'wb') as f: f.write('on')
670
712
tree.add(['bloo', 'blo'])
671
713
tree.commit("blah", allow_pointless=False)
672
714
base = tree.branch.repository.revision_tree(tree.last_revision())
673
bzrdir.BzrDir.open("mine").sprout("other")
674
file('other/bloo', 'wb').write('two')
715
controldir.ControlDir.open("mine").sprout("other")
716
with file('other/bloo', 'wb') as f: f.write('two')
675
717
othertree = WorkingTree.open('other')
676
718
othertree.commit('blah', allow_pointless=False)
677
file('mine/bloo', 'wb').write('three')
719
with file('mine/bloo', 'wb') as f: f.write('three')
678
720
tree.commit("blah", allow_pointless=False)
679
721
merge_inner(tree.branch, othertree, base, this_tree=tree)
730
772
def test_format_description(self):
731
773
tree = self.make_branch_and_tree('tree')
732
774
text = tree._format.get_format_description()
733
self.failUnless(len(text))
775
self.assertTrue(len(text))
735
777
def test_branch_attribute_is_not_settable(self):
736
778
# the branch attribute is an aspect of the working tree, not a
768
810
self.assertEqual([('', 'directory'), (u'\xe5', 'file')],
769
811
[(path, ie.kind) for path,ie in
770
tree.inventory.iter_entries()])
812
tree.iter_entries_by_dir()])
773
815
osutils.normalized_filename = orig
789
831
def test__write_inventory(self):
790
832
# The private interface _write_inventory is currently used by transform.
791
833
tree = self.make_branch_and_tree('.')
834
if not isinstance(tree, InventoryWorkingTree):
835
raise TestNotApplicable("_write_inventory does not exist on "
836
"non-inventory working trees")
792
837
# if we write write an inventory then do a walkdirs we should get back
793
838
# missing entries, and actual, and unknowns as appropriate.
794
839
self.build_tree(['present', 'unknown'])
916
961
case_sensitive = True
917
962
tree = self.make_branch_and_tree('test')
918
if tree.__class__ == WorkingTree2:
919
raise TestSkipped('WorkingTree2 is not supported')
920
963
self.assertEqual(case_sensitive, tree.case_sensitive)
964
if not isinstance(tree, InventoryWorkingTree):
965
raise TestNotApplicable("get_format_string is only available "
966
"on bzr working trees")
967
# now we cheat, and make a file that matches the case-sensitive name
968
t = tree.bzrdir.get_workingtree_transport(None)
970
content = tree._format.get_format_string()
971
except NotImplementedError:
972
# All-in-one formats didn't have a separate format string.
973
content = tree.bzrdir._format.get_format_string()
974
t.put_bytes(tree._format.case_sensitive_filename, content)
975
tree = tree.bzrdir.open_workingtree()
976
self.assertFalse(tree.case_sensitive)
978
def test_supports_executable(self):
979
self.build_tree(['filename'])
980
tree = self.make_branch_and_tree('.')
982
self.assertIsInstance(tree._supports_executable(), bool)
983
if tree._supports_executable():
986
self.assertFalse(tree.is_executable(tree.path2id('filename')))
989
os.chmod('filename', 0755)
990
self.addCleanup(tree.lock_read().unlock)
991
self.assertTrue(tree.is_executable(tree.path2id('filename')))
993
self.addCleanup(tree.lock_read().unlock)
994
self.assertFalse(tree.is_executable(tree.path2id('filename')))
922
996
def test_all_file_ids_with_missing(self):
923
997
tree = self.make_branch_and_tree('tree')
1054
1127
self.assertEqual(0, wt.update(revision='1'))
1055
1128
self.assertEqual('1', wt.last_revision())
1056
1129
self.assertEqual(tip, wt.branch.last_revision())
1057
self.failUnlessExists('checkout/file1')
1058
self.failIfExists('checkout/file4')
1059
self.failIfExists('checkout/file5')
1130
self.assertPathExists('checkout/file1')
1131
self.assertPathDoesNotExist('checkout/file4')
1132
self.assertPathDoesNotExist('checkout/file5')
1062
1135
class TestIllegalPaths(TestCaseWithWorkingTree):
1065
1138
if osutils.normalizes_filenames():
1066
1139
# You *can't* create an illegal filename on OSX.
1067
1140
raise tests.TestNotApplicable('OSX normalizes filenames')
1068
self.requireFeature(tests.UTF8Filesystem)
1141
self.requireFeature(features.UTF8Filesystem)
1069
1142
# We require a UTF8 filesystem, because otherwise we would need to get
1070
1143
# tricky to figure out how to create an illegal filename.
1071
1144
# \xb5 is an illegal path because it should be \xc2\xb5 for UTF-8
1104
1177
# above the control dir but we might need to relax that?
1105
1178
self.assertEqual(wt.control_url.find(wt.user_url), 0)
1106
1179
self.assertEqual(wt.control_url, wt.control_transport.base)
1182
class TestWorthSavingLimit(TestCaseWithWorkingTree):
1184
def make_wt_with_worth_saving_limit(self):
1185
wt = self.make_branch_and_tree('wt')
1186
if getattr(wt, '_worth_saving_limit', None) is None:
1187
raise tests.TestNotApplicable('no _worth_saving_limit for'
1190
self.addCleanup(wt.unlock)
1193
def test_not_set(self):
1194
# Default should be 10
1195
wt = self.make_wt_with_worth_saving_limit()
1196
self.assertEqual(10, wt._worth_saving_limit())
1197
ds = wt.current_dirstate()
1198
self.assertEqual(10, ds._worth_saving_limit)
1200
def test_set_in_branch(self):
1201
wt = self.make_wt_with_worth_saving_limit()
1202
conf = wt.get_config_stack()
1203
conf.set('bzr.workingtree.worth_saving_limit', '20')
1204
self.assertEqual(20, wt._worth_saving_limit())
1205
ds = wt.current_dirstate()
1206
self.assertEqual(10, ds._worth_saving_limit)
1208
def test_invalid(self):
1209
wt = self.make_wt_with_worth_saving_limit()
1210
conf = wt.get_config_stack()
1211
conf.set('bzr.workingtree.worth_saving_limit', 'a')
1212
# If the config entry is invalid, default to 10
1215
warnings.append(args[0] % args[1:])
1216
self.overrideAttr(trace, 'warning', warning)
1217
self.assertEqual(10, wt._worth_saving_limit())
1218
self.assertLength(1, warnings)
1219
self.assertEqual('Value "a" is not valid for'
1220
' "bzr.workingtree.worth_saving_limit"',
1224
class TestFormatAttributes(TestCaseWithWorkingTree):
1226
def test_versioned_directories(self):
1228
[self.workingtree_format.supports_versioned_directories],