72
72
# if there is a working tree now, this is not supported.
73
73
test_branch.controldir.open_workingtree()
74
74
raise TestNotApplicable("only on trees that can be separate"
75
" from their branch.")
75
" from their branch.")
76
76
except (errors.NoWorkingTree, errors.NotLocalUrl):
90
90
files = list(tree.list_files())
92
self.assertEqual(files.pop(0), ('dir', '?', 'directory', None, TreeDirectory()))
93
self.assertEqual(files.pop(0), ('file', '?', 'file', None, TreeFile()))
93
files.pop(0), ('dir', '?', 'directory', TreeDirectory()))
94
self.assertEqual(files.pop(0), ('file', '?', 'file', TreeFile()))
95
self.assertEqual(files.pop(0), ('symlink', '?', 'symlink', None, TreeLink()))
97
files.pop(0), ('symlink', '?', 'symlink', TreeLink()))
97
99
def test_list_files_sorted(self):
98
100
tree = self.make_branch_and_tree('.')
99
101
self.build_tree(['dir/', 'file', 'dir/file', 'dir/b',
100
102
'dir/subdir/', 'a', 'dir/subfile',
101
103
'zz_dir/', 'zz_dir/subfile'])
103
files = [(path, kind) for (path, v, kind, file_id, entry)
104
in tree.list_files()]
104
with tree.lock_read():
105
files = [(path, kind) for (path, v, kind, entry)
106
in tree.list_files()]
106
107
self.assertEqual([
108
109
('dir', 'directory'),
113
114
with tree.lock_write():
114
115
if tree.has_versioned_directories():
115
116
tree.add(['dir', 'zz_dir'])
116
files = [(path, kind) for (path, v, kind, file_id, entry)
117
in tree.list_files()]
117
files = [(path, kind) for (path, v, kind, entry)
118
in tree.list_files()]
118
119
self.assertEqual([
120
121
('dir', 'directory'),
130
131
tree.add(['dir/b'])
131
files = [(path, kind) for (path, v, kind, file_id, entry)
132
in tree.list_files()]
132
files = [(path, kind) for (path, v, kind, entry)
133
in tree.list_files()]
133
134
self.assertEqual([
135
136
('dir', 'directory'),
153
154
self.assertEqual(1, len(result))
154
155
if tree.has_versioned_directories():
155
156
self.assertEqual(
156
('filename', 'V', 'directory', tree.path2id('filename')),
157
('filename', 'V', 'directory', tree.path2id('filename')),
158
(result[0][0], result[0][1], result[0][2],
159
getattr(result[0][3], 'file_id', None)))
159
161
self.assertEqual(
160
('filename', '?', 'directory', None),
162
('filename', '?', 'directory', None),
163
(result[0][0], result[0][1], result[0][2],
164
getattr(result[0][3], 'file_id', None)))
163
166
def test_get_config_stack(self):
164
167
# Smoke test that all working trees succeed getting a config
196
199
# can even be a url: finds '.' and relpath of 'foo'
197
200
wt, relpath = WorkingTree.open_containing('./foo')
198
201
wt, relpath = WorkingTree.open_containing(
199
urlutils.local_path_to_url(getcwd() + '/foo'))
202
urlutils.local_path_to_url(getcwd() + '/foo'))
200
203
self.assertEqual('foo', relpath)
201
204
self.assertEqual(wt.basedir + '/', local_base)
223
226
tree = self.make_branch_and_tree('.')
225
228
self.build_tree(['hello.txt'])
226
with open('hello.txt', 'w') as f: f.write('initial hello')
229
with open('hello.txt', 'w') as f:
230
f.write('initial hello')
228
232
self.assertRaises(PathsNotVersionedError,
229
233
tree.revert, ['hello.txt'])
231
235
tree.commit('create initial hello.txt')
233
237
self.check_file_contents('hello.txt', b'initial hello')
234
with open('hello.txt', 'w') as f: f.write('new hello')
238
with open('hello.txt', 'w') as f:
235
240
self.check_file_contents('hello.txt', b'new hello')
237
242
# revert file modified since last revision
245
250
self.check_file_contents('hello.txt.~1~', b'new hello')
247
252
# backup files are numbered
248
with open('hello.txt', 'w') as f: f.write('new hello2')
253
with open('hello.txt', 'w') as f:
254
f.write('new hello2')
249
255
tree.revert(['hello.txt'])
250
256
self.check_file_contents('hello.txt', b'initial hello')
251
257
self.check_file_contents('hello.txt.~1~', b'new hello')
254
260
def test_revert_missing(self):
255
261
# Revert a file that has been deleted since last commit
256
262
tree = self.make_branch_and_tree('.')
257
with open('hello.txt', 'w') as f: f.write('initial hello')
263
with open('hello.txt', 'w') as f:
264
f.write('initial hello')
258
265
tree.add('hello.txt')
259
266
tree.commit('added hello.txt')
260
267
os.unlink('hello.txt')
276
283
self.build_tree_contents([('.bzrignore', b'*.~*\n')])
277
284
tree.add('.bzrignore')
278
285
self.assertEqual(list(tree.unknowns()),
288
def test_unknowns_empty_dir(self):
289
tree = self.make_branch_and_tree('.')
290
self.build_tree(['subdir/', 'subdir/somefile'])
291
if tree.has_versioned_directories():
292
self.assertEqual(list(tree.unknowns()), ['subdir'])
294
self.assertEqual(list(tree.unknowns()), ['subdir/somefile'])
281
296
def test_initialize(self):
282
297
# initialize should create a working tree and branch in an existing dir
311
326
self.check_tree_shape(wt,
312
['newdir/', 'newdir/sub/', 'newdir/sub/file'])
327
['newdir/', 'newdir/sub/', 'newdir/sub/file'])
314
329
wt.rename_one('newdir/sub', 'newdir/newsub')
316
331
self.check_tree_shape(wt, ['newdir/', 'newdir/newsub/',
317
'newdir/newsub/file'])
332
'newdir/newsub/file'])
320
335
def test_add_in_unversioned(self):
340
355
self.assertRaises(errors.NoSuchFile, wt.add, 'fpp')
342
357
def test_remove_verbose(self):
343
#FIXME the remove api should not print or otherwise depend on the
358
# FIXME the remove api should not print or otherwise depend on the
344
359
# text UI - RBC 20060124
345
360
wt = self.make_branch_and_tree('.')
346
361
self.build_tree(['hello'])
364
379
def test_clone_empty(self):
365
380
wt = self.make_branch_and_tree('source')
366
cloned_dir = wt.controldir.clone('target', revision_id=_mod_revision.NULL_REVISION)
381
cloned_dir = wt.controldir.clone(
382
'target', revision_id=_mod_revision.NULL_REVISION)
367
383
cloned = cloned_dir.open_workingtree()
368
384
self.assertEqual(cloned.get_parent_ids(), wt.get_parent_ids())
489
505
source.branch.repository.clone(made_control)
490
506
source.branch.clone(made_control)
491
507
made_tree = self.workingtree_format.initialize(made_control,
493
509
self.assertEqual([a], made_tree.get_parent_ids())
495
511
def test_post_build_tree_hook(self):
497
514
def track_post_build_tree(tree):
498
515
calls.append(tree.last_revision())
499
516
source = self.make_branch_and_tree('source')
504
521
source.branch.repository.clone(made_control)
505
522
source.branch.clone(made_control)
506
523
MutableTree.hooks.install_named_hook("post_build_tree",
507
track_post_build_tree, "Test")
524
track_post_build_tree, "Test")
508
525
made_tree = self.workingtree_format.initialize(made_control,
510
527
self.assertEqual([a], calls)
512
529
def test_update_sets_last_revision(self):
560
577
wt = self.make_branch_and_tree('tree')
561
578
if not wt._format.supports_setting_file_ids:
562
579
self.assertRaises(SettingFileIdUnsupported, wt.set_root_id,
565
582
wt.set_root_id(b'first_root_id')
566
583
self.assertEqual(b'first_root_id', wt.get_root_id())
604
621
def test_merge_revert(self):
605
622
from breezy.merge import merge_inner
606
623
this = self.make_branch_and_tree('b1')
607
self.build_tree_contents([('b1/a', b'a test\n'), ('b1/b', b'b test\n')])
624
self.build_tree_contents(
625
[('b1/a', b'a test\n'), ('b1/b', b'b test\n')])
608
626
this.add(['a', 'b'])
609
627
this.commit(message='')
610
628
base = this.controldir.clone('b2').open_workingtree()
611
629
self.build_tree_contents([('b2/a', b'b test\n')])
612
630
other = this.controldir.clone('b3').open_workingtree()
613
self.build_tree_contents([('b3/a', b'c test\n'), ('b3/c', b'c test\n')])
631
self.build_tree_contents(
632
[('b3/a', b'c test\n'), ('b3/c', b'c test\n')])
616
self.build_tree_contents([('b1/b', b'q test\n'), ('b1/d', b'd test\n')])
635
self.build_tree_contents(
636
[('b1/b', b'q test\n'), ('b1/d', b'd test\n')])
617
637
# Note: If we don't lock this before calling merge_inner, then we get a
618
638
# lock-contention failure. This probably indicates something
619
639
# weird going on inside merge_inner. Probably something about
701
721
tree = self.make_branch_and_tree('master')
702
722
if not isinstance(tree, InventoryWorkingTree):
703
723
raise TestNotApplicable("merge-hashes is specific to bzr "
705
725
tree._transport.put_bytes('merge-hashes', b'asdfasdf')
706
726
self.assertRaises(errors.MergeModifiedFormatError, tree.merge_modified)
749
769
def make_merge_conflicts(self):
750
770
from breezy.merge import merge_inner
751
771
tree = self.make_branch_and_tree('mine')
752
with open('mine/bloo', 'wb') as f: f.write(b'one')
753
with open('mine/blo', 'wb') as f: f.write(b'on')
772
with open('mine/bloo', 'wb') as f:
774
with open('mine/blo', 'wb') as f:
754
776
tree.add(['bloo', 'blo'])
755
777
tree.commit("blah", allow_pointless=False)
756
778
base = tree.branch.repository.revision_tree(tree.last_revision())
757
779
controldir.ControlDir.open("mine").sprout("other")
758
with open('other/bloo', 'wb') as f: f.write(b'two')
780
with open('other/bloo', 'wb') as f:
759
782
othertree = WorkingTree.open('other')
760
783
othertree.commit('blah', allow_pointless=False)
761
with open('mine/bloo', 'wb') as f: f.write(b'three')
784
with open('mine/bloo', 'wb') as f:
762
786
tree.commit("blah", allow_pointless=False)
763
787
merge_inner(tree.branch, othertree, base, this_tree=tree)
819
843
def test_format_leftmost_parent_id_as_ghost(self):
820
844
tree = self.make_branch_and_tree('tree')
822
tree._format.supports_leftmost_parent_id_as_ghost, (True, False))
846
tree._format.supports_leftmost_parent_id_as_ghost, (True, False))
824
848
def test_branch_attribute_is_not_settable(self):
825
849
# the branch attribute is an aspect of the working tree, not a
826
850
# configurable attribute
827
851
tree = self.make_branch_and_tree('tree')
828
853
def set_branch():
829
854
tree.branch = tree.branch
830
855
self.assertRaises(AttributeError, set_branch)
841
866
files = sorted(list(tree.list_files()))
843
self.assertEqual((u'.bzrignore', '?', 'file', None), files[0][:-1])
844
self.assertEqual((u'foo.pyc', 'V', 'file', anid), files[1][:-1])
869
(u'.bzrignore', '?', 'file', None),
870
(files[0][0], files[0][1], files[0][2],
871
getattr(files[0][3], 'file_id', None)))
873
(u'foo.pyc', 'V', 'file', anid),
874
(files[1][0], files[1][1], files[1][2], files[1][3].file_id))
845
875
self.assertEqual(2, len(files))
847
877
def test_non_normalized_add_accessible(self):
854
884
osutils.normalized_filename = osutils._accessible_normalized_filename
856
886
tree.add([u'a\u030a'])
858
self.assertEqual([('', 'directory'), (u'\xe5', 'file')],
859
[(path, ie.kind) for path, ie in
860
tree.iter_entries_by_dir()])
887
with tree.lock_read():
888
self.assertEqual([('', 'directory'), (u'\xe5', 'file')],
889
[(path, ie.kind) for path, ie in
890
tree.iter_entries_by_dir()])
863
892
osutils.normalized_filename = orig
872
901
osutils.normalized_filename = osutils._inaccessible_normalized_filename
874
903
self.assertRaises(errors.InvalidNormalization,
875
tree.add, [u'a\u030a'])
904
tree.add, [u'a\u030a'])
877
906
osutils.normalized_filename = orig
879
908
def test__write_inventory(self):
880
# The private interface _write_inventory is currently used by transform.
909
# The private interface _write_inventory is currently used by
881
911
tree = self.make_branch_and_tree('.')
882
912
if not isinstance(tree, InventoryWorkingTree):
883
913
raise TestNotApplicable("_write_inventory does not exist on "
884
"non-inventory working trees")
914
"non-inventory working trees")
885
915
# if we write write an inventory then do a walkdirs we should get back
886
916
# missing entries, and actual, and unknowns as appropriate.
887
917
self.build_tree(['present', 'unknown'])
893
923
tree.lock_write()
894
924
tree._write_inventory(inventory)
926
with tree.lock_read():
898
927
present_stat = os.lstat('present')
899
928
unknown_stat = os.lstat('unknown')
900
929
expected_results = [
901
930
(('', tree.get_root_id()),
902
931
[('missing', 'missing', 'unknown', None, b'missing-id', 'file'),
903
('present', 'present', 'file', present_stat, b'present-id', 'file'),
932
('present', 'present', 'file',
933
present_stat, b'present-id', 'file'),
904
934
('unknown', 'unknown', 'file', unknown_stat, None, None),
907
937
self.assertEqual(expected_results, list(tree.walkdirs()))
911
939
def test_path2id(self):
912
940
# smoke test for path2id
915
943
if tree.supports_setting_file_ids():
916
944
tree.add(['foo'], [b'foo-id'])
917
945
self.assertEqual(b'foo-id', tree.path2id('foo'))
918
# the next assertion is for backwards compatability with
946
# the next assertion is for backwards compatibility with
919
947
# WorkingTree3, though its probably a bad idea, it makes things
920
948
# work. Perhaps it should raise a deprecation warning?
921
949
self.assertEqual(b'foo-id', tree.path2id('foo/'))
930
958
# smoke test for filter_unversioned_files
931
959
tree = self.make_branch_and_tree('.')
932
960
paths = ['here-and-versioned', 'here-and-not-versioned',
933
'not-here-and-versioned', 'not-here-and-not-versioned']
961
'not-here-and-versioned', 'not-here-and-not-versioned']
934
962
tree.add(['here-and-versioned', 'not-here-and-versioned'],
935
kinds=['file', 'file'])
963
kinds=['file', 'file'])
936
964
self.build_tree(['here-and-versioned', 'here-and-not-versioned'])
938
966
self.addCleanup(tree.unlock)
961
989
# move them around so the names no longer correspond to the types
962
990
os.rename(names[0], 'tmp')
963
991
for i in range(1, len(names)):
964
os.rename(names[i], names[i-1])
992
os.rename(names[i], names[i - 1])
965
993
os.rename('tmp', names[-1])
966
994
# now look and expect to see the correct types again
967
995
for i in range(len(names)):
968
actual_kind = tree.kind(names[i-1])
996
actual_kind = tree.kind(names[i - 1])
969
997
expected_kind = names[i]
970
998
self.assertEqual(expected_kind, actual_kind)
1058
1086
self.build_tree(['tree/a', 'tree/b'])
1059
1087
tree.add(['a', 'b'])
1060
1088
os.unlink('tree/a')
1062
1091
{'a', 'b', ''},
1063
1092
set(tree.all_versioned_paths()))
1093
except errors.UnsupportedOperation:
1094
raise TestNotApplicable('tree does not support all_file_ids')
1065
1096
def test_sprout_hardlink(self):
1066
1097
real_os_link = getattr(os, 'link', None)
1070
1101
self.build_tree(['source/file'])
1071
1102
source.add('file')
1072
1103
source.commit('added file')
1073
1105
def fake_link(source, target):
1074
1106
raise OSError(errno.EPERM, 'Operation not permitted')
1075
1107
os.link = fake_link
1135
1167
final_branch = builder.get_branch()
1136
1168
# The master branch
1137
1169
master = final_branch.controldir.sprout(master_path,
1138
master_revid).open_branch()
1170
master_revid).open_branch()
1140
1172
wt = self.make_branch_and_tree(wt_path)
1141
1173
wt.pull(final_branch, stop_revision=wt_revid)
1142
wt.branch.pull(final_branch, stop_revision=branch_revid, overwrite=True)
1175
final_branch, stop_revision=branch_revid, overwrite=True)
1144
1177
wt.branch.bind(master)
1145
1178
except errors.UpgradeRequired:
1266
1299
conf.set('bzr.workingtree.worth_saving_limit', 'a')
1267
1300
# If the config entry is invalid, default to 10
1269
1303
def warning(*args):
1270
1304
warnings.append(args[0] % args[1:])
1271
1305
self.overrideAttr(trace, 'warning', warning)
1272
1306
self.assertEqual(10, wt._worth_saving_limit())
1273
1307
self.assertLength(1, warnings)
1274
1308
self.assertEqual('Value "a" is not valid for'
1275
' "bzr.workingtree.worth_saving_limit"',
1309
' "bzr.workingtree.worth_saving_limit"',
1279
1313
class TestFormatAttributes(TestCaseWithWorkingTree):