190
255
transform3.delete_contents(oz_id)
191
256
self.assertEqual(transform3.find_conflicts(),
192
257
[('missing parent', oz_id)])
193
root_id = transform3.trans_id_tree_file_id('TREE_ROOT')
258
root_id = transform3.root
194
259
tip_id = transform3.trans_id_tree_file_id('tip-id')
195
260
transform3.adjust_path('tip', root_id, tip_id)
196
261
transform3.apply()
263
def test_conflict_on_case_insensitive(self):
264
tree = self.make_branch_and_tree('tree')
265
# Don't try this at home, kids!
266
# Force the tree to report that it is case sensitive, for conflict
268
tree.case_sensitive = True
269
transform = TreeTransform(tree)
270
self.addCleanup(transform.finalize)
271
transform.new_file('file', transform.root, 'content')
272
transform.new_file('FiLe', transform.root, 'content')
273
result = transform.find_conflicts()
274
self.assertEqual([], result)
276
# Force the tree to report that it is case insensitive, for conflict
278
tree.case_sensitive = False
279
transform = TreeTransform(tree)
280
self.addCleanup(transform.finalize)
281
transform.new_file('file', transform.root, 'content')
282
transform.new_file('FiLe', transform.root, 'content')
283
result = transform.find_conflicts()
284
self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
286
def test_conflict_on_case_insensitive_existing(self):
287
tree = self.make_branch_and_tree('tree')
288
self.build_tree(['tree/FiLe'])
289
# Don't try this at home, kids!
290
# Force the tree to report that it is case sensitive, for conflict
292
tree.case_sensitive = True
293
transform = TreeTransform(tree)
294
self.addCleanup(transform.finalize)
295
transform.new_file('file', transform.root, 'content')
296
result = transform.find_conflicts()
297
self.assertEqual([], result)
299
# Force the tree to report that it is case insensitive, for conflict
301
tree.case_sensitive = False
302
transform = TreeTransform(tree)
303
self.addCleanup(transform.finalize)
304
transform.new_file('file', transform.root, 'content')
305
result = transform.find_conflicts()
306
self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
308
def test_resolve_case_insensitive_conflict(self):
309
tree = self.make_branch_and_tree('tree')
310
# Don't try this at home, kids!
311
# Force the tree to report that it is case insensitive, for conflict
313
tree.case_sensitive = False
314
transform = TreeTransform(tree)
315
self.addCleanup(transform.finalize)
316
transform.new_file('file', transform.root, 'content')
317
transform.new_file('FiLe', transform.root, 'content')
318
resolve_conflicts(transform)
320
self.failUnlessExists('tree/file')
321
self.failUnlessExists('tree/FiLe.moved')
323
def test_resolve_checkout_case_conflict(self):
324
tree = self.make_branch_and_tree('tree')
325
# Don't try this at home, kids!
326
# Force the tree to report that it is case insensitive, for conflict
328
tree.case_sensitive = False
329
transform = TreeTransform(tree)
330
self.addCleanup(transform.finalize)
331
transform.new_file('file', transform.root, 'content')
332
transform.new_file('FiLe', transform.root, 'content')
333
resolve_conflicts(transform,
334
pass_func=lambda t, c: resolve_checkout(t, c, []))
336
self.failUnlessExists('tree/file')
337
self.failUnlessExists('tree/FiLe.moved')
339
def test_apply_case_conflict(self):
340
"""Ensure that a transform with case conflicts can always be applied"""
341
tree = self.make_branch_and_tree('tree')
342
transform = TreeTransform(tree)
343
self.addCleanup(transform.finalize)
344
transform.new_file('file', transform.root, 'content')
345
transform.new_file('FiLe', transform.root, 'content')
346
dir = transform.new_directory('dir', transform.root)
347
transform.new_file('dirfile', dir, 'content')
348
transform.new_file('dirFiLe', dir, 'content')
349
resolve_conflicts(transform)
351
self.failUnlessExists('tree/file')
352
if not os.path.exists('tree/FiLe.moved'):
353
self.failUnlessExists('tree/FiLe')
354
self.failUnlessExists('tree/dir/dirfile')
355
if not os.path.exists('tree/dir/dirFiLe.moved'):
356
self.failUnlessExists('tree/dir/dirFiLe')
358
def test_case_insensitive_limbo(self):
359
tree = self.make_branch_and_tree('tree')
360
# Don't try this at home, kids!
361
# Force the tree to report that it is case insensitive
362
tree.case_sensitive = False
363
transform = TreeTransform(tree)
364
self.addCleanup(transform.finalize)
365
dir = transform.new_directory('dir', transform.root)
366
first = transform.new_file('file', dir, 'content')
367
second = transform.new_file('FiLe', dir, 'content')
368
self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
369
self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
198
371
def test_add_del(self):
199
372
start, root = self.get_transform()
200
373
start.new_directory('a', root, 'a')
507
759
transform, root = self.get_transform()
508
760
wt = transform._tree
762
self.addCleanup(wt.unlock)
509
763
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
511
sac = transform.new_file('set_after_creation', root, 'Set after creation', 'sac')
765
sac = transform.new_file('set_after_creation', root,
766
'Set after creation', 'sac')
512
767
transform.set_executability(True, sac)
513
uws = transform.new_file('unset_without_set', root, 'Unset badly', 'uws')
768
uws = transform.new_file('unset_without_set', root, 'Unset badly',
514
770
self.assertRaises(KeyError, transform.set_executability, None, uws)
515
771
transform.apply()
516
772
self.assertTrue(wt.is_executable('soc'))
517
773
self.assertTrue(wt.is_executable('sac'))
775
def test_preserve_mode(self):
776
"""File mode is preserved when replacing content"""
777
if sys.platform == 'win32':
778
raise TestSkipped('chmod has no effect on win32')
779
transform, root = self.get_transform()
780
transform.new_file('file1', root, 'contents', 'file1-id', True)
783
self.addCleanup(self.wt.unlock)
784
self.assertTrue(self.wt.is_executable('file1-id'))
785
transform, root = self.get_transform()
786
file1_id = transform.trans_id_tree_file_id('file1-id')
787
transform.delete_contents(file1_id)
788
transform.create_file('contents2', file1_id)
790
self.assertTrue(self.wt.is_executable('file1-id'))
792
def test__set_mode_stats_correctly(self):
793
"""_set_mode stats to determine file mode."""
794
if sys.platform == 'win32':
795
raise TestSkipped('chmod has no effect on win32')
799
def instrumented_stat(path):
800
stat_paths.append(path)
801
return real_stat(path)
803
transform, root = self.get_transform()
805
bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
806
file_id='bar-id-1', executable=False)
809
transform, root = self.get_transform()
810
bar1_id = transform.trans_id_tree_path('bar')
811
bar2_id = transform.trans_id_tree_path('bar2')
813
os.stat = instrumented_stat
814
transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
819
bar1_abspath = self.wt.abspath('bar')
820
self.assertEqual([bar1_abspath], stat_paths)
822
def test_iter_changes(self):
823
self.wt.set_root_id('eert_toor')
824
transform, root = self.get_transform()
825
transform.new_file('old', root, 'blah', 'id-1', True)
827
transform, root = self.get_transform()
829
self.assertEqual([], list(transform.iter_changes()))
830
old = transform.trans_id_tree_file_id('id-1')
831
transform.unversion_file(old)
832
self.assertEqual([('id-1', ('old', None), False, (True, False),
833
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
834
(True, True))], list(transform.iter_changes()))
835
transform.new_directory('new', root, 'id-1')
836
self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
837
('eert_toor', 'eert_toor'), ('old', 'new'),
838
('file', 'directory'),
839
(True, False))], list(transform.iter_changes()))
843
def test_iter_changes_new(self):
844
self.wt.set_root_id('eert_toor')
845
transform, root = self.get_transform()
846
transform.new_file('old', root, 'blah')
848
transform, root = self.get_transform()
850
old = transform.trans_id_tree_path('old')
851
transform.version_file('id-1', old)
852
self.assertEqual([('id-1', (None, 'old'), False, (False, True),
853
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
854
(False, False))], list(transform.iter_changes()))
858
def test_iter_changes_modifications(self):
859
self.wt.set_root_id('eert_toor')
860
transform, root = self.get_transform()
861
transform.new_file('old', root, 'blah', 'id-1')
862
transform.new_file('new', root, 'blah')
863
transform.new_directory('subdir', root, 'subdir-id')
865
transform, root = self.get_transform()
867
old = transform.trans_id_tree_path('old')
868
subdir = transform.trans_id_tree_file_id('subdir-id')
869
new = transform.trans_id_tree_path('new')
870
self.assertEqual([], list(transform.iter_changes()))
873
transform.delete_contents(old)
874
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
875
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
876
(False, False))], list(transform.iter_changes()))
879
transform.create_file('blah', old)
880
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
881
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
882
(False, False))], list(transform.iter_changes()))
883
transform.cancel_deletion(old)
884
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
885
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
886
(False, False))], list(transform.iter_changes()))
887
transform.cancel_creation(old)
889
# move file_id to a different file
890
self.assertEqual([], list(transform.iter_changes()))
891
transform.unversion_file(old)
892
transform.version_file('id-1', new)
893
transform.adjust_path('old', root, new)
894
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
895
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
896
(False, False))], list(transform.iter_changes()))
897
transform.cancel_versioning(new)
898
transform._removed_id = set()
901
self.assertEqual([], list(transform.iter_changes()))
902
transform.set_executability(True, old)
903
self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
904
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
905
(False, True))], list(transform.iter_changes()))
906
transform.set_executability(None, old)
909
self.assertEqual([], list(transform.iter_changes()))
910
transform.adjust_path('new', root, old)
911
transform._new_parent = {}
912
self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
913
('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
914
(False, False))], list(transform.iter_changes()))
915
transform._new_name = {}
918
self.assertEqual([], list(transform.iter_changes()))
919
transform.adjust_path('new', subdir, old)
920
transform._new_name = {}
921
self.assertEqual([('id-1', ('old', 'subdir/old'), False,
922
(True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
923
('file', 'file'), (False, False))],
924
list(transform.iter_changes()))
925
transform._new_path = {}
930
def test_iter_changes_modified_bleed(self):
931
self.wt.set_root_id('eert_toor')
932
"""Modified flag should not bleed from one change to another"""
933
# unfortunately, we have no guarantee that file1 (which is modified)
934
# will be applied before file2. And if it's applied after file2, it
935
# obviously can't bleed into file2's change output. But for now, it
937
transform, root = self.get_transform()
938
transform.new_file('file1', root, 'blah', 'id-1')
939
transform.new_file('file2', root, 'blah', 'id-2')
941
transform, root = self.get_transform()
943
transform.delete_contents(transform.trans_id_file_id('id-1'))
944
transform.set_executability(True,
945
transform.trans_id_file_id('id-2'))
946
self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
947
('eert_toor', 'eert_toor'), ('file1', u'file1'),
948
('file', None), (False, False)),
949
('id-2', (u'file2', u'file2'), False, (True, True),
950
('eert_toor', 'eert_toor'), ('file2', u'file2'),
951
('file', 'file'), (False, True))],
952
list(transform.iter_changes()))
956
def test_iter_changes_move_missing(self):
957
"""Test moving ids with no files around"""
958
self.wt.set_root_id('toor_eert')
959
# Need two steps because versioning a non-existant file is a conflict.
960
transform, root = self.get_transform()
961
transform.new_directory('floater', root, 'floater-id')
963
transform, root = self.get_transform()
964
transform.delete_contents(transform.trans_id_tree_path('floater'))
966
transform, root = self.get_transform()
967
floater = transform.trans_id_tree_path('floater')
969
transform.adjust_path('flitter', root, floater)
970
self.assertEqual([('floater-id', ('floater', 'flitter'), False,
971
(True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
972
(None, None), (False, False))], list(transform.iter_changes()))
976
def test_iter_changes_pointless(self):
977
"""Ensure that no-ops are not treated as modifications"""
978
self.wt.set_root_id('eert_toor')
979
transform, root = self.get_transform()
980
transform.new_file('old', root, 'blah', 'id-1')
981
transform.new_directory('subdir', root, 'subdir-id')
983
transform, root = self.get_transform()
985
old = transform.trans_id_tree_path('old')
986
subdir = transform.trans_id_tree_file_id('subdir-id')
987
self.assertEqual([], list(transform.iter_changes()))
988
transform.delete_contents(subdir)
989
transform.create_directory(subdir)
990
transform.set_executability(False, old)
991
transform.unversion_file(old)
992
transform.version_file('id-1', old)
993
transform.adjust_path('old', root, old)
994
self.assertEqual([], list(transform.iter_changes()))
998
def test_rename_count(self):
999
transform, root = self.get_transform()
1000
transform.new_file('name1', root, 'contents')
1001
self.assertEqual(transform.rename_count, 0)
1003
self.assertEqual(transform.rename_count, 1)
1004
transform2, root = self.get_transform()
1005
transform2.adjust_path('name2', root,
1006
transform2.trans_id_tree_path('name1'))
1007
self.assertEqual(transform2.rename_count, 0)
1009
self.assertEqual(transform2.rename_count, 2)
1011
def test_change_parent(self):
1012
"""Ensure that after we change a parent, the results are still right.
1014
Renames and parent changes on pending transforms can happen as part
1015
of conflict resolution, and are explicitly permitted by the
1018
This test ensures they work correctly with the rename-avoidance
1021
transform, root = self.get_transform()
1022
parent1 = transform.new_directory('parent1', root)
1023
child1 = transform.new_file('child1', parent1, 'contents')
1024
parent2 = transform.new_directory('parent2', root)
1025
transform.adjust_path('child1', parent2, child1)
1027
self.failIfExists(self.wt.abspath('parent1/child1'))
1028
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1029
# rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1030
# no rename for child1 (counting only renames during apply)
1031
self.failUnlessEqual(2, transform.rename_count)
1033
def test_cancel_parent(self):
1034
"""Cancelling a parent doesn't cause deletion of a non-empty directory
1036
This is like the test_change_parent, except that we cancel the parent
1037
before adjusting the path. The transform must detect that the
1038
directory is non-empty, and move children to safe locations.
1040
transform, root = self.get_transform()
1041
parent1 = transform.new_directory('parent1', root)
1042
child1 = transform.new_file('child1', parent1, 'contents')
1043
child2 = transform.new_file('child2', parent1, 'contents')
1045
transform.cancel_creation(parent1)
1047
self.fail('Failed to move child1 before deleting parent1')
1048
transform.cancel_creation(child2)
1049
transform.create_directory(parent1)
1051
transform.cancel_creation(parent1)
1052
# If the transform incorrectly believes that child2 is still in
1053
# parent1's limbo directory, it will try to rename it and fail
1054
# because was already moved by the first cancel_creation.
1056
self.fail('Transform still thinks child2 is a child of parent1')
1057
parent2 = transform.new_directory('parent2', root)
1058
transform.adjust_path('child1', parent2, child1)
1060
self.failIfExists(self.wt.abspath('parent1'))
1061
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1062
# rename limbo/new-3 => parent2, rename limbo/new-2 => child1
1063
self.failUnlessEqual(2, transform.rename_count)
1065
def test_adjust_and_cancel(self):
1066
"""Make sure adjust_path keeps track of limbo children properly"""
1067
transform, root = self.get_transform()
1068
parent1 = transform.new_directory('parent1', root)
1069
child1 = transform.new_file('child1', parent1, 'contents')
1070
parent2 = transform.new_directory('parent2', root)
1071
transform.adjust_path('child1', parent2, child1)
1072
transform.cancel_creation(child1)
1074
transform.cancel_creation(parent1)
1075
# if the transform thinks child1 is still in parent1's limbo
1076
# directory, it will attempt to move it and fail.
1078
self.fail('Transform still thinks child1 is a child of parent1')
1079
transform.finalize()
1081
def test_noname_contents(self):
1082
"""TreeTransform should permit deferring naming files."""
1083
transform, root = self.get_transform()
1084
parent = transform.trans_id_file_id('parent-id')
1086
transform.create_directory(parent)
1088
self.fail("Can't handle contents with no name")
1089
transform.finalize()
1091
def test_noname_contents_nested(self):
1092
"""TreeTransform should permit deferring naming files."""
1093
transform, root = self.get_transform()
1094
parent = transform.trans_id_file_id('parent-id')
1096
transform.create_directory(parent)
1098
self.fail("Can't handle contents with no name")
1099
child = transform.new_directory('child', parent)
1100
transform.adjust_path('parent', root, parent)
1102
self.failUnlessExists(self.wt.abspath('parent/child'))
1103
self.assertEqual(1, transform.rename_count)
1105
def test_reuse_name(self):
1106
"""Avoid reusing the same limbo name for different files"""
1107
transform, root = self.get_transform()
1108
parent = transform.new_directory('parent', root)
1109
child1 = transform.new_directory('child', parent)
1111
child2 = transform.new_directory('child', parent)
1113
self.fail('Tranform tried to use the same limbo name twice')
1114
transform.adjust_path('child2', parent, child2)
1116
# limbo/new-1 => parent, limbo/new-3 => parent/child2
1117
# child2 is put into top-level limbo because child1 has already
1118
# claimed the direct limbo path when child2 is created. There is no
1119
# advantage in renaming files once they're in top-level limbo, except
1121
self.assertEqual(2, transform.rename_count)
1123
def test_reuse_when_first_moved(self):
1124
"""Don't avoid direct paths when it is safe to use them"""
1125
transform, root = self.get_transform()
1126
parent = transform.new_directory('parent', root)
1127
child1 = transform.new_directory('child', parent)
1128
transform.adjust_path('child1', parent, child1)
1129
child2 = transform.new_directory('child', parent)
1131
# limbo/new-1 => parent
1132
self.assertEqual(1, transform.rename_count)
1134
def test_reuse_after_cancel(self):
1135
"""Don't avoid direct paths when it is safe to use them"""
1136
transform, root = self.get_transform()
1137
parent2 = transform.new_directory('parent2', root)
1138
child1 = transform.new_directory('child1', parent2)
1139
transform.cancel_creation(parent2)
1140
transform.create_directory(parent2)
1141
child2 = transform.new_directory('child1', parent2)
1142
transform.adjust_path('child2', parent2, child1)
1144
# limbo/new-1 => parent2, limbo/new-2 => parent2/child1
1145
self.assertEqual(2, transform.rename_count)
1147
def test_finalize_order(self):
1148
"""Finalize must be done in child-to-parent order"""
1149
transform, root = self.get_transform()
1150
parent = transform.new_directory('parent', root)
1151
child = transform.new_directory('child', parent)
1153
transform.finalize()
1155
self.fail('Tried to remove parent before child1')
1157
def test_cancel_with_cancelled_child_should_succeed(self):
1158
transform, root = self.get_transform()
1159
parent = transform.new_directory('parent', root)
1160
child = transform.new_directory('child', parent)
1161
transform.cancel_creation(child)
1162
transform.cancel_creation(parent)
1163
transform.finalize()
1165
def test_case_insensitive_clash(self):
1166
self.requireFeature(CaseInsensitiveFilesystemFeature)
1168
wt = self.make_branch_and_tree('.')
1169
tt = TreeTransform(wt) # TreeTransform obtains write lock
1171
tt.new_file('foo', tt.root, 'bar')
1172
tt.new_file('Foo', tt.root, 'spam')
1173
# Lie to tt that we've already resolved all conflicts.
1174
tt.apply(no_conflicts=True)
1178
err = self.assertRaises(errors.FileExists, tt_helper)
1179
self.assertContainsRe(str(err),
1180
"^File exists: .+/foo")
1182
def test_two_directories_clash(self):
1184
wt = self.make_branch_and_tree('.')
1185
tt = TreeTransform(wt) # TreeTransform obtains write lock
1187
foo_1 = tt.new_directory('foo', tt.root)
1188
tt.new_directory('bar', foo_1)
1189
foo_2 = tt.new_directory('foo', tt.root)
1190
tt.new_directory('baz', foo_2)
1191
# Lie to tt that we've already resolved all conflicts.
1192
tt.apply(no_conflicts=True)
1196
err = self.assertRaises(errors.FileExists, tt_helper)
1197
self.assertContainsRe(str(err),
1198
"^File exists: .+/foo")
1200
def test_two_directories_clash_finalize(self):
1202
wt = self.make_branch_and_tree('.')
1203
tt = TreeTransform(wt) # TreeTransform obtains write lock
1205
foo_1 = tt.new_directory('foo', tt.root)
1206
tt.new_directory('bar', foo_1)
1207
foo_2 = tt.new_directory('foo', tt.root)
1208
tt.new_directory('baz', foo_2)
1209
# Lie to tt that we've already resolved all conflicts.
1210
tt.apply(no_conflicts=True)
1214
err = self.assertRaises(errors.FileExists, tt_helper)
1215
self.assertContainsRe(str(err),
1216
"^File exists: .+/foo")
1218
def test_file_to_directory(self):
1219
wt = self.make_branch_and_tree('.')
1220
self.build_tree(['foo'])
1223
tt = TreeTransform(wt)
1224
self.addCleanup(tt.finalize)
1225
foo_trans_id = tt.trans_id_tree_path("foo")
1226
tt.delete_contents(foo_trans_id)
1227
tt.create_directory(foo_trans_id)
1228
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1229
tt.create_file(["aa\n"], bar_trans_id)
1230
tt.version_file("bar-1", bar_trans_id)
1232
self.failUnlessExists("foo/bar")
1235
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1240
changes = wt.changes_from(wt.basis_tree())
1241
self.assertFalse(changes.has_changed(), changes)
1243
def test_file_to_symlink(self):
1244
self.requireFeature(SymlinkFeature)
1245
wt = self.make_branch_and_tree('.')
1246
self.build_tree(['foo'])
1249
tt = TreeTransform(wt)
1250
self.addCleanup(tt.finalize)
1251
foo_trans_id = tt.trans_id_tree_path("foo")
1252
tt.delete_contents(foo_trans_id)
1253
tt.create_symlink("bar", foo_trans_id)
1255
self.failUnlessExists("foo")
1257
self.addCleanup(wt.unlock)
1258
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1261
def test_dir_to_file(self):
1262
wt = self.make_branch_and_tree('.')
1263
self.build_tree(['foo/', 'foo/bar'])
1264
wt.add(['foo', 'foo/bar'])
1266
tt = TreeTransform(wt)
1267
self.addCleanup(tt.finalize)
1268
foo_trans_id = tt.trans_id_tree_path("foo")
1269
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1270
tt.delete_contents(foo_trans_id)
1271
tt.delete_versioned(bar_trans_id)
1272
tt.create_file(["aa\n"], foo_trans_id)
1274
self.failUnlessExists("foo")
1276
self.addCleanup(wt.unlock)
1277
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1280
def test_dir_to_hardlink(self):
1281
self.requireFeature(HardlinkFeature)
1282
wt = self.make_branch_and_tree('.')
1283
self.build_tree(['foo/', 'foo/bar'])
1284
wt.add(['foo', 'foo/bar'])
1286
tt = TreeTransform(wt)
1287
self.addCleanup(tt.finalize)
1288
foo_trans_id = tt.trans_id_tree_path("foo")
1289
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1290
tt.delete_contents(foo_trans_id)
1291
tt.delete_versioned(bar_trans_id)
1292
self.build_tree(['baz'])
1293
tt.create_hardlink("baz", foo_trans_id)
1295
self.failUnlessExists("foo")
1296
self.failUnlessExists("baz")
1298
self.addCleanup(wt.unlock)
1299
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
520
1303
class TransformGroup(object):
521
def __init__(self, dirname):
1305
def __init__(self, dirname, root_id):
522
1306
self.name = dirname
523
1307
os.mkdir(dirname)
524
1308
self.wt = BzrDir.create_standalone_workingtree(dirname)
1309
self.wt.set_root_id(root_id)
525
1310
self.b = self.wt.branch
526
1311
self.tt = TreeTransform(self.wt)
527
1312
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
529
1315
def conflict_text(tree, merge):
530
1316
template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
531
1317
return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
534
1320
class TestTransformMerge(TestCaseInTempDir):
535
1322
def test_text_merge(self):
536
base = TransformGroup("base")
1323
root_id = generate_ids.gen_root_id()
1324
base = TransformGroup("base", root_id)
537
1325
base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
538
1326
base.tt.new_file('b', base.root, 'b1', 'b')
539
1327
base.tt.new_file('c', base.root, 'c', 'c')
724
1516
a.add(['foo', 'foo/bar', 'foo/baz'])
725
1517
a.commit('initial commit')
726
1518
b = BzrDir.create_standalone_workingtree('b')
727
build_tree(a.basis_tree(), b)
1519
basis = a.basis_tree()
1521
self.addCleanup(basis.unlock)
1522
build_tree(basis, b)
728
1523
self.assertIs(os.path.isdir('b/foo'), True)
729
1524
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
730
1525
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1527
def test_build_with_references(self):
1528
tree = self.make_branch_and_tree('source',
1529
format='dirstate-with-subtree')
1530
subtree = self.make_branch_and_tree('source/subtree',
1531
format='dirstate-with-subtree')
1532
tree.add_reference(subtree)
1533
tree.commit('a revision')
1534
tree.branch.create_checkout('target')
1535
self.failUnlessExists('target')
1536
self.failUnlessExists('target/subtree')
1538
def test_file_conflict_handling(self):
1539
"""Ensure that when building trees, conflict handling is done"""
1540
source = self.make_branch_and_tree('source')
1541
target = self.make_branch_and_tree('target')
1542
self.build_tree(['source/file', 'target/file'])
1543
source.add('file', 'new-file')
1544
source.commit('added file')
1545
build_tree(source.basis_tree(), target)
1546
self.assertEqual([DuplicateEntry('Moved existing file to',
1547
'file.moved', 'file', None, 'new-file')],
1549
target2 = self.make_branch_and_tree('target2')
1550
target_file = file('target2/file', 'wb')
1552
source_file = file('source/file', 'rb')
1554
target_file.write(source_file.read())
1559
build_tree(source.basis_tree(), target2)
1560
self.assertEqual([], target2.conflicts())
1562
def test_symlink_conflict_handling(self):
1563
"""Ensure that when building trees, conflict handling is done"""
1564
self.requireFeature(SymlinkFeature)
1565
source = self.make_branch_and_tree('source')
1566
os.symlink('foo', 'source/symlink')
1567
source.add('symlink', 'new-symlink')
1568
source.commit('added file')
1569
target = self.make_branch_and_tree('target')
1570
os.symlink('bar', 'target/symlink')
1571
build_tree(source.basis_tree(), target)
1572
self.assertEqual([DuplicateEntry('Moved existing file to',
1573
'symlink.moved', 'symlink', None, 'new-symlink')],
1575
target = self.make_branch_and_tree('target2')
1576
os.symlink('foo', 'target2/symlink')
1577
build_tree(source.basis_tree(), target)
1578
self.assertEqual([], target.conflicts())
1580
def test_directory_conflict_handling(self):
1581
"""Ensure that when building trees, conflict handling is done"""
1582
source = self.make_branch_and_tree('source')
1583
target = self.make_branch_and_tree('target')
1584
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1585
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1586
source.commit('added file')
1587
build_tree(source.basis_tree(), target)
1588
self.assertEqual([], target.conflicts())
1589
self.failUnlessExists('target/dir1/file')
1591
# Ensure contents are merged
1592
target = self.make_branch_and_tree('target2')
1593
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1594
build_tree(source.basis_tree(), target)
1595
self.assertEqual([], target.conflicts())
1596
self.failUnlessExists('target2/dir1/file2')
1597
self.failUnlessExists('target2/dir1/file')
1599
# Ensure new contents are suppressed for existing branches
1600
target = self.make_branch_and_tree('target3')
1601
self.make_branch('target3/dir1')
1602
self.build_tree(['target3/dir1/file2'])
1603
build_tree(source.basis_tree(), target)
1604
self.failIfExists('target3/dir1/file')
1605
self.failUnlessExists('target3/dir1/file2')
1606
self.failUnlessExists('target3/dir1.diverted/file')
1607
self.assertEqual([DuplicateEntry('Diverted to',
1608
'dir1.diverted', 'dir1', 'new-dir1', None)],
1611
target = self.make_branch_and_tree('target4')
1612
self.build_tree(['target4/dir1/'])
1613
self.make_branch('target4/dir1/file')
1614
build_tree(source.basis_tree(), target)
1615
self.failUnlessExists('target4/dir1/file')
1616
self.assertEqual('directory', file_kind('target4/dir1/file'))
1617
self.failUnlessExists('target4/dir1/file.diverted')
1618
self.assertEqual([DuplicateEntry('Diverted to',
1619
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1622
def test_mixed_conflict_handling(self):
1623
"""Ensure that when building trees, conflict handling is done"""
1624
source = self.make_branch_and_tree('source')
1625
target = self.make_branch_and_tree('target')
1626
self.build_tree(['source/name', 'target/name/'])
1627
source.add('name', 'new-name')
1628
source.commit('added file')
1629
build_tree(source.basis_tree(), target)
1630
self.assertEqual([DuplicateEntry('Moved existing file to',
1631
'name.moved', 'name', None, 'new-name')], target.conflicts())
1633
def test_raises_in_populated(self):
1634
source = self.make_branch_and_tree('source')
1635
self.build_tree(['source/name'])
1637
source.commit('added name')
1638
target = self.make_branch_and_tree('target')
1639
self.build_tree(['target/name'])
1641
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1642
build_tree, source.basis_tree(), target)
1644
def test_build_tree_rename_count(self):
1645
source = self.make_branch_and_tree('source')
1646
self.build_tree(['source/file1', 'source/dir1/'])
1647
source.add(['file1', 'dir1'])
1648
source.commit('add1')
1649
target1 = self.make_branch_and_tree('target1')
1650
transform_result = build_tree(source.basis_tree(), target1)
1651
self.assertEqual(2, transform_result.rename_count)
1653
self.build_tree(['source/dir1/file2'])
1654
source.add(['dir1/file2'])
1655
source.commit('add3')
1656
target2 = self.make_branch_and_tree('target2')
1657
transform_result = build_tree(source.basis_tree(), target2)
1658
# children of non-root directories should not be renamed
1659
self.assertEqual(2, transform_result.rename_count)
1661
def create_ab_tree(self):
1662
"""Create a committed test tree with two files"""
1663
source = self.make_branch_and_tree('source')
1664
self.build_tree_contents([('source/file1', 'A')])
1665
self.build_tree_contents([('source/file2', 'B')])
1666
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1667
source.commit('commit files')
1669
self.addCleanup(source.unlock)
1672
def test_build_tree_accelerator_tree(self):
1673
source = self.create_ab_tree()
1674
self.build_tree_contents([('source/file2', 'C')])
1676
real_source_get_file = source.get_file
1677
def get_file(file_id, path=None):
1678
calls.append(file_id)
1679
return real_source_get_file(file_id, path)
1680
source.get_file = get_file
1681
target = self.make_branch_and_tree('target')
1682
revision_tree = source.basis_tree()
1683
revision_tree.lock_read()
1684
self.addCleanup(revision_tree.unlock)
1685
build_tree(revision_tree, target, source)
1686
self.assertEqual(['file1-id'], calls)
1688
self.addCleanup(target.unlock)
1689
self.assertEqual([], list(target.iter_changes(revision_tree)))
1691
def test_build_tree_accelerator_tree_missing_file(self):
1692
source = self.create_ab_tree()
1693
os.unlink('source/file1')
1694
source.remove(['file2'])
1695
target = self.make_branch_and_tree('target')
1696
revision_tree = source.basis_tree()
1697
revision_tree.lock_read()
1698
self.addCleanup(revision_tree.unlock)
1699
build_tree(revision_tree, target, source)
1701
self.addCleanup(target.unlock)
1702
self.assertEqual([], list(target.iter_changes(revision_tree)))
1704
def test_build_tree_accelerator_wrong_kind(self):
1705
self.requireFeature(SymlinkFeature)
1706
source = self.make_branch_and_tree('source')
1707
self.build_tree_contents([('source/file1', '')])
1708
self.build_tree_contents([('source/file2', '')])
1709
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1710
source.commit('commit files')
1711
os.unlink('source/file2')
1712
self.build_tree_contents([('source/file2/', 'C')])
1713
os.unlink('source/file1')
1714
os.symlink('file2', 'source/file1')
1716
real_source_get_file = source.get_file
1717
def get_file(file_id, path=None):
1718
calls.append(file_id)
1719
return real_source_get_file(file_id, path)
1720
source.get_file = get_file
1721
target = self.make_branch_and_tree('target')
1722
revision_tree = source.basis_tree()
1723
revision_tree.lock_read()
1724
self.addCleanup(revision_tree.unlock)
1725
build_tree(revision_tree, target, source)
1726
self.assertEqual([], calls)
1728
self.addCleanup(target.unlock)
1729
self.assertEqual([], list(target.iter_changes(revision_tree)))
1731
def test_build_tree_hardlink(self):
1732
self.requireFeature(HardlinkFeature)
1733
source = self.create_ab_tree()
1734
target = self.make_branch_and_tree('target')
1735
revision_tree = source.basis_tree()
1736
revision_tree.lock_read()
1737
self.addCleanup(revision_tree.unlock)
1738
build_tree(revision_tree, target, source, hardlink=True)
1740
self.addCleanup(target.unlock)
1741
self.assertEqual([], list(target.iter_changes(revision_tree)))
1742
source_stat = os.stat('source/file1')
1743
target_stat = os.stat('target/file1')
1744
self.assertEqual(source_stat, target_stat)
1746
# Explicitly disallowing hardlinks should prevent them.
1747
target2 = self.make_branch_and_tree('target2')
1748
build_tree(revision_tree, target2, source, hardlink=False)
1750
self.addCleanup(target2.unlock)
1751
self.assertEqual([], list(target2.iter_changes(revision_tree)))
1752
source_stat = os.stat('source/file1')
1753
target2_stat = os.stat('target2/file1')
1754
self.assertNotEqual(source_stat, target2_stat)
1756
def test_build_tree_accelerator_tree_moved(self):
1757
source = self.make_branch_and_tree('source')
1758
self.build_tree_contents([('source/file1', 'A')])
1759
source.add(['file1'], ['file1-id'])
1760
source.commit('commit files')
1761
source.rename_one('file1', 'file2')
1763
self.addCleanup(source.unlock)
1764
target = self.make_branch_and_tree('target')
1765
revision_tree = source.basis_tree()
1766
revision_tree.lock_read()
1767
self.addCleanup(revision_tree.unlock)
1768
build_tree(revision_tree, target, source)
1770
self.addCleanup(target.unlock)
1771
self.assertEqual([], list(target.iter_changes(revision_tree)))
1773
def test_build_tree_hardlinks_preserve_execute(self):
1774
self.requireFeature(HardlinkFeature)
1775
source = self.create_ab_tree()
1776
tt = TreeTransform(source)
1777
trans_id = tt.trans_id_tree_file_id('file1-id')
1778
tt.set_executability(True, trans_id)
1780
self.assertTrue(source.is_executable('file1-id'))
1781
target = self.make_branch_and_tree('target')
1782
revision_tree = source.basis_tree()
1783
revision_tree.lock_read()
1784
self.addCleanup(revision_tree.unlock)
1785
build_tree(revision_tree, target, source, hardlink=True)
1787
self.addCleanup(target.unlock)
1788
self.assertEqual([], list(target.iter_changes(revision_tree)))
1789
self.assertTrue(source.is_executable('file1-id'))
1791
def test_case_insensitive_build_tree_inventory(self):
1792
source = self.make_branch_and_tree('source')
1793
self.build_tree(['source/file', 'source/FILE'])
1794
source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1795
source.commit('added files')
1796
# Don't try this at home, kids!
1797
# Force the tree to report that it is case insensitive
1798
target = self.make_branch_and_tree('target')
1799
target.case_sensitive = False
1800
build_tree(source.basis_tree(), target, source, delta_from_tree=True)
1801
self.assertEqual('file.moved', target.id2path('lower-id'))
1802
self.assertEqual('FILE', target.id2path('upper-id'))
732
1805
class MockTransform(object):
734
1807
def has_named_child(self, by_parent, parent_id, name):
758
1833
self.assertEqual(name, 'name.~1~')
759
1834
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
760
1835
self.assertEqual(name, 'name.~4~')
1838
class TestFileMover(tests.TestCaseWithTransport):
1840
def test_file_mover(self):
1841
self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
1842
mover = _FileMover()
1843
mover.rename('a', 'q')
1844
self.failUnlessExists('q')
1845
self.failIfExists('a')
1846
self.failUnlessExists('q/b')
1847
self.failUnlessExists('c')
1848
self.failUnlessExists('c/d')
1850
def test_pre_delete_rollback(self):
1851
self.build_tree(['a/'])
1852
mover = _FileMover()
1853
mover.pre_delete('a', 'q')
1854
self.failUnlessExists('q')
1855
self.failIfExists('a')
1857
self.failIfExists('q')
1858
self.failUnlessExists('a')
1860
def test_apply_deletions(self):
1861
self.build_tree(['a/', 'b/'])
1862
mover = _FileMover()
1863
mover.pre_delete('a', 'q')
1864
mover.pre_delete('b', 'r')
1865
self.failUnlessExists('q')
1866
self.failUnlessExists('r')
1867
self.failIfExists('a')
1868
self.failIfExists('b')
1869
mover.apply_deletions()
1870
self.failIfExists('q')
1871
self.failIfExists('r')
1872
self.failIfExists('a')
1873
self.failIfExists('b')
1875
def test_file_mover_rollback(self):
1876
self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
1877
mover = _FileMover()
1878
mover.rename('c/d', 'c/f')
1879
mover.rename('c/e', 'c/d')
1881
mover.rename('a', 'c')
1882
except errors.FileExists, e:
1884
self.failUnlessExists('a')
1885
self.failUnlessExists('c/d')
1888
class Bogus(Exception):
1892
class TestTransformRollback(tests.TestCaseWithTransport):
1894
class ExceptionFileMover(_FileMover):
1896
def __init__(self, bad_source=None, bad_target=None):
1897
_FileMover.__init__(self)
1898
self.bad_source = bad_source
1899
self.bad_target = bad_target
1901
def rename(self, source, target):
1902
if (self.bad_source is not None and
1903
source.endswith(self.bad_source)):
1905
elif (self.bad_target is not None and
1906
target.endswith(self.bad_target)):
1909
_FileMover.rename(self, source, target)
1911
def test_rollback_rename(self):
1912
tree = self.make_branch_and_tree('.')
1913
self.build_tree(['a/', 'a/b'])
1914
tt = TreeTransform(tree)
1915
self.addCleanup(tt.finalize)
1916
a_id = tt.trans_id_tree_path('a')
1917
tt.adjust_path('c', tt.root, a_id)
1918
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1919
self.assertRaises(Bogus, tt.apply,
1920
_mover=self.ExceptionFileMover(bad_source='a'))
1921
self.failUnlessExists('a')
1922
self.failUnlessExists('a/b')
1924
self.failUnlessExists('c')
1925
self.failUnlessExists('c/d')
1927
def test_rollback_rename_into_place(self):
1928
tree = self.make_branch_and_tree('.')
1929
self.build_tree(['a/', 'a/b'])
1930
tt = TreeTransform(tree)
1931
self.addCleanup(tt.finalize)
1932
a_id = tt.trans_id_tree_path('a')
1933
tt.adjust_path('c', tt.root, a_id)
1934
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1935
self.assertRaises(Bogus, tt.apply,
1936
_mover=self.ExceptionFileMover(bad_target='c/d'))
1937
self.failUnlessExists('a')
1938
self.failUnlessExists('a/b')
1940
self.failUnlessExists('c')
1941
self.failUnlessExists('c/d')
1943
def test_rollback_deletion(self):
1944
tree = self.make_branch_and_tree('.')
1945
self.build_tree(['a/', 'a/b'])
1946
tt = TreeTransform(tree)
1947
self.addCleanup(tt.finalize)
1948
a_id = tt.trans_id_tree_path('a')
1949
tt.delete_contents(a_id)
1950
tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
1951
self.assertRaises(Bogus, tt.apply,
1952
_mover=self.ExceptionFileMover(bad_target='d'))
1953
self.failUnlessExists('a')
1954
self.failUnlessExists('a/b')
1956
def test_resolve_no_parent(self):
1957
wt = self.make_branch_and_tree('.')
1958
tt = TreeTransform(wt)
1959
self.addCleanup(tt.finalize)
1960
parent = tt.trans_id_file_id('parent-id')
1961
tt.new_file('file', parent, 'Contents')
1962
resolve_conflicts(tt)
1965
class TestTransformPreview(tests.TestCaseWithTransport):
1967
def create_tree(self):
1968
tree = self.make_branch_and_tree('.')
1969
self.build_tree_contents([('a', 'content 1')])
1970
tree.add('a', 'a-id')
1971
tree.commit('rev1', rev_id='rev1')
1972
return tree.branch.repository.revision_tree('rev1')
1974
def get_empty_preview(self):
1975
repository = self.make_repository('repo')
1976
tree = repository.revision_tree(_mod_revision.NULL_REVISION)
1977
preview = TransformPreview(tree)
1978
self.addCleanup(preview.finalize)
1981
def test_transform_preview(self):
1982
revision_tree = self.create_tree()
1983
preview = TransformPreview(revision_tree)
1984
self.addCleanup(preview.finalize)
1986
def test_transform_preview_tree(self):
1987
revision_tree = self.create_tree()
1988
preview = TransformPreview(revision_tree)
1989
self.addCleanup(preview.finalize)
1990
preview.get_preview_tree()
1992
def test_transform_new_file(self):
1993
revision_tree = self.create_tree()
1994
preview = TransformPreview(revision_tree)
1995
self.addCleanup(preview.finalize)
1996
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
1997
preview_tree = preview.get_preview_tree()
1998
self.assertEqual(preview_tree.kind('file2-id'), 'file')
2000
preview_tree.get_file('file2-id').read(), 'content B\n')
2002
def test_diff_preview_tree(self):
2003
revision_tree = self.create_tree()
2004
preview = TransformPreview(revision_tree)
2005
self.addCleanup(preview.finalize)
2006
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2007
preview_tree = preview.get_preview_tree()
2009
show_diff_trees(revision_tree, preview_tree, out)
2010
lines = out.getvalue().splitlines()
2011
self.assertEqual(lines[0], "=== added file 'file2'")
2012
# 3 lines of diff administrivia
2013
self.assertEqual(lines[4], "+content B")
2015
def test_transform_conflicts(self):
2016
revision_tree = self.create_tree()
2017
preview = TransformPreview(revision_tree)
2018
self.addCleanup(preview.finalize)
2019
preview.new_file('a', preview.root, 'content 2')
2020
resolve_conflicts(preview)
2021
trans_id = preview.trans_id_file_id('a-id')
2022
self.assertEqual('a.moved', preview.final_name(trans_id))
2024
def get_tree_and_preview_tree(self):
2025
revision_tree = self.create_tree()
2026
preview = TransformPreview(revision_tree)
2027
self.addCleanup(preview.finalize)
2028
a_trans_id = preview.trans_id_file_id('a-id')
2029
preview.delete_contents(a_trans_id)
2030
preview.create_file('b content', a_trans_id)
2031
preview_tree = preview.get_preview_tree()
2032
return revision_tree, preview_tree
2034
def test_iter_changes(self):
2035
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2036
root = revision_tree.inventory.root.file_id
2037
self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
2038
(root, root), ('a', 'a'), ('file', 'file'),
2040
list(preview_tree.iter_changes(revision_tree)))
2042
def test_wrong_tree_value_error(self):
2043
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2044
e = self.assertRaises(ValueError, preview_tree.iter_changes,
2046
self.assertEqual('from_tree must be transform source tree.', str(e))
2048
def test_include_unchanged_value_error(self):
2049
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2050
e = self.assertRaises(ValueError, preview_tree.iter_changes,
2051
revision_tree, include_unchanged=True)
2052
self.assertEqual('include_unchanged is not supported', str(e))
2054
def test_specific_files(self):
2055
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2056
e = self.assertRaises(ValueError, preview_tree.iter_changes,
2057
revision_tree, specific_files=['pete'])
2058
self.assertEqual('specific_files is not supported', str(e))
2060
def test_want_unversioned_value_error(self):
2061
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2062
e = self.assertRaises(ValueError, preview_tree.iter_changes,
2063
revision_tree, want_unversioned=True)
2064
self.assertEqual('want_unversioned is not supported', str(e))
2066
def test_ignore_extra_trees_no_specific_files(self):
2067
# extra_trees is harmless without specific_files, so we'll silently
2068
# accept it, even though we won't use it.
2069
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2070
preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
2072
def test_ignore_require_versioned_no_specific_files(self):
2073
# require_versioned is meaningless without specific_files.
2074
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2075
preview_tree.iter_changes(revision_tree, require_versioned=False)
2077
def test_ignore_pb(self):
2078
# pb could be supported, but TT.iter_changes doesn't support it.
2079
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2080
preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
2082
def test_kind(self):
2083
revision_tree = self.create_tree()
2084
preview = TransformPreview(revision_tree)
2085
self.addCleanup(preview.finalize)
2086
preview.new_file('file', preview.root, 'contents', 'file-id')
2087
preview.new_directory('directory', preview.root, 'dir-id')
2088
preview_tree = preview.get_preview_tree()
2089
self.assertEqual('file', preview_tree.kind('file-id'))
2090
self.assertEqual('directory', preview_tree.kind('dir-id'))
2092
def test_get_file_mtime(self):
2093
preview = self.get_empty_preview()
2094
file_trans_id = preview.new_file('file', preview.root, 'contents',
2096
limbo_path = preview._limbo_name(file_trans_id)
2097
preview_tree = preview.get_preview_tree()
2098
self.assertEqual(os.stat(limbo_path).st_mtime,
2099
preview_tree.get_file_mtime('file-id'))
2101
def test_get_file(self):
2102
preview = self.get_empty_preview()
2103
preview.new_file('file', preview.root, 'contents', 'file-id')
2104
preview_tree = preview.get_preview_tree()
2105
tree_file = preview_tree.get_file('file-id')
2107
self.assertEqual('contents', tree_file.read())
2111
def test_get_symlink_target(self):
2112
self.requireFeature(SymlinkFeature)
2113
preview = self.get_empty_preview()
2114
preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
2115
preview_tree = preview.get_preview_tree()
2116
self.assertEqual('target',
2117
preview_tree.get_symlink_target('symlink-id'))
2119
def test_all_file_ids(self):
2120
tree = self.make_branch_and_tree('tree')
2121
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
2122
tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
2123
preview = TransformPreview(tree)
2124
self.addCleanup(preview.finalize)
2125
preview.unversion_file(preview.trans_id_file_id('b-id'))
2126
c_trans_id = preview.trans_id_file_id('c-id')
2127
preview.unversion_file(c_trans_id)
2128
preview.version_file('c-id', c_trans_id)
2129
preview_tree = preview.get_preview_tree()
2130
self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
2131
preview_tree.all_file_ids())
2133
def test_path2id_deleted_unchanged(self):
2134
tree = self.make_branch_and_tree('tree')
2135
self.build_tree(['tree/unchanged', 'tree/deleted'])
2136
tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
2137
preview = TransformPreview(tree)
2138
self.addCleanup(preview.finalize)
2139
preview.unversion_file(preview.trans_id_file_id('deleted-id'))
2140
preview_tree = preview.get_preview_tree()
2141
self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
2142
self.assertIs(None, preview_tree.path2id('deleted'))
2144
def test_path2id_created(self):
2145
tree = self.make_branch_and_tree('tree')
2146
self.build_tree(['tree/unchanged'])
2147
tree.add(['unchanged'], ['unchanged-id'])
2148
preview = TransformPreview(tree)
2149
self.addCleanup(preview.finalize)
2150
preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
2151
'contents', 'new-id')
2152
preview_tree = preview.get_preview_tree()
2153
self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
2155
def test_path2id_moved(self):
2156
tree = self.make_branch_and_tree('tree')
2157
self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
2158
tree.add(['old_parent', 'old_parent/child'],
2159
['old_parent-id', 'child-id'])
2160
preview = TransformPreview(tree)
2161
self.addCleanup(preview.finalize)
2162
new_parent = preview.new_directory('new_parent', preview.root,
2164
preview.adjust_path('child', new_parent,
2165
preview.trans_id_file_id('child-id'))
2166
preview_tree = preview.get_preview_tree()
2167
self.assertIs(None, preview_tree.path2id('old_parent/child'))
2168
self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
2170
def test_path2id_renamed_parent(self):
2171
tree = self.make_branch_and_tree('tree')
2172
self.build_tree(['tree/old_name/', 'tree/old_name/child'])
2173
tree.add(['old_name', 'old_name/child'],
2174
['parent-id', 'child-id'])
2175
preview = TransformPreview(tree)
2176
self.addCleanup(preview.finalize)
2177
preview.adjust_path('new_name', preview.root,
2178
preview.trans_id_file_id('parent-id'))
2179
preview_tree = preview.get_preview_tree()
2180
self.assertIs(None, preview_tree.path2id('old_name/child'))
2181
self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
2183
def assertMatchingIterEntries(self, tt, specific_file_ids=None):
2184
preview_tree = tt.get_preview_tree()
2185
preview_result = list(preview_tree.iter_entries_by_dir(
2189
actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
2190
self.assertEqual(actual_result, preview_result)
2192
def test_iter_entries_by_dir_new(self):
2193
tree = self.make_branch_and_tree('tree')
2194
tt = TreeTransform(tree)
2195
tt.new_file('new', tt.root, 'contents', 'new-id')
2196
self.assertMatchingIterEntries(tt)
2198
def test_iter_entries_by_dir_deleted(self):
2199
tree = self.make_branch_and_tree('tree')
2200
self.build_tree(['tree/deleted'])
2201
tree.add('deleted', 'deleted-id')
2202
tt = TreeTransform(tree)
2203
tt.delete_contents(tt.trans_id_file_id('deleted-id'))
2204
self.assertMatchingIterEntries(tt)
2206
def test_iter_entries_by_dir_unversioned(self):
2207
tree = self.make_branch_and_tree('tree')
2208
self.build_tree(['tree/removed'])
2209
tree.add('removed', 'removed-id')
2210
tt = TreeTransform(tree)
2211
tt.unversion_file(tt.trans_id_file_id('removed-id'))
2212
self.assertMatchingIterEntries(tt)
2214
def test_iter_entries_by_dir_moved(self):
2215
tree = self.make_branch_and_tree('tree')
2216
self.build_tree(['tree/moved', 'tree/new_parent/'])
2217
tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
2218
tt = TreeTransform(tree)
2219
tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
2220
tt.trans_id_file_id('moved-id'))
2221
self.assertMatchingIterEntries(tt)
2223
def test_iter_entries_by_dir_specific_file_ids(self):
2224
tree = self.make_branch_and_tree('tree')
2225
tree.set_root_id('tree-root-id')
2226
self.build_tree(['tree/parent/', 'tree/parent/child'])
2227
tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
2228
tt = TreeTransform(tree)
2229
self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
2231
def test_symlink_content_summary(self):
2232
self.requireFeature(SymlinkFeature)
2233
preview = self.get_empty_preview()
2234
preview.new_symlink('path', preview.root, 'target', 'path-id')
2235
summary = preview.get_preview_tree().path_content_summary('path')
2236
self.assertEqual(('symlink', None, None, 'target'), summary)
2238
def test_missing_content_summary(self):
2239
preview = self.get_empty_preview()
2240
summary = preview.get_preview_tree().path_content_summary('path')
2241
self.assertEqual(('missing', None, None, None), summary)
2243
def test_deleted_content_summary(self):
2244
tree = self.make_branch_and_tree('tree')
2245
self.build_tree(['tree/path/'])
2247
preview = TransformPreview(tree)
2248
self.addCleanup(preview.finalize)
2249
preview.delete_contents(preview.trans_id_tree_path('path'))
2250
summary = preview.get_preview_tree().path_content_summary('path')
2251
self.assertEqual(('missing', None, None, None), summary)
2253
def test_file_content_summary_executable(self):
2254
if not osutils.supports_executable():
2255
raise TestNotApplicable()
2256
preview = self.get_empty_preview()
2257
path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2258
preview.set_executability(True, path_id)
2259
summary = preview.get_preview_tree().path_content_summary('path')
2260
self.assertEqual(4, len(summary))
2261
self.assertEqual('file', summary[0])
2262
# size must be known
2263
self.assertEqual(len('contents'), summary[1])
2265
self.assertEqual(True, summary[2])
2266
# will not have hash (not cheap to determine)
2267
self.assertIs(None, summary[3])
2269
def test_change_executability(self):
2270
if not osutils.supports_executable():
2271
raise TestNotApplicable()
2272
tree = self.make_branch_and_tree('tree')
2273
self.build_tree(['tree/path'])
2275
preview = TransformPreview(tree)
2276
self.addCleanup(preview.finalize)
2277
path_id = preview.trans_id_tree_path('path')
2278
preview.set_executability(True, path_id)
2279
summary = preview.get_preview_tree().path_content_summary('path')
2280
self.assertEqual(True, summary[2])
2282
def test_file_content_summary_non_exec(self):
2283
preview = self.get_empty_preview()
2284
preview.new_file('path', preview.root, 'contents', 'path-id')
2285
summary = preview.get_preview_tree().path_content_summary('path')
2286
self.assertEqual(4, len(summary))
2287
self.assertEqual('file', summary[0])
2288
# size must be known
2289
self.assertEqual(len('contents'), summary[1])
2291
if osutils.supports_executable():
2292
self.assertEqual(False, summary[2])
2294
self.assertEqual(None, summary[2])
2295
# will not have hash (not cheap to determine)
2296
self.assertIs(None, summary[3])
2298
def test_dir_content_summary(self):
2299
preview = self.get_empty_preview()
2300
preview.new_directory('path', preview.root, 'path-id')
2301
summary = preview.get_preview_tree().path_content_summary('path')
2302
self.assertEqual(('directory', None, None, None), summary)
2304
def test_tree_content_summary(self):
2305
preview = self.get_empty_preview()
2306
path = preview.new_directory('path', preview.root, 'path-id')
2307
preview.set_tree_reference('rev-1', path)
2308
summary = preview.get_preview_tree().path_content_summary('path')
2309
self.assertEqual(4, len(summary))
2310
self.assertEqual('tree-reference', summary[0])
2312
def test_annotate(self):
2313
tree = self.make_branch_and_tree('tree')
2314
self.build_tree_contents([('tree/file', 'a\n')])
2315
tree.add('file', 'file-id')
2316
tree.commit('a', rev_id='one')
2317
self.build_tree_contents([('tree/file', 'a\nb\n')])
2318
preview = TransformPreview(tree)
2319
self.addCleanup(preview.finalize)
2320
file_trans_id = preview.trans_id_file_id('file-id')
2321
preview.delete_contents(file_trans_id)
2322
preview.create_file('a\nb\nc\n', file_trans_id)
2323
preview_tree = preview.get_preview_tree()
2329
annotation = preview_tree.annotate_iter('file-id', 'me:')
2330
self.assertEqual(expected, annotation)
2332
def test_annotate_missing(self):
2333
preview = self.get_empty_preview()
2334
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2335
preview_tree = preview.get_preview_tree()
2341
annotation = preview_tree.annotate_iter('file-id', 'me:')
2342
self.assertEqual(expected, annotation)
2344
def test_annotate_rename(self):
2345
tree = self.make_branch_and_tree('tree')
2346
self.build_tree_contents([('tree/file', 'a\n')])
2347
tree.add('file', 'file-id')
2348
tree.commit('a', rev_id='one')
2349
preview = TransformPreview(tree)
2350
self.addCleanup(preview.finalize)
2351
file_trans_id = preview.trans_id_file_id('file-id')
2352
preview.adjust_path('newname', preview.root, file_trans_id)
2353
preview_tree = preview.get_preview_tree()
2357
annotation = preview_tree.annotate_iter('file-id', 'me:')
2358
self.assertEqual(expected, annotation)
2360
def test_annotate_deleted(self):
2361
tree = self.make_branch_and_tree('tree')
2362
self.build_tree_contents([('tree/file', 'a\n')])
2363
tree.add('file', 'file-id')
2364
tree.commit('a', rev_id='one')
2365
self.build_tree_contents([('tree/file', 'a\nb\n')])
2366
preview = TransformPreview(tree)
2367
self.addCleanup(preview.finalize)
2368
file_trans_id = preview.trans_id_file_id('file-id')
2369
preview.delete_contents(file_trans_id)
2370
preview_tree = preview.get_preview_tree()
2371
annotation = preview_tree.annotate_iter('file-id', 'me:')
2372
self.assertIs(None, annotation)
2374
def test_stored_kind(self):
2375
preview = self.get_empty_preview()
2376
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2377
preview_tree = preview.get_preview_tree()
2378
self.assertEqual('file', preview_tree.stored_kind('file-id'))
2380
def test_is_executable(self):
2381
preview = self.get_empty_preview()
2382
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2383
preview.set_executability(True, preview.trans_id_file_id('file-id'))
2384
preview_tree = preview.get_preview_tree()
2385
self.assertEqual(True, preview_tree.is_executable('file-id'))
2387
def test_get_set_parent_ids(self):
2388
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2389
self.assertEqual([], preview_tree.get_parent_ids())
2390
preview_tree.set_parent_ids(['rev-1'])
2391
self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
2393
def test_plan_file_merge(self):
2394
work_a = self.make_branch_and_tree('wta')
2395
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2396
work_a.add('file', 'file-id')
2397
base_id = work_a.commit('base version')
2398
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2399
preview = TransformPreview(work_a)
2400
self.addCleanup(preview.finalize)
2401
trans_id = preview.trans_id_file_id('file-id')
2402
preview.delete_contents(trans_id)
2403
preview.create_file('b\nc\nd\ne\n', trans_id)
2404
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2405
tree_a = preview.get_preview_tree()
2406
tree_a.set_parent_ids([base_id])
2408
('killed-a', 'a\n'),
2409
('killed-b', 'b\n'),
2410
('unchanged', 'c\n'),
2411
('unchanged', 'd\n'),
2414
], list(tree_a.plan_file_merge('file-id', tree_b)))
2416
def test_plan_file_merge_revision_tree(self):
2417
work_a = self.make_branch_and_tree('wta')
2418
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2419
work_a.add('file', 'file-id')
2420
base_id = work_a.commit('base version')
2421
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2422
preview = TransformPreview(work_a.basis_tree())
2423
self.addCleanup(preview.finalize)
2424
trans_id = preview.trans_id_file_id('file-id')
2425
preview.delete_contents(trans_id)
2426
preview.create_file('b\nc\nd\ne\n', trans_id)
2427
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2428
tree_a = preview.get_preview_tree()
2429
tree_a.set_parent_ids([base_id])
2431
('killed-a', 'a\n'),
2432
('killed-b', 'b\n'),
2433
('unchanged', 'c\n'),
2434
('unchanged', 'd\n'),
2437
], list(tree_a.plan_file_merge('file-id', tree_b)))