185
255
transform3.delete_contents(oz_id)
186
256
self.assertEqual(transform3.find_conflicts(),
187
257
[('missing parent', oz_id)])
188
root_id = transform3.trans_id_tree_file_id('TREE_ROOT')
258
root_id = transform3.root
189
259
tip_id = transform3.trans_id_tree_file_id('tip-id')
190
260
transform3.adjust_path('tip', root_id, tip_id)
191
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')
193
371
def test_add_del(self):
194
372
start, root = self.get_transform()
195
373
start.new_directory('a', root, 'a')
479
747
rename.set_executability(True, myfile)
482
def test_find_interesting(self):
483
create, root = self.get_transform()
485
create.new_file('vfile', root, 'myfile-text', 'myfile-id')
486
create.new_file('uvfile', root, 'othertext')
488
self.assertEqual(find_interesting(wt, wt, ['vfile']),
490
self.assertRaises(NotVersionedError, find_interesting, wt, wt,
750
def test_set_executability_order(self):
751
"""Ensure that executability behaves the same, no matter what order.
753
- create file and set executability simultaneously
754
- create file and set executability afterward
755
- unsetting the executability of a file whose executability has not been
756
declared should throw an exception (this may happen when a
757
merge attempts to create a file with a duplicate ID)
759
transform, root = self.get_transform()
762
self.addCleanup(wt.unlock)
763
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
765
sac = transform.new_file('set_after_creation', root,
766
'Set after creation', 'sac')
767
transform.set_executability(True, sac)
768
uws = transform.new_file('unset_without_set', root, 'Unset badly',
770
self.assertRaises(KeyError, transform.set_executability, None, uws)
772
self.assertTrue(wt.is_executable('soc'))
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_rollback_on_directory_clash(self):
1167
wt = self.make_branch_and_tree('.')
1168
tt = TreeTransform(wt) # TreeTransform obtains write lock
1170
foo = tt.new_directory('foo', tt.root)
1171
tt.new_file('bar', foo, 'foobar')
1172
baz = tt.new_directory('baz', tt.root)
1173
tt.new_file('qux', baz, 'quux')
1174
# Ask for a rename 'foo' -> 'baz'
1175
tt.adjust_path('baz', tt.root, foo)
1176
# Lie to tt that we've already resolved all conflicts.
1177
tt.apply(no_conflicts=True)
1181
# The rename will fail because the target directory is not empty (but
1182
# raises FileExists anyway).
1183
err = self.assertRaises(errors.FileExists, tt_helper)
1184
self.assertContainsRe(str(err),
1185
"^File exists: .+/baz")
1187
def test_two_directories_clash(self):
1189
wt = self.make_branch_and_tree('.')
1190
tt = TreeTransform(wt) # TreeTransform obtains write lock
1192
foo_1 = tt.new_directory('foo', tt.root)
1193
tt.new_directory('bar', foo_1)
1194
# Adding the same directory with a different content
1195
foo_2 = tt.new_directory('foo', tt.root)
1196
tt.new_directory('baz', foo_2)
1197
# Lie to tt that we've already resolved all conflicts.
1198
tt.apply(no_conflicts=True)
1202
err = self.assertRaises(errors.FileExists, tt_helper)
1203
self.assertContainsRe(str(err),
1204
"^File exists: .+/foo")
1206
def test_two_directories_clash_finalize(self):
1208
wt = self.make_branch_and_tree('.')
1209
tt = TreeTransform(wt) # TreeTransform obtains write lock
1211
foo_1 = tt.new_directory('foo', tt.root)
1212
tt.new_directory('bar', foo_1)
1213
# Adding the same directory with a different content
1214
foo_2 = tt.new_directory('foo', tt.root)
1215
tt.new_directory('baz', foo_2)
1216
# Lie to tt that we've already resolved all conflicts.
1217
tt.apply(no_conflicts=True)
1221
err = self.assertRaises(errors.FileExists, tt_helper)
1222
self.assertContainsRe(str(err),
1223
"^File exists: .+/foo")
1225
def test_file_to_directory(self):
1226
wt = self.make_branch_and_tree('.')
1227
self.build_tree(['foo'])
1230
tt = TreeTransform(wt)
1231
self.addCleanup(tt.finalize)
1232
foo_trans_id = tt.trans_id_tree_path("foo")
1233
tt.delete_contents(foo_trans_id)
1234
tt.create_directory(foo_trans_id)
1235
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1236
tt.create_file(["aa\n"], bar_trans_id)
1237
tt.version_file("bar-1", bar_trans_id)
1239
self.failUnlessExists("foo/bar")
1242
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1247
changes = wt.changes_from(wt.basis_tree())
1248
self.assertFalse(changes.has_changed(), changes)
1250
def test_file_to_symlink(self):
1251
self.requireFeature(SymlinkFeature)
1252
wt = self.make_branch_and_tree('.')
1253
self.build_tree(['foo'])
1256
tt = TreeTransform(wt)
1257
self.addCleanup(tt.finalize)
1258
foo_trans_id = tt.trans_id_tree_path("foo")
1259
tt.delete_contents(foo_trans_id)
1260
tt.create_symlink("bar", foo_trans_id)
1262
self.failUnlessExists("foo")
1264
self.addCleanup(wt.unlock)
1265
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1268
def test_dir_to_file(self):
1269
wt = self.make_branch_and_tree('.')
1270
self.build_tree(['foo/', 'foo/bar'])
1271
wt.add(['foo', 'foo/bar'])
1273
tt = TreeTransform(wt)
1274
self.addCleanup(tt.finalize)
1275
foo_trans_id = tt.trans_id_tree_path("foo")
1276
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1277
tt.delete_contents(foo_trans_id)
1278
tt.delete_versioned(bar_trans_id)
1279
tt.create_file(["aa\n"], foo_trans_id)
1281
self.failUnlessExists("foo")
1283
self.addCleanup(wt.unlock)
1284
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1287
def test_dir_to_hardlink(self):
1288
self.requireFeature(HardlinkFeature)
1289
wt = self.make_branch_and_tree('.')
1290
self.build_tree(['foo/', 'foo/bar'])
1291
wt.add(['foo', 'foo/bar'])
1293
tt = TreeTransform(wt)
1294
self.addCleanup(tt.finalize)
1295
foo_trans_id = tt.trans_id_tree_path("foo")
1296
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1297
tt.delete_contents(foo_trans_id)
1298
tt.delete_versioned(bar_trans_id)
1299
self.build_tree(['baz'])
1300
tt.create_hardlink("baz", foo_trans_id)
1302
self.failUnlessExists("foo")
1303
self.failUnlessExists("baz")
1305
self.addCleanup(wt.unlock)
1306
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1309
def test_no_final_path(self):
1310
transform, root = self.get_transform()
1311
trans_id = transform.trans_id_file_id('foo')
1312
transform.create_file('bar', trans_id)
1313
transform.cancel_creation(trans_id)
494
1317
class TransformGroup(object):
495
def __init__(self, dirname):
1319
def __init__(self, dirname, root_id):
496
1320
self.name = dirname
497
1321
os.mkdir(dirname)
498
1322
self.wt = BzrDir.create_standalone_workingtree(dirname)
1323
self.wt.set_root_id(root_id)
499
1324
self.b = self.wt.branch
500
1325
self.tt = TreeTransform(self.wt)
501
1326
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
503
1329
def conflict_text(tree, merge):
504
1330
template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
505
1331
return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
508
1334
class TestTransformMerge(TestCaseInTempDir):
509
1336
def test_text_merge(self):
510
base = TransformGroup("base")
1337
root_id = generate_ids.gen_root_id()
1338
base = TransformGroup("base", root_id)
511
1339
base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
512
1340
base.tt.new_file('b', base.root, 'b1', 'b')
513
1341
base.tt.new_file('c', base.root, 'c', 'c')
698
1530
a.add(['foo', 'foo/bar', 'foo/baz'])
699
1531
a.commit('initial commit')
700
1532
b = BzrDir.create_standalone_workingtree('b')
701
build_tree(a.basis_tree(), b)
1533
basis = a.basis_tree()
1535
self.addCleanup(basis.unlock)
1536
build_tree(basis, b)
702
1537
self.assertIs(os.path.isdir('b/foo'), True)
703
1538
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
704
1539
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1541
def test_build_with_references(self):
1542
tree = self.make_branch_and_tree('source',
1543
format='dirstate-with-subtree')
1544
subtree = self.make_branch_and_tree('source/subtree',
1545
format='dirstate-with-subtree')
1546
tree.add_reference(subtree)
1547
tree.commit('a revision')
1548
tree.branch.create_checkout('target')
1549
self.failUnlessExists('target')
1550
self.failUnlessExists('target/subtree')
1552
def test_file_conflict_handling(self):
1553
"""Ensure that when building trees, conflict handling is done"""
1554
source = self.make_branch_and_tree('source')
1555
target = self.make_branch_and_tree('target')
1556
self.build_tree(['source/file', 'target/file'])
1557
source.add('file', 'new-file')
1558
source.commit('added file')
1559
build_tree(source.basis_tree(), target)
1560
self.assertEqual([DuplicateEntry('Moved existing file to',
1561
'file.moved', 'file', None, 'new-file')],
1563
target2 = self.make_branch_and_tree('target2')
1564
target_file = file('target2/file', 'wb')
1566
source_file = file('source/file', 'rb')
1568
target_file.write(source_file.read())
1573
build_tree(source.basis_tree(), target2)
1574
self.assertEqual([], target2.conflicts())
1576
def test_symlink_conflict_handling(self):
1577
"""Ensure that when building trees, conflict handling is done"""
1578
self.requireFeature(SymlinkFeature)
1579
source = self.make_branch_and_tree('source')
1580
os.symlink('foo', 'source/symlink')
1581
source.add('symlink', 'new-symlink')
1582
source.commit('added file')
1583
target = self.make_branch_and_tree('target')
1584
os.symlink('bar', 'target/symlink')
1585
build_tree(source.basis_tree(), target)
1586
self.assertEqual([DuplicateEntry('Moved existing file to',
1587
'symlink.moved', 'symlink', None, 'new-symlink')],
1589
target = self.make_branch_and_tree('target2')
1590
os.symlink('foo', 'target2/symlink')
1591
build_tree(source.basis_tree(), target)
1592
self.assertEqual([], target.conflicts())
1594
def test_directory_conflict_handling(self):
1595
"""Ensure that when building trees, conflict handling is done"""
1596
source = self.make_branch_and_tree('source')
1597
target = self.make_branch_and_tree('target')
1598
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1599
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1600
source.commit('added file')
1601
build_tree(source.basis_tree(), target)
1602
self.assertEqual([], target.conflicts())
1603
self.failUnlessExists('target/dir1/file')
1605
# Ensure contents are merged
1606
target = self.make_branch_and_tree('target2')
1607
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1608
build_tree(source.basis_tree(), target)
1609
self.assertEqual([], target.conflicts())
1610
self.failUnlessExists('target2/dir1/file2')
1611
self.failUnlessExists('target2/dir1/file')
1613
# Ensure new contents are suppressed for existing branches
1614
target = self.make_branch_and_tree('target3')
1615
self.make_branch('target3/dir1')
1616
self.build_tree(['target3/dir1/file2'])
1617
build_tree(source.basis_tree(), target)
1618
self.failIfExists('target3/dir1/file')
1619
self.failUnlessExists('target3/dir1/file2')
1620
self.failUnlessExists('target3/dir1.diverted/file')
1621
self.assertEqual([DuplicateEntry('Diverted to',
1622
'dir1.diverted', 'dir1', 'new-dir1', None)],
1625
target = self.make_branch_and_tree('target4')
1626
self.build_tree(['target4/dir1/'])
1627
self.make_branch('target4/dir1/file')
1628
build_tree(source.basis_tree(), target)
1629
self.failUnlessExists('target4/dir1/file')
1630
self.assertEqual('directory', file_kind('target4/dir1/file'))
1631
self.failUnlessExists('target4/dir1/file.diverted')
1632
self.assertEqual([DuplicateEntry('Diverted to',
1633
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1636
def test_mixed_conflict_handling(self):
1637
"""Ensure that when building trees, conflict handling is done"""
1638
source = self.make_branch_and_tree('source')
1639
target = self.make_branch_and_tree('target')
1640
self.build_tree(['source/name', 'target/name/'])
1641
source.add('name', 'new-name')
1642
source.commit('added file')
1643
build_tree(source.basis_tree(), target)
1644
self.assertEqual([DuplicateEntry('Moved existing file to',
1645
'name.moved', 'name', None, 'new-name')], target.conflicts())
1647
def test_raises_in_populated(self):
1648
source = self.make_branch_and_tree('source')
1649
self.build_tree(['source/name'])
1651
source.commit('added name')
1652
target = self.make_branch_and_tree('target')
1653
self.build_tree(['target/name'])
1655
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1656
build_tree, source.basis_tree(), target)
1658
def test_build_tree_rename_count(self):
1659
source = self.make_branch_and_tree('source')
1660
self.build_tree(['source/file1', 'source/dir1/'])
1661
source.add(['file1', 'dir1'])
1662
source.commit('add1')
1663
target1 = self.make_branch_and_tree('target1')
1664
transform_result = build_tree(source.basis_tree(), target1)
1665
self.assertEqual(2, transform_result.rename_count)
1667
self.build_tree(['source/dir1/file2'])
1668
source.add(['dir1/file2'])
1669
source.commit('add3')
1670
target2 = self.make_branch_and_tree('target2')
1671
transform_result = build_tree(source.basis_tree(), target2)
1672
# children of non-root directories should not be renamed
1673
self.assertEqual(2, transform_result.rename_count)
1675
def create_ab_tree(self):
1676
"""Create a committed test tree with two files"""
1677
source = self.make_branch_and_tree('source')
1678
self.build_tree_contents([('source/file1', 'A')])
1679
self.build_tree_contents([('source/file2', 'B')])
1680
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1681
source.commit('commit files')
1683
self.addCleanup(source.unlock)
1686
def test_build_tree_accelerator_tree(self):
1687
source = self.create_ab_tree()
1688
self.build_tree_contents([('source/file2', 'C')])
1690
real_source_get_file = source.get_file
1691
def get_file(file_id, path=None):
1692
calls.append(file_id)
1693
return real_source_get_file(file_id, path)
1694
source.get_file = get_file
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)
1700
self.assertEqual(['file1-id'], calls)
1702
self.addCleanup(target.unlock)
1703
self.assertEqual([], list(target.iter_changes(revision_tree)))
1705
def test_build_tree_accelerator_tree_missing_file(self):
1706
source = self.create_ab_tree()
1707
os.unlink('source/file1')
1708
source.remove(['file2'])
1709
target = self.make_branch_and_tree('target')
1710
revision_tree = source.basis_tree()
1711
revision_tree.lock_read()
1712
self.addCleanup(revision_tree.unlock)
1713
build_tree(revision_tree, target, source)
1715
self.addCleanup(target.unlock)
1716
self.assertEqual([], list(target.iter_changes(revision_tree)))
1718
def test_build_tree_accelerator_wrong_kind(self):
1719
self.requireFeature(SymlinkFeature)
1720
source = self.make_branch_and_tree('source')
1721
self.build_tree_contents([('source/file1', '')])
1722
self.build_tree_contents([('source/file2', '')])
1723
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1724
source.commit('commit files')
1725
os.unlink('source/file2')
1726
self.build_tree_contents([('source/file2/', 'C')])
1727
os.unlink('source/file1')
1728
os.symlink('file2', 'source/file1')
1730
real_source_get_file = source.get_file
1731
def get_file(file_id, path=None):
1732
calls.append(file_id)
1733
return real_source_get_file(file_id, path)
1734
source.get_file = get_file
1735
target = self.make_branch_and_tree('target')
1736
revision_tree = source.basis_tree()
1737
revision_tree.lock_read()
1738
self.addCleanup(revision_tree.unlock)
1739
build_tree(revision_tree, target, source)
1740
self.assertEqual([], calls)
1742
self.addCleanup(target.unlock)
1743
self.assertEqual([], list(target.iter_changes(revision_tree)))
1745
def test_build_tree_hardlink(self):
1746
self.requireFeature(HardlinkFeature)
1747
source = self.create_ab_tree()
1748
target = self.make_branch_and_tree('target')
1749
revision_tree = source.basis_tree()
1750
revision_tree.lock_read()
1751
self.addCleanup(revision_tree.unlock)
1752
build_tree(revision_tree, target, source, hardlink=True)
1754
self.addCleanup(target.unlock)
1755
self.assertEqual([], list(target.iter_changes(revision_tree)))
1756
source_stat = os.stat('source/file1')
1757
target_stat = os.stat('target/file1')
1758
self.assertEqual(source_stat, target_stat)
1760
# Explicitly disallowing hardlinks should prevent them.
1761
target2 = self.make_branch_and_tree('target2')
1762
build_tree(revision_tree, target2, source, hardlink=False)
1764
self.addCleanup(target2.unlock)
1765
self.assertEqual([], list(target2.iter_changes(revision_tree)))
1766
source_stat = os.stat('source/file1')
1767
target2_stat = os.stat('target2/file1')
1768
self.assertNotEqual(source_stat, target2_stat)
1770
def test_build_tree_accelerator_tree_moved(self):
1771
source = self.make_branch_and_tree('source')
1772
self.build_tree_contents([('source/file1', 'A')])
1773
source.add(['file1'], ['file1-id'])
1774
source.commit('commit files')
1775
source.rename_one('file1', 'file2')
1777
self.addCleanup(source.unlock)
1778
target = self.make_branch_and_tree('target')
1779
revision_tree = source.basis_tree()
1780
revision_tree.lock_read()
1781
self.addCleanup(revision_tree.unlock)
1782
build_tree(revision_tree, target, source)
1784
self.addCleanup(target.unlock)
1785
self.assertEqual([], list(target.iter_changes(revision_tree)))
1787
def test_build_tree_hardlinks_preserve_execute(self):
1788
self.requireFeature(HardlinkFeature)
1789
source = self.create_ab_tree()
1790
tt = TreeTransform(source)
1791
trans_id = tt.trans_id_tree_file_id('file1-id')
1792
tt.set_executability(True, trans_id)
1794
self.assertTrue(source.is_executable('file1-id'))
1795
target = self.make_branch_and_tree('target')
1796
revision_tree = source.basis_tree()
1797
revision_tree.lock_read()
1798
self.addCleanup(revision_tree.unlock)
1799
build_tree(revision_tree, target, source, hardlink=True)
1801
self.addCleanup(target.unlock)
1802
self.assertEqual([], list(target.iter_changes(revision_tree)))
1803
self.assertTrue(source.is_executable('file1-id'))
1805
def test_case_insensitive_build_tree_inventory(self):
1806
source = self.make_branch_and_tree('source')
1807
self.build_tree(['source/file', 'source/FILE'])
1808
source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1809
source.commit('added files')
1810
# Don't try this at home, kids!
1811
# Force the tree to report that it is case insensitive
1812
target = self.make_branch_and_tree('target')
1813
target.case_sensitive = False
1814
build_tree(source.basis_tree(), target, source, delta_from_tree=True)
1815
self.assertEqual('file.moved', target.id2path('lower-id'))
1816
self.assertEqual('FILE', target.id2path('upper-id'))
706
1819
class MockTransform(object):
708
1821
def has_named_child(self, by_parent, parent_id, name):
732
1847
self.assertEqual(name, 'name.~1~')
733
1848
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
734
1849
self.assertEqual(name, 'name.~4~')
1852
class TestFileMover(tests.TestCaseWithTransport):
1854
def test_file_mover(self):
1855
self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
1856
mover = _FileMover()
1857
mover.rename('a', 'q')
1858
self.failUnlessExists('q')
1859
self.failIfExists('a')
1860
self.failUnlessExists('q/b')
1861
self.failUnlessExists('c')
1862
self.failUnlessExists('c/d')
1864
def test_pre_delete_rollback(self):
1865
self.build_tree(['a/'])
1866
mover = _FileMover()
1867
mover.pre_delete('a', 'q')
1868
self.failUnlessExists('q')
1869
self.failIfExists('a')
1871
self.failIfExists('q')
1872
self.failUnlessExists('a')
1874
def test_apply_deletions(self):
1875
self.build_tree(['a/', 'b/'])
1876
mover = _FileMover()
1877
mover.pre_delete('a', 'q')
1878
mover.pre_delete('b', 'r')
1879
self.failUnlessExists('q')
1880
self.failUnlessExists('r')
1881
self.failIfExists('a')
1882
self.failIfExists('b')
1883
mover.apply_deletions()
1884
self.failIfExists('q')
1885
self.failIfExists('r')
1886
self.failIfExists('a')
1887
self.failIfExists('b')
1889
def test_file_mover_rollback(self):
1890
self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
1891
mover = _FileMover()
1892
mover.rename('c/d', 'c/f')
1893
mover.rename('c/e', 'c/d')
1895
mover.rename('a', 'c')
1896
except errors.FileExists, e:
1898
self.failUnlessExists('a')
1899
self.failUnlessExists('c/d')
1902
class Bogus(Exception):
1906
class TestTransformRollback(tests.TestCaseWithTransport):
1908
class ExceptionFileMover(_FileMover):
1910
def __init__(self, bad_source=None, bad_target=None):
1911
_FileMover.__init__(self)
1912
self.bad_source = bad_source
1913
self.bad_target = bad_target
1915
def rename(self, source, target):
1916
if (self.bad_source is not None and
1917
source.endswith(self.bad_source)):
1919
elif (self.bad_target is not None and
1920
target.endswith(self.bad_target)):
1923
_FileMover.rename(self, source, target)
1925
def test_rollback_rename(self):
1926
tree = self.make_branch_and_tree('.')
1927
self.build_tree(['a/', 'a/b'])
1928
tt = TreeTransform(tree)
1929
self.addCleanup(tt.finalize)
1930
a_id = tt.trans_id_tree_path('a')
1931
tt.adjust_path('c', tt.root, a_id)
1932
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1933
self.assertRaises(Bogus, tt.apply,
1934
_mover=self.ExceptionFileMover(bad_source='a'))
1935
self.failUnlessExists('a')
1936
self.failUnlessExists('a/b')
1938
self.failUnlessExists('c')
1939
self.failUnlessExists('c/d')
1941
def test_rollback_rename_into_place(self):
1942
tree = self.make_branch_and_tree('.')
1943
self.build_tree(['a/', 'a/b'])
1944
tt = TreeTransform(tree)
1945
self.addCleanup(tt.finalize)
1946
a_id = tt.trans_id_tree_path('a')
1947
tt.adjust_path('c', tt.root, a_id)
1948
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1949
self.assertRaises(Bogus, tt.apply,
1950
_mover=self.ExceptionFileMover(bad_target='c/d'))
1951
self.failUnlessExists('a')
1952
self.failUnlessExists('a/b')
1954
self.failUnlessExists('c')
1955
self.failUnlessExists('c/d')
1957
def test_rollback_deletion(self):
1958
tree = self.make_branch_and_tree('.')
1959
self.build_tree(['a/', 'a/b'])
1960
tt = TreeTransform(tree)
1961
self.addCleanup(tt.finalize)
1962
a_id = tt.trans_id_tree_path('a')
1963
tt.delete_contents(a_id)
1964
tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
1965
self.assertRaises(Bogus, tt.apply,
1966
_mover=self.ExceptionFileMover(bad_target='d'))
1967
self.failUnlessExists('a')
1968
self.failUnlessExists('a/b')
1970
def test_resolve_no_parent(self):
1971
wt = self.make_branch_and_tree('.')
1972
tt = TreeTransform(wt)
1973
self.addCleanup(tt.finalize)
1974
parent = tt.trans_id_file_id('parent-id')
1975
tt.new_file('file', parent, 'Contents')
1976
resolve_conflicts(tt)
1979
class TestTransformPreview(tests.TestCaseWithTransport):
1981
def create_tree(self):
1982
tree = self.make_branch_and_tree('.')
1983
self.build_tree_contents([('a', 'content 1')])
1984
tree.add('a', 'a-id')
1985
tree.commit('rev1', rev_id='rev1')
1986
return tree.branch.repository.revision_tree('rev1')
1988
def get_empty_preview(self):
1989
repository = self.make_repository('repo')
1990
tree = repository.revision_tree(_mod_revision.NULL_REVISION)
1991
preview = TransformPreview(tree)
1992
self.addCleanup(preview.finalize)
1995
def test_transform_preview(self):
1996
revision_tree = self.create_tree()
1997
preview = TransformPreview(revision_tree)
1998
self.addCleanup(preview.finalize)
2000
def test_transform_preview_tree(self):
2001
revision_tree = self.create_tree()
2002
preview = TransformPreview(revision_tree)
2003
self.addCleanup(preview.finalize)
2004
preview.get_preview_tree()
2006
def test_transform_new_file(self):
2007
revision_tree = self.create_tree()
2008
preview = TransformPreview(revision_tree)
2009
self.addCleanup(preview.finalize)
2010
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2011
preview_tree = preview.get_preview_tree()
2012
self.assertEqual(preview_tree.kind('file2-id'), 'file')
2014
preview_tree.get_file('file2-id').read(), 'content B\n')
2016
def test_diff_preview_tree(self):
2017
revision_tree = self.create_tree()
2018
preview = TransformPreview(revision_tree)
2019
self.addCleanup(preview.finalize)
2020
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2021
preview_tree = preview.get_preview_tree()
2023
show_diff_trees(revision_tree, preview_tree, out)
2024
lines = out.getvalue().splitlines()
2025
self.assertEqual(lines[0], "=== added file 'file2'")
2026
# 3 lines of diff administrivia
2027
self.assertEqual(lines[4], "+content B")
2029
def test_transform_conflicts(self):
2030
revision_tree = self.create_tree()
2031
preview = TransformPreview(revision_tree)
2032
self.addCleanup(preview.finalize)
2033
preview.new_file('a', preview.root, 'content 2')
2034
resolve_conflicts(preview)
2035
trans_id = preview.trans_id_file_id('a-id')
2036
self.assertEqual('a.moved', preview.final_name(trans_id))
2038
def get_tree_and_preview_tree(self):
2039
revision_tree = self.create_tree()
2040
preview = TransformPreview(revision_tree)
2041
self.addCleanup(preview.finalize)
2042
a_trans_id = preview.trans_id_file_id('a-id')
2043
preview.delete_contents(a_trans_id)
2044
preview.create_file('b content', a_trans_id)
2045
preview_tree = preview.get_preview_tree()
2046
return revision_tree, preview_tree
2048
def test_iter_changes(self):
2049
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2050
root = revision_tree.inventory.root.file_id
2051
self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
2052
(root, root), ('a', 'a'), ('file', 'file'),
2054
list(preview_tree.iter_changes(revision_tree)))
2056
def test_include_unchanged_value_error(self):
2057
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2058
e = self.assertRaises(ValueError, preview_tree.iter_changes,
2059
revision_tree, include_unchanged=True)
2060
self.assertEqual('include_unchanged is not supported', str(e))
2062
def test_specific_files(self):
2063
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2064
e = self.assertRaises(ValueError, preview_tree.iter_changes,
2065
revision_tree, specific_files=['pete'])
2066
self.assertEqual('specific_files is not supported', str(e))
2068
def test_want_unversioned_value_error(self):
2069
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2070
e = self.assertRaises(ValueError, preview_tree.iter_changes,
2071
revision_tree, want_unversioned=True)
2072
self.assertEqual('want_unversioned is not supported', str(e))
2074
def test_ignore_extra_trees_no_specific_files(self):
2075
# extra_trees is harmless without specific_files, so we'll silently
2076
# accept it, even though we won't use it.
2077
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2078
preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
2080
def test_ignore_require_versioned_no_specific_files(self):
2081
# require_versioned is meaningless without specific_files.
2082
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2083
preview_tree.iter_changes(revision_tree, require_versioned=False)
2085
def test_ignore_pb(self):
2086
# pb could be supported, but TT.iter_changes doesn't support it.
2087
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2088
preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
2090
def test_kind(self):
2091
revision_tree = self.create_tree()
2092
preview = TransformPreview(revision_tree)
2093
self.addCleanup(preview.finalize)
2094
preview.new_file('file', preview.root, 'contents', 'file-id')
2095
preview.new_directory('directory', preview.root, 'dir-id')
2096
preview_tree = preview.get_preview_tree()
2097
self.assertEqual('file', preview_tree.kind('file-id'))
2098
self.assertEqual('directory', preview_tree.kind('dir-id'))
2100
def test_get_file_mtime(self):
2101
preview = self.get_empty_preview()
2102
file_trans_id = preview.new_file('file', preview.root, 'contents',
2104
limbo_path = preview._limbo_name(file_trans_id)
2105
preview_tree = preview.get_preview_tree()
2106
self.assertEqual(os.stat(limbo_path).st_mtime,
2107
preview_tree.get_file_mtime('file-id'))
2109
def test_get_file(self):
2110
preview = self.get_empty_preview()
2111
preview.new_file('file', preview.root, 'contents', 'file-id')
2112
preview_tree = preview.get_preview_tree()
2113
tree_file = preview_tree.get_file('file-id')
2115
self.assertEqual('contents', tree_file.read())
2119
def test_get_symlink_target(self):
2120
self.requireFeature(SymlinkFeature)
2121
preview = self.get_empty_preview()
2122
preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
2123
preview_tree = preview.get_preview_tree()
2124
self.assertEqual('target',
2125
preview_tree.get_symlink_target('symlink-id'))
2127
def test_all_file_ids(self):
2128
tree = self.make_branch_and_tree('tree')
2129
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
2130
tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
2131
preview = TransformPreview(tree)
2132
self.addCleanup(preview.finalize)
2133
preview.unversion_file(preview.trans_id_file_id('b-id'))
2134
c_trans_id = preview.trans_id_file_id('c-id')
2135
preview.unversion_file(c_trans_id)
2136
preview.version_file('c-id', c_trans_id)
2137
preview_tree = preview.get_preview_tree()
2138
self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
2139
preview_tree.all_file_ids())
2141
def test_path2id_deleted_unchanged(self):
2142
tree = self.make_branch_and_tree('tree')
2143
self.build_tree(['tree/unchanged', 'tree/deleted'])
2144
tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
2145
preview = TransformPreview(tree)
2146
self.addCleanup(preview.finalize)
2147
preview.unversion_file(preview.trans_id_file_id('deleted-id'))
2148
preview_tree = preview.get_preview_tree()
2149
self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
2150
self.assertIs(None, preview_tree.path2id('deleted'))
2152
def test_path2id_created(self):
2153
tree = self.make_branch_and_tree('tree')
2154
self.build_tree(['tree/unchanged'])
2155
tree.add(['unchanged'], ['unchanged-id'])
2156
preview = TransformPreview(tree)
2157
self.addCleanup(preview.finalize)
2158
preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
2159
'contents', 'new-id')
2160
preview_tree = preview.get_preview_tree()
2161
self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
2163
def test_path2id_moved(self):
2164
tree = self.make_branch_and_tree('tree')
2165
self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
2166
tree.add(['old_parent', 'old_parent/child'],
2167
['old_parent-id', 'child-id'])
2168
preview = TransformPreview(tree)
2169
self.addCleanup(preview.finalize)
2170
new_parent = preview.new_directory('new_parent', preview.root,
2172
preview.adjust_path('child', new_parent,
2173
preview.trans_id_file_id('child-id'))
2174
preview_tree = preview.get_preview_tree()
2175
self.assertIs(None, preview_tree.path2id('old_parent/child'))
2176
self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
2178
def test_path2id_renamed_parent(self):
2179
tree = self.make_branch_and_tree('tree')
2180
self.build_tree(['tree/old_name/', 'tree/old_name/child'])
2181
tree.add(['old_name', 'old_name/child'],
2182
['parent-id', 'child-id'])
2183
preview = TransformPreview(tree)
2184
self.addCleanup(preview.finalize)
2185
preview.adjust_path('new_name', preview.root,
2186
preview.trans_id_file_id('parent-id'))
2187
preview_tree = preview.get_preview_tree()
2188
self.assertIs(None, preview_tree.path2id('old_name/child'))
2189
self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
2191
def assertMatchingIterEntries(self, tt, specific_file_ids=None):
2192
preview_tree = tt.get_preview_tree()
2193
preview_result = list(preview_tree.iter_entries_by_dir(
2197
actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
2198
self.assertEqual(actual_result, preview_result)
2200
def test_iter_entries_by_dir_new(self):
2201
tree = self.make_branch_and_tree('tree')
2202
tt = TreeTransform(tree)
2203
tt.new_file('new', tt.root, 'contents', 'new-id')
2204
self.assertMatchingIterEntries(tt)
2206
def test_iter_entries_by_dir_deleted(self):
2207
tree = self.make_branch_and_tree('tree')
2208
self.build_tree(['tree/deleted'])
2209
tree.add('deleted', 'deleted-id')
2210
tt = TreeTransform(tree)
2211
tt.delete_contents(tt.trans_id_file_id('deleted-id'))
2212
self.assertMatchingIterEntries(tt)
2214
def test_iter_entries_by_dir_unversioned(self):
2215
tree = self.make_branch_and_tree('tree')
2216
self.build_tree(['tree/removed'])
2217
tree.add('removed', 'removed-id')
2218
tt = TreeTransform(tree)
2219
tt.unversion_file(tt.trans_id_file_id('removed-id'))
2220
self.assertMatchingIterEntries(tt)
2222
def test_iter_entries_by_dir_moved(self):
2223
tree = self.make_branch_and_tree('tree')
2224
self.build_tree(['tree/moved', 'tree/new_parent/'])
2225
tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
2226
tt = TreeTransform(tree)
2227
tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
2228
tt.trans_id_file_id('moved-id'))
2229
self.assertMatchingIterEntries(tt)
2231
def test_iter_entries_by_dir_specific_file_ids(self):
2232
tree = self.make_branch_and_tree('tree')
2233
tree.set_root_id('tree-root-id')
2234
self.build_tree(['tree/parent/', 'tree/parent/child'])
2235
tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
2236
tt = TreeTransform(tree)
2237
self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
2239
def test_symlink_content_summary(self):
2240
self.requireFeature(SymlinkFeature)
2241
preview = self.get_empty_preview()
2242
preview.new_symlink('path', preview.root, 'target', 'path-id')
2243
summary = preview.get_preview_tree().path_content_summary('path')
2244
self.assertEqual(('symlink', None, None, 'target'), summary)
2246
def test_missing_content_summary(self):
2247
preview = self.get_empty_preview()
2248
summary = preview.get_preview_tree().path_content_summary('path')
2249
self.assertEqual(('missing', None, None, None), summary)
2251
def test_deleted_content_summary(self):
2252
tree = self.make_branch_and_tree('tree')
2253
self.build_tree(['tree/path/'])
2255
preview = TransformPreview(tree)
2256
self.addCleanup(preview.finalize)
2257
preview.delete_contents(preview.trans_id_tree_path('path'))
2258
summary = preview.get_preview_tree().path_content_summary('path')
2259
self.assertEqual(('missing', None, None, None), summary)
2261
def test_file_content_summary_executable(self):
2262
if not osutils.supports_executable():
2263
raise TestNotApplicable()
2264
preview = self.get_empty_preview()
2265
path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2266
preview.set_executability(True, path_id)
2267
summary = preview.get_preview_tree().path_content_summary('path')
2268
self.assertEqual(4, len(summary))
2269
self.assertEqual('file', summary[0])
2270
# size must be known
2271
self.assertEqual(len('contents'), summary[1])
2273
self.assertEqual(True, summary[2])
2274
# will not have hash (not cheap to determine)
2275
self.assertIs(None, summary[3])
2277
def test_change_executability(self):
2278
if not osutils.supports_executable():
2279
raise TestNotApplicable()
2280
tree = self.make_branch_and_tree('tree')
2281
self.build_tree(['tree/path'])
2283
preview = TransformPreview(tree)
2284
self.addCleanup(preview.finalize)
2285
path_id = preview.trans_id_tree_path('path')
2286
preview.set_executability(True, path_id)
2287
summary = preview.get_preview_tree().path_content_summary('path')
2288
self.assertEqual(True, summary[2])
2290
def test_file_content_summary_non_exec(self):
2291
preview = self.get_empty_preview()
2292
preview.new_file('path', preview.root, 'contents', 'path-id')
2293
summary = preview.get_preview_tree().path_content_summary('path')
2294
self.assertEqual(4, len(summary))
2295
self.assertEqual('file', summary[0])
2296
# size must be known
2297
self.assertEqual(len('contents'), summary[1])
2299
if osutils.supports_executable():
2300
self.assertEqual(False, summary[2])
2302
self.assertEqual(None, summary[2])
2303
# will not have hash (not cheap to determine)
2304
self.assertIs(None, summary[3])
2306
def test_dir_content_summary(self):
2307
preview = self.get_empty_preview()
2308
preview.new_directory('path', preview.root, 'path-id')
2309
summary = preview.get_preview_tree().path_content_summary('path')
2310
self.assertEqual(('directory', None, None, None), summary)
2312
def test_tree_content_summary(self):
2313
preview = self.get_empty_preview()
2314
path = preview.new_directory('path', preview.root, 'path-id')
2315
preview.set_tree_reference('rev-1', path)
2316
summary = preview.get_preview_tree().path_content_summary('path')
2317
self.assertEqual(4, len(summary))
2318
self.assertEqual('tree-reference', summary[0])
2320
def test_annotate(self):
2321
tree = self.make_branch_and_tree('tree')
2322
self.build_tree_contents([('tree/file', 'a\n')])
2323
tree.add('file', 'file-id')
2324
tree.commit('a', rev_id='one')
2325
self.build_tree_contents([('tree/file', 'a\nb\n')])
2326
preview = TransformPreview(tree)
2327
self.addCleanup(preview.finalize)
2328
file_trans_id = preview.trans_id_file_id('file-id')
2329
preview.delete_contents(file_trans_id)
2330
preview.create_file('a\nb\nc\n', file_trans_id)
2331
preview_tree = preview.get_preview_tree()
2337
annotation = preview_tree.annotate_iter('file-id', 'me:')
2338
self.assertEqual(expected, annotation)
2340
def test_annotate_missing(self):
2341
preview = self.get_empty_preview()
2342
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2343
preview_tree = preview.get_preview_tree()
2349
annotation = preview_tree.annotate_iter('file-id', 'me:')
2350
self.assertEqual(expected, annotation)
2352
def test_annotate_rename(self):
2353
tree = self.make_branch_and_tree('tree')
2354
self.build_tree_contents([('tree/file', 'a\n')])
2355
tree.add('file', 'file-id')
2356
tree.commit('a', rev_id='one')
2357
preview = TransformPreview(tree)
2358
self.addCleanup(preview.finalize)
2359
file_trans_id = preview.trans_id_file_id('file-id')
2360
preview.adjust_path('newname', preview.root, file_trans_id)
2361
preview_tree = preview.get_preview_tree()
2365
annotation = preview_tree.annotate_iter('file-id', 'me:')
2366
self.assertEqual(expected, annotation)
2368
def test_annotate_deleted(self):
2369
tree = self.make_branch_and_tree('tree')
2370
self.build_tree_contents([('tree/file', 'a\n')])
2371
tree.add('file', 'file-id')
2372
tree.commit('a', rev_id='one')
2373
self.build_tree_contents([('tree/file', 'a\nb\n')])
2374
preview = TransformPreview(tree)
2375
self.addCleanup(preview.finalize)
2376
file_trans_id = preview.trans_id_file_id('file-id')
2377
preview.delete_contents(file_trans_id)
2378
preview_tree = preview.get_preview_tree()
2379
annotation = preview_tree.annotate_iter('file-id', 'me:')
2380
self.assertIs(None, annotation)
2382
def test_stored_kind(self):
2383
preview = self.get_empty_preview()
2384
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2385
preview_tree = preview.get_preview_tree()
2386
self.assertEqual('file', preview_tree.stored_kind('file-id'))
2388
def test_is_executable(self):
2389
preview = self.get_empty_preview()
2390
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2391
preview.set_executability(True, preview.trans_id_file_id('file-id'))
2392
preview_tree = preview.get_preview_tree()
2393
self.assertEqual(True, preview_tree.is_executable('file-id'))
2395
def test_get_set_parent_ids(self):
2396
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2397
self.assertEqual([], preview_tree.get_parent_ids())
2398
preview_tree.set_parent_ids(['rev-1'])
2399
self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
2401
def test_plan_file_merge(self):
2402
work_a = self.make_branch_and_tree('wta')
2403
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2404
work_a.add('file', 'file-id')
2405
base_id = work_a.commit('base version')
2406
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2407
preview = TransformPreview(work_a)
2408
self.addCleanup(preview.finalize)
2409
trans_id = preview.trans_id_file_id('file-id')
2410
preview.delete_contents(trans_id)
2411
preview.create_file('b\nc\nd\ne\n', trans_id)
2412
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2413
tree_a = preview.get_preview_tree()
2414
tree_a.set_parent_ids([base_id])
2416
('killed-a', 'a\n'),
2417
('killed-b', 'b\n'),
2418
('unchanged', 'c\n'),
2419
('unchanged', 'd\n'),
2422
], list(tree_a.plan_file_merge('file-id', tree_b)))
2424
def test_plan_file_merge_revision_tree(self):
2425
work_a = self.make_branch_and_tree('wta')
2426
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2427
work_a.add('file', 'file-id')
2428
base_id = work_a.commit('base version')
2429
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2430
preview = TransformPreview(work_a.basis_tree())
2431
self.addCleanup(preview.finalize)
2432
trans_id = preview.trans_id_file_id('file-id')
2433
preview.delete_contents(trans_id)
2434
preview.create_file('b\nc\nd\ne\n', trans_id)
2435
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2436
tree_a = preview.get_preview_tree()
2437
tree_a.set_parent_ids([base_id])
2439
('killed-a', 'a\n'),
2440
('killed-b', 'b\n'),
2441
('unchanged', 'c\n'),
2442
('unchanged', 'd\n'),
2445
], list(tree_a.plan_file_merge('file-id', tree_b)))
2447
def test_walkdirs(self):
2448
preview = self.get_empty_preview()
2449
preview.version_file('tree-root', preview.root)
2450
preview_tree = preview.get_preview_tree()
2451
file_trans_id = preview.new_file('a', preview.root, 'contents',
2453
expected = [(('', 'tree-root'),
2454
[('a', 'a', 'file', None, 'a-id', 'file')])]
2455
self.assertEqual(expected, list(preview_tree.walkdirs()))
2457
def test_extras(self):
2458
work_tree = self.make_branch_and_tree('tree')
2459
self.build_tree(['tree/removed-file', 'tree/existing-file',
2460
'tree/not-removed-file'])
2461
work_tree.add(['removed-file', 'not-removed-file'])
2462
preview = TransformPreview(work_tree)
2463
self.addCleanup(preview.finalize)
2464
preview.new_file('new-file', preview.root, 'contents')
2465
preview.new_file('new-versioned-file', preview.root, 'contents',
2467
tree = preview.get_preview_tree()
2468
preview.unversion_file(preview.trans_id_tree_path('removed-file'))
2469
self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
2472
def test_merge_into_preview(self):
2473
work_tree = self.make_branch_and_tree('tree')
2474
self.build_tree_contents([('tree/file','b\n')])
2475
work_tree.add('file', 'file-id')
2476
work_tree.commit('first commit')
2477
child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
2478
self.build_tree_contents([('child/file','b\nc\n')])
2479
child_tree.commit('child commit')
2480
child_tree.lock_write()
2481
self.addCleanup(child_tree.unlock)
2482
work_tree.lock_write()
2483
self.addCleanup(work_tree.unlock)
2484
preview = TransformPreview(work_tree)
2485
self.addCleanup(preview.finalize)
2486
preview_tree = preview.get_preview_tree()
2487
file_trans_id = preview.trans_id_file_id('file-id')
2488
preview.delete_contents(file_trans_id)
2489
preview.create_file('a\nb\n', file_trans_id)
2490
pb = progress.DummyProgress()
2491
merger = Merger.from_revision_ids(pb, preview_tree,
2492
child_tree.branch.last_revision(),
2493
other_branch=child_tree.branch,
2494
tree_branch=work_tree.branch)
2495
merger.merge_type = Merge3Merger
2496
tt = merger.make_merger().make_preview_transform()
2497
self.addCleanup(tt.finalize)
2498
final_tree = tt.get_preview_tree()
2499
self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))