185
239
transform3.delete_contents(oz_id)
186
240
self.assertEqual(transform3.find_conflicts(),
187
241
[('missing parent', oz_id)])
188
root_id = transform3.trans_id_tree_file_id('TREE_ROOT')
242
root_id = transform3.root
189
243
tip_id = transform3.trans_id_tree_file_id('tip-id')
190
244
transform3.adjust_path('tip', root_id, tip_id)
191
245
transform3.apply()
247
def test_conflict_on_case_insensitive(self):
248
tree = self.make_branch_and_tree('tree')
249
# Don't try this at home, kids!
250
# Force the tree to report that it is case sensitive, for conflict
252
tree.case_sensitive = True
253
transform = TreeTransform(tree)
254
self.addCleanup(transform.finalize)
255
transform.new_file('file', transform.root, 'content')
256
transform.new_file('FiLe', transform.root, 'content')
257
result = transform.find_conflicts()
258
self.assertEqual([], result)
260
# Force the tree to report that it is case insensitive, for conflict
262
tree.case_sensitive = False
263
transform = TreeTransform(tree)
264
self.addCleanup(transform.finalize)
265
transform.new_file('file', transform.root, 'content')
266
transform.new_file('FiLe', transform.root, 'content')
267
result = transform.find_conflicts()
268
self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
270
def test_conflict_on_case_insensitive_existing(self):
271
tree = self.make_branch_and_tree('tree')
272
self.build_tree(['tree/FiLe'])
273
# Don't try this at home, kids!
274
# Force the tree to report that it is case sensitive, for conflict
276
tree.case_sensitive = True
277
transform = TreeTransform(tree)
278
self.addCleanup(transform.finalize)
279
transform.new_file('file', transform.root, 'content')
280
result = transform.find_conflicts()
281
self.assertEqual([], result)
283
# Force the tree to report that it is case insensitive, for conflict
285
tree.case_sensitive = False
286
transform = TreeTransform(tree)
287
self.addCleanup(transform.finalize)
288
transform.new_file('file', transform.root, 'content')
289
result = transform.find_conflicts()
290
self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
292
def test_resolve_case_insensitive_conflict(self):
293
tree = self.make_branch_and_tree('tree')
294
# Don't try this at home, kids!
295
# Force the tree to report that it is case insensitive, for conflict
297
tree.case_sensitive = False
298
transform = TreeTransform(tree)
299
self.addCleanup(transform.finalize)
300
transform.new_file('file', transform.root, 'content')
301
transform.new_file('FiLe', transform.root, 'content')
302
resolve_conflicts(transform)
304
self.failUnlessExists('tree/file')
305
self.failUnlessExists('tree/FiLe.moved')
307
def test_resolve_checkout_case_conflict(self):
308
tree = self.make_branch_and_tree('tree')
309
# Don't try this at home, kids!
310
# Force the tree to report that it is case insensitive, for conflict
312
tree.case_sensitive = False
313
transform = TreeTransform(tree)
314
self.addCleanup(transform.finalize)
315
transform.new_file('file', transform.root, 'content')
316
transform.new_file('FiLe', transform.root, 'content')
317
resolve_conflicts(transform,
318
pass_func=lambda t, c: resolve_checkout(t, c, []))
320
self.failUnlessExists('tree/file')
321
self.failUnlessExists('tree/FiLe.moved')
323
def test_apply_case_conflict(self):
324
"""Ensure that a transform with case conflicts can always be applied"""
325
tree = self.make_branch_and_tree('tree')
326
transform = TreeTransform(tree)
327
self.addCleanup(transform.finalize)
328
transform.new_file('file', transform.root, 'content')
329
transform.new_file('FiLe', transform.root, 'content')
330
dir = transform.new_directory('dir', transform.root)
331
transform.new_file('dirfile', dir, 'content')
332
transform.new_file('dirFiLe', dir, 'content')
333
resolve_conflicts(transform)
335
self.failUnlessExists('tree/file')
336
if not os.path.exists('tree/FiLe.moved'):
337
self.failUnlessExists('tree/FiLe')
338
self.failUnlessExists('tree/dir/dirfile')
339
if not os.path.exists('tree/dir/dirFiLe.moved'):
340
self.failUnlessExists('tree/dir/dirFiLe')
342
def test_case_insensitive_limbo(self):
343
tree = self.make_branch_and_tree('tree')
344
# Don't try this at home, kids!
345
# Force the tree to report that it is case insensitive
346
tree.case_sensitive = False
347
transform = TreeTransform(tree)
348
self.addCleanup(transform.finalize)
349
dir = transform.new_directory('dir', transform.root)
350
first = transform.new_file('file', dir, 'content')
351
second = transform.new_file('FiLe', dir, 'content')
352
self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
353
self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
193
355
def test_add_del(self):
194
356
start, root = self.get_transform()
195
357
start.new_directory('a', root, 'a')
485
737
create.new_file('vfile', root, 'myfile-text', 'myfile-id')
486
738
create.new_file('uvfile', root, 'othertext')
488
self.assertEqual(find_interesting(wt, wt, ['vfile']),
490
self.assertRaises(NotVersionedError, find_interesting, wt, wt,
740
result = self.applyDeprecated(symbol_versioning.zero_fifteen,
741
find_interesting, wt, wt, ['vfile'])
742
self.assertEqual(result, set(['myfile-id']))
744
def test_set_executability_order(self):
745
"""Ensure that executability behaves the same, no matter what order.
747
- create file and set executability simultaneously
748
- create file and set executability afterward
749
- unsetting the executability of a file whose executability has not been
750
declared should throw an exception (this may happen when a
751
merge attempts to create a file with a duplicate ID)
753
transform, root = self.get_transform()
756
self.addCleanup(wt.unlock)
757
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
759
sac = transform.new_file('set_after_creation', root,
760
'Set after creation', 'sac')
761
transform.set_executability(True, sac)
762
uws = transform.new_file('unset_without_set', root, 'Unset badly',
764
self.assertRaises(KeyError, transform.set_executability, None, uws)
766
self.assertTrue(wt.is_executable('soc'))
767
self.assertTrue(wt.is_executable('sac'))
769
def test_preserve_mode(self):
770
"""File mode is preserved when replacing content"""
771
if sys.platform == 'win32':
772
raise TestSkipped('chmod has no effect on win32')
773
transform, root = self.get_transform()
774
transform.new_file('file1', root, 'contents', 'file1-id', True)
777
self.addCleanup(self.wt.unlock)
778
self.assertTrue(self.wt.is_executable('file1-id'))
779
transform, root = self.get_transform()
780
file1_id = transform.trans_id_tree_file_id('file1-id')
781
transform.delete_contents(file1_id)
782
transform.create_file('contents2', file1_id)
784
self.assertTrue(self.wt.is_executable('file1-id'))
786
def test__set_mode_stats_correctly(self):
787
"""_set_mode stats to determine file mode."""
788
if sys.platform == 'win32':
789
raise TestSkipped('chmod has no effect on win32')
793
def instrumented_stat(path):
794
stat_paths.append(path)
795
return real_stat(path)
797
transform, root = self.get_transform()
799
bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
800
file_id='bar-id-1', executable=False)
803
transform, root = self.get_transform()
804
bar1_id = transform.trans_id_tree_path('bar')
805
bar2_id = transform.trans_id_tree_path('bar2')
807
os.stat = instrumented_stat
808
transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
813
bar1_abspath = self.wt.abspath('bar')
814
self.assertEqual([bar1_abspath], stat_paths)
816
def test_iter_changes(self):
817
self.wt.set_root_id('eert_toor')
818
transform, root = self.get_transform()
819
transform.new_file('old', root, 'blah', 'id-1', True)
821
transform, root = self.get_transform()
823
self.assertEqual([], list(transform._iter_changes()))
824
old = transform.trans_id_tree_file_id('id-1')
825
transform.unversion_file(old)
826
self.assertEqual([('id-1', ('old', None), False, (True, False),
827
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
828
(True, True))], list(transform._iter_changes()))
829
transform.new_directory('new', root, 'id-1')
830
self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
831
('eert_toor', 'eert_toor'), ('old', 'new'),
832
('file', 'directory'),
833
(True, False))], list(transform._iter_changes()))
837
def test_iter_changes_new(self):
838
self.wt.set_root_id('eert_toor')
839
transform, root = self.get_transform()
840
transform.new_file('old', root, 'blah')
842
transform, root = self.get_transform()
844
old = transform.trans_id_tree_path('old')
845
transform.version_file('id-1', old)
846
self.assertEqual([('id-1', (None, 'old'), False, (False, True),
847
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
848
(False, False))], list(transform._iter_changes()))
852
def test_iter_changes_modifications(self):
853
self.wt.set_root_id('eert_toor')
854
transform, root = self.get_transform()
855
transform.new_file('old', root, 'blah', 'id-1')
856
transform.new_file('new', root, 'blah')
857
transform.new_directory('subdir', root, 'subdir-id')
859
transform, root = self.get_transform()
861
old = transform.trans_id_tree_path('old')
862
subdir = transform.trans_id_tree_file_id('subdir-id')
863
new = transform.trans_id_tree_path('new')
864
self.assertEqual([], list(transform._iter_changes()))
867
transform.delete_contents(old)
868
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
869
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
870
(False, False))], list(transform._iter_changes()))
873
transform.create_file('blah', old)
874
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
875
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
876
(False, False))], list(transform._iter_changes()))
877
transform.cancel_deletion(old)
878
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
879
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
880
(False, False))], list(transform._iter_changes()))
881
transform.cancel_creation(old)
883
# move file_id to a different file
884
self.assertEqual([], list(transform._iter_changes()))
885
transform.unversion_file(old)
886
transform.version_file('id-1', new)
887
transform.adjust_path('old', root, new)
888
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
889
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
890
(False, False))], list(transform._iter_changes()))
891
transform.cancel_versioning(new)
892
transform._removed_id = set()
895
self.assertEqual([], list(transform._iter_changes()))
896
transform.set_executability(True, old)
897
self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
898
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
899
(False, True))], list(transform._iter_changes()))
900
transform.set_executability(None, old)
903
self.assertEqual([], list(transform._iter_changes()))
904
transform.adjust_path('new', root, old)
905
transform._new_parent = {}
906
self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
907
('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
908
(False, False))], list(transform._iter_changes()))
909
transform._new_name = {}
912
self.assertEqual([], list(transform._iter_changes()))
913
transform.adjust_path('new', subdir, old)
914
transform._new_name = {}
915
self.assertEqual([('id-1', ('old', 'subdir/old'), False,
916
(True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
917
('file', 'file'), (False, False))],
918
list(transform._iter_changes()))
919
transform._new_path = {}
924
def test_iter_changes_modified_bleed(self):
925
self.wt.set_root_id('eert_toor')
926
"""Modified flag should not bleed from one change to another"""
927
# unfortunately, we have no guarantee that file1 (which is modified)
928
# will be applied before file2. And if it's applied after file2, it
929
# obviously can't bleed into file2's change output. But for now, it
931
transform, root = self.get_transform()
932
transform.new_file('file1', root, 'blah', 'id-1')
933
transform.new_file('file2', root, 'blah', 'id-2')
935
transform, root = self.get_transform()
937
transform.delete_contents(transform.trans_id_file_id('id-1'))
938
transform.set_executability(True,
939
transform.trans_id_file_id('id-2'))
940
self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
941
('eert_toor', 'eert_toor'), ('file1', u'file1'),
942
('file', None), (False, False)),
943
('id-2', (u'file2', u'file2'), False, (True, True),
944
('eert_toor', 'eert_toor'), ('file2', u'file2'),
945
('file', 'file'), (False, True))],
946
list(transform._iter_changes()))
950
def test_iter_changes_move_missing(self):
951
"""Test moving ids with no files around"""
952
self.wt.set_root_id('toor_eert')
953
# Need two steps because versioning a non-existant file is a conflict.
954
transform, root = self.get_transform()
955
transform.new_directory('floater', root, 'floater-id')
957
transform, root = self.get_transform()
958
transform.delete_contents(transform.trans_id_tree_path('floater'))
960
transform, root = self.get_transform()
961
floater = transform.trans_id_tree_path('floater')
963
transform.adjust_path('flitter', root, floater)
964
self.assertEqual([('floater-id', ('floater', 'flitter'), False,
965
(True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
966
(None, None), (False, False))], list(transform._iter_changes()))
970
def test_iter_changes_pointless(self):
971
"""Ensure that no-ops are not treated as modifications"""
972
self.wt.set_root_id('eert_toor')
973
transform, root = self.get_transform()
974
transform.new_file('old', root, 'blah', 'id-1')
975
transform.new_directory('subdir', root, 'subdir-id')
977
transform, root = self.get_transform()
979
old = transform.trans_id_tree_path('old')
980
subdir = transform.trans_id_tree_file_id('subdir-id')
981
self.assertEqual([], list(transform._iter_changes()))
982
transform.delete_contents(subdir)
983
transform.create_directory(subdir)
984
transform.set_executability(False, old)
985
transform.unversion_file(old)
986
transform.version_file('id-1', old)
987
transform.adjust_path('old', root, old)
988
self.assertEqual([], list(transform._iter_changes()))
992
def test_rename_count(self):
993
transform, root = self.get_transform()
994
transform.new_file('name1', root, 'contents')
995
self.assertEqual(transform.rename_count, 0)
997
self.assertEqual(transform.rename_count, 1)
998
transform2, root = self.get_transform()
999
transform2.adjust_path('name2', root,
1000
transform2.trans_id_tree_path('name1'))
1001
self.assertEqual(transform2.rename_count, 0)
1003
self.assertEqual(transform2.rename_count, 2)
1005
def test_change_parent(self):
1006
"""Ensure that after we change a parent, the results are still right.
1008
Renames and parent changes on pending transforms can happen as part
1009
of conflict resolution, and are explicitly permitted by the
1012
This test ensures they work correctly with the rename-avoidance
1015
transform, root = self.get_transform()
1016
parent1 = transform.new_directory('parent1', root)
1017
child1 = transform.new_file('child1', parent1, 'contents')
1018
parent2 = transform.new_directory('parent2', root)
1019
transform.adjust_path('child1', parent2, child1)
1021
self.failIfExists(self.wt.abspath('parent1/child1'))
1022
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1023
# rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1024
# no rename for child1 (counting only renames during apply)
1025
self.failUnlessEqual(2, transform.rename_count)
1027
def test_cancel_parent(self):
1028
"""Cancelling a parent doesn't cause deletion of a non-empty directory
1030
This is like the test_change_parent, except that we cancel the parent
1031
before adjusting the path. The transform must detect that the
1032
directory is non-empty, and move children to safe locations.
1034
transform, root = self.get_transform()
1035
parent1 = transform.new_directory('parent1', root)
1036
child1 = transform.new_file('child1', parent1, 'contents')
1037
child2 = transform.new_file('child2', parent1, 'contents')
1039
transform.cancel_creation(parent1)
1041
self.fail('Failed to move child1 before deleting parent1')
1042
transform.cancel_creation(child2)
1043
transform.create_directory(parent1)
1045
transform.cancel_creation(parent1)
1046
# If the transform incorrectly believes that child2 is still in
1047
# parent1's limbo directory, it will try to rename it and fail
1048
# because was already moved by the first cancel_creation.
1050
self.fail('Transform still thinks child2 is a child of parent1')
1051
parent2 = transform.new_directory('parent2', root)
1052
transform.adjust_path('child1', parent2, child1)
1054
self.failIfExists(self.wt.abspath('parent1'))
1055
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1056
# rename limbo/new-3 => parent2, rename limbo/new-2 => child1
1057
self.failUnlessEqual(2, transform.rename_count)
1059
def test_adjust_and_cancel(self):
1060
"""Make sure adjust_path keeps track of limbo children properly"""
1061
transform, root = self.get_transform()
1062
parent1 = transform.new_directory('parent1', root)
1063
child1 = transform.new_file('child1', parent1, 'contents')
1064
parent2 = transform.new_directory('parent2', root)
1065
transform.adjust_path('child1', parent2, child1)
1066
transform.cancel_creation(child1)
1068
transform.cancel_creation(parent1)
1069
# if the transform thinks child1 is still in parent1's limbo
1070
# directory, it will attempt to move it and fail.
1072
self.fail('Transform still thinks child1 is a child of parent1')
1073
transform.finalize()
1075
def test_noname_contents(self):
1076
"""TreeTransform should permit deferring naming files."""
1077
transform, root = self.get_transform()
1078
parent = transform.trans_id_file_id('parent-id')
1080
transform.create_directory(parent)
1082
self.fail("Can't handle contents with no name")
1083
transform.finalize()
1085
def test_noname_contents_nested(self):
1086
"""TreeTransform should permit deferring naming files."""
1087
transform, root = self.get_transform()
1088
parent = transform.trans_id_file_id('parent-id')
1090
transform.create_directory(parent)
1092
self.fail("Can't handle contents with no name")
1093
child = transform.new_directory('child', parent)
1094
transform.adjust_path('parent', root, parent)
1096
self.failUnlessExists(self.wt.abspath('parent/child'))
1097
self.assertEqual(1, transform.rename_count)
1099
def test_reuse_name(self):
1100
"""Avoid reusing the same limbo name for different files"""
1101
transform, root = self.get_transform()
1102
parent = transform.new_directory('parent', root)
1103
child1 = transform.new_directory('child', parent)
1105
child2 = transform.new_directory('child', parent)
1107
self.fail('Tranform tried to use the same limbo name twice')
1108
transform.adjust_path('child2', parent, child2)
1110
# limbo/new-1 => parent, limbo/new-3 => parent/child2
1111
# child2 is put into top-level limbo because child1 has already
1112
# claimed the direct limbo path when child2 is created. There is no
1113
# advantage in renaming files once they're in top-level limbo, except
1115
self.assertEqual(2, transform.rename_count)
1117
def test_reuse_when_first_moved(self):
1118
"""Don't avoid direct paths when it is safe to use them"""
1119
transform, root = self.get_transform()
1120
parent = transform.new_directory('parent', root)
1121
child1 = transform.new_directory('child', parent)
1122
transform.adjust_path('child1', parent, child1)
1123
child2 = transform.new_directory('child', parent)
1125
# limbo/new-1 => parent
1126
self.assertEqual(1, transform.rename_count)
1128
def test_reuse_after_cancel(self):
1129
"""Don't avoid direct paths when it is safe to use them"""
1130
transform, root = self.get_transform()
1131
parent2 = transform.new_directory('parent2', root)
1132
child1 = transform.new_directory('child1', parent2)
1133
transform.cancel_creation(parent2)
1134
transform.create_directory(parent2)
1135
child2 = transform.new_directory('child1', parent2)
1136
transform.adjust_path('child2', parent2, child1)
1138
# limbo/new-1 => parent2, limbo/new-2 => parent2/child1
1139
self.assertEqual(2, transform.rename_count)
1141
def test_finalize_order(self):
1142
"""Finalize must be done in child-to-parent order"""
1143
transform, root = self.get_transform()
1144
parent = transform.new_directory('parent', root)
1145
child = transform.new_directory('child', parent)
1147
transform.finalize()
1149
self.fail('Tried to remove parent before child1')
1151
def test_cancel_with_cancelled_child_should_succeed(self):
1152
transform, root = self.get_transform()
1153
parent = transform.new_directory('parent', root)
1154
child = transform.new_directory('child', parent)
1155
transform.cancel_creation(child)
1156
transform.cancel_creation(parent)
1157
transform.finalize()
1159
def test_change_entry(self):
1160
txt = 'bzrlib.transform.change_entry was deprecated in version 0.90.'
1161
self.callDeprecated([txt], change_entry, None, None, None, None, None,
1164
def test_case_insensitive_clash(self):
1165
self.requireFeature(CaseInsensitiveFilesystemFeature)
1167
wt = self.make_branch_and_tree('.')
1168
tt = TreeTransform(wt) # TreeTransform obtains write lock
1170
tt.new_file('foo', tt.root, 'bar')
1171
tt.new_file('Foo', tt.root, 'spam')
1172
# Lie to tt that we've already resolved all conflicts.
1173
tt.apply(no_conflicts=True)
1177
err = self.assertRaises(errors.FileExists, tt_helper)
1178
self.assertContainsRe(str(err),
1179
"^File exists: .+/foo")
1181
def test_two_directories_clash(self):
1183
wt = self.make_branch_and_tree('.')
1184
tt = TreeTransform(wt) # TreeTransform obtains write lock
1186
foo_1 = tt.new_directory('foo', tt.root)
1187
tt.new_directory('bar', foo_1)
1188
foo_2 = tt.new_directory('foo', tt.root)
1189
tt.new_directory('baz', foo_2)
1190
# Lie to tt that we've already resolved all conflicts.
1191
tt.apply(no_conflicts=True)
1195
err = self.assertRaises(errors.FileExists, tt_helper)
1196
self.assertContainsRe(str(err),
1197
"^File exists: .+/foo")
1199
def test_two_directories_clash_finalize(self):
1201
wt = self.make_branch_and_tree('.')
1202
tt = TreeTransform(wt) # TreeTransform obtains write lock
1204
foo_1 = tt.new_directory('foo', tt.root)
1205
tt.new_directory('bar', foo_1)
1206
foo_2 = tt.new_directory('foo', tt.root)
1207
tt.new_directory('baz', foo_2)
1208
# Lie to tt that we've already resolved all conflicts.
1209
tt.apply(no_conflicts=True)
1213
err = self.assertRaises(errors.FileExists, tt_helper)
1214
self.assertContainsRe(str(err),
1215
"^File exists: .+/foo")
494
1218
class TransformGroup(object):
495
def __init__(self, dirname):
1220
def __init__(self, dirname, root_id):
496
1221
self.name = dirname
497
1222
os.mkdir(dirname)
498
1223
self.wt = BzrDir.create_standalone_workingtree(dirname)
1224
self.wt.set_root_id(root_id)
499
1225
self.b = self.wt.branch
500
1226
self.tt = TreeTransform(self.wt)
501
1227
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
503
1230
def conflict_text(tree, merge):
504
1231
template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
505
1232
return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
508
1235
class TestTransformMerge(TestCaseInTempDir):
509
1237
def test_text_merge(self):
510
base = TransformGroup("base")
1238
root_id = generate_ids.gen_root_id()
1239
base = TransformGroup("base", root_id)
511
1240
base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
512
1241
base.tt.new_file('b', base.root, 'b1', 'b')
513
1242
base.tt.new_file('c', base.root, 'c', 'c')
698
1431
a.add(['foo', 'foo/bar', 'foo/baz'])
699
1432
a.commit('initial commit')
700
1433
b = BzrDir.create_standalone_workingtree('b')
701
build_tree(a.basis_tree(), b)
1434
basis = a.basis_tree()
1436
self.addCleanup(basis.unlock)
1437
build_tree(basis, b)
702
1438
self.assertIs(os.path.isdir('b/foo'), True)
703
1439
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
704
1440
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1442
def test_build_with_references(self):
1443
tree = self.make_branch_and_tree('source',
1444
format='dirstate-with-subtree')
1445
subtree = self.make_branch_and_tree('source/subtree',
1446
format='dirstate-with-subtree')
1447
tree.add_reference(subtree)
1448
tree.commit('a revision')
1449
tree.branch.create_checkout('target')
1450
self.failUnlessExists('target')
1451
self.failUnlessExists('target/subtree')
1453
def test_file_conflict_handling(self):
1454
"""Ensure that when building trees, conflict handling is done"""
1455
source = self.make_branch_and_tree('source')
1456
target = self.make_branch_and_tree('target')
1457
self.build_tree(['source/file', 'target/file'])
1458
source.add('file', 'new-file')
1459
source.commit('added file')
1460
build_tree(source.basis_tree(), target)
1461
self.assertEqual([DuplicateEntry('Moved existing file to',
1462
'file.moved', 'file', None, 'new-file')],
1464
target2 = self.make_branch_and_tree('target2')
1465
target_file = file('target2/file', 'wb')
1467
source_file = file('source/file', 'rb')
1469
target_file.write(source_file.read())
1474
build_tree(source.basis_tree(), target2)
1475
self.assertEqual([], target2.conflicts())
1477
def test_symlink_conflict_handling(self):
1478
"""Ensure that when building trees, conflict handling is done"""
1479
self.requireFeature(SymlinkFeature)
1480
source = self.make_branch_and_tree('source')
1481
os.symlink('foo', 'source/symlink')
1482
source.add('symlink', 'new-symlink')
1483
source.commit('added file')
1484
target = self.make_branch_and_tree('target')
1485
os.symlink('bar', 'target/symlink')
1486
build_tree(source.basis_tree(), target)
1487
self.assertEqual([DuplicateEntry('Moved existing file to',
1488
'symlink.moved', 'symlink', None, 'new-symlink')],
1490
target = self.make_branch_and_tree('target2')
1491
os.symlink('foo', 'target2/symlink')
1492
build_tree(source.basis_tree(), target)
1493
self.assertEqual([], target.conflicts())
1495
def test_directory_conflict_handling(self):
1496
"""Ensure that when building trees, conflict handling is done"""
1497
source = self.make_branch_and_tree('source')
1498
target = self.make_branch_and_tree('target')
1499
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1500
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1501
source.commit('added file')
1502
build_tree(source.basis_tree(), target)
1503
self.assertEqual([], target.conflicts())
1504
self.failUnlessExists('target/dir1/file')
1506
# Ensure contents are merged
1507
target = self.make_branch_and_tree('target2')
1508
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1509
build_tree(source.basis_tree(), target)
1510
self.assertEqual([], target.conflicts())
1511
self.failUnlessExists('target2/dir1/file2')
1512
self.failUnlessExists('target2/dir1/file')
1514
# Ensure new contents are suppressed for existing branches
1515
target = self.make_branch_and_tree('target3')
1516
self.make_branch('target3/dir1')
1517
self.build_tree(['target3/dir1/file2'])
1518
build_tree(source.basis_tree(), target)
1519
self.failIfExists('target3/dir1/file')
1520
self.failUnlessExists('target3/dir1/file2')
1521
self.failUnlessExists('target3/dir1.diverted/file')
1522
self.assertEqual([DuplicateEntry('Diverted to',
1523
'dir1.diverted', 'dir1', 'new-dir1', None)],
1526
target = self.make_branch_and_tree('target4')
1527
self.build_tree(['target4/dir1/'])
1528
self.make_branch('target4/dir1/file')
1529
build_tree(source.basis_tree(), target)
1530
self.failUnlessExists('target4/dir1/file')
1531
self.assertEqual('directory', file_kind('target4/dir1/file'))
1532
self.failUnlessExists('target4/dir1/file.diverted')
1533
self.assertEqual([DuplicateEntry('Diverted to',
1534
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1537
def test_mixed_conflict_handling(self):
1538
"""Ensure that when building trees, conflict handling is done"""
1539
source = self.make_branch_and_tree('source')
1540
target = self.make_branch_and_tree('target')
1541
self.build_tree(['source/name', 'target/name/'])
1542
source.add('name', 'new-name')
1543
source.commit('added file')
1544
build_tree(source.basis_tree(), target)
1545
self.assertEqual([DuplicateEntry('Moved existing file to',
1546
'name.moved', 'name', None, 'new-name')], target.conflicts())
1548
def test_raises_in_populated(self):
1549
source = self.make_branch_and_tree('source')
1550
self.build_tree(['source/name'])
1552
source.commit('added name')
1553
target = self.make_branch_and_tree('target')
1554
self.build_tree(['target/name'])
1556
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1557
build_tree, source.basis_tree(), target)
1559
def test_build_tree_rename_count(self):
1560
source = self.make_branch_and_tree('source')
1561
self.build_tree(['source/file1', 'source/dir1/'])
1562
source.add(['file1', 'dir1'])
1563
source.commit('add1')
1564
target1 = self.make_branch_and_tree('target1')
1565
transform_result = build_tree(source.basis_tree(), target1)
1566
self.assertEqual(2, transform_result.rename_count)
1568
self.build_tree(['source/dir1/file2'])
1569
source.add(['dir1/file2'])
1570
source.commit('add3')
1571
target2 = self.make_branch_and_tree('target2')
1572
transform_result = build_tree(source.basis_tree(), target2)
1573
# children of non-root directories should not be renamed
1574
self.assertEqual(2, transform_result.rename_count)
1576
def test_build_tree_accelerator_tree(self):
1577
source = self.make_branch_and_tree('source')
1578
self.build_tree_contents([('source/file1', 'A')])
1579
self.build_tree_contents([('source/file2', 'B')])
1580
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1581
source.commit('commit files')
1582
self.build_tree_contents([('source/file2', 'C')])
1584
real_source_get_file = source.get_file
1585
def get_file(file_id, path=None):
1586
calls.append(file_id)
1587
return real_source_get_file(file_id, path)
1588
source.get_file = get_file
1590
self.addCleanup(source.unlock)
1591
target = self.make_branch_and_tree('target')
1592
revision_tree = source.basis_tree()
1593
revision_tree.lock_read()
1594
self.addCleanup(revision_tree.unlock)
1595
build_tree(revision_tree, target, source)
1596
self.assertEqual(['file1-id'], calls)
1598
self.addCleanup(target.unlock)
1599
self.assertEqual([], list(target._iter_changes(revision_tree)))
1601
def test_build_tree_accelerator_tree_missing_file(self):
1602
source = self.make_branch_and_tree('source')
1603
self.build_tree_contents([('source/file1', 'A')])
1604
self.build_tree_contents([('source/file2', 'B')])
1605
source.add(['file1', 'file2'])
1606
source.commit('commit files')
1607
os.unlink('source/file1')
1608
source.remove(['file2'])
1609
target = self.make_branch_and_tree('target')
1610
revision_tree = source.basis_tree()
1611
revision_tree.lock_read()
1612
self.addCleanup(revision_tree.unlock)
1613
build_tree(revision_tree, target, source)
1615
self.addCleanup(target.unlock)
1616
self.assertEqual([], list(target._iter_changes(revision_tree)))
1618
def test_build_tree_accelerator_wrong_kind(self):
1619
self.requireFeature(SymlinkFeature)
1620
source = self.make_branch_and_tree('source')
1621
self.build_tree_contents([('source/file1', '')])
1622
self.build_tree_contents([('source/file2', '')])
1623
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1624
source.commit('commit files')
1625
os.unlink('source/file2')
1626
self.build_tree_contents([('source/file2/', 'C')])
1627
os.unlink('source/file1')
1628
os.symlink('file2', 'source/file1')
1630
real_source_get_file = source.get_file
1631
def get_file(file_id, path=None):
1632
calls.append(file_id)
1633
return real_source_get_file(file_id, path)
1634
source.get_file = get_file
1636
self.addCleanup(source.unlock)
1637
target = self.make_branch_and_tree('target')
1638
revision_tree = source.basis_tree()
1639
revision_tree.lock_read()
1640
self.addCleanup(revision_tree.unlock)
1641
build_tree(revision_tree, target, source)
1642
self.assertEqual([], calls)
1644
self.addCleanup(target.unlock)
1645
self.assertEqual([], list(target._iter_changes(revision_tree)))
1647
def test_build_tree_accelerator_tree_moved(self):
1648
source = self.make_branch_and_tree('source')
1649
self.build_tree_contents([('source/file1', 'A')])
1650
source.add(['file1'], ['file1-id'])
1651
source.commit('commit files')
1652
source.rename_one('file1', 'file2')
1654
self.addCleanup(source.unlock)
1655
target = self.make_branch_and_tree('target')
1656
revision_tree = source.basis_tree()
1657
revision_tree.lock_read()
1658
self.addCleanup(revision_tree.unlock)
1659
build_tree(revision_tree, target, source)
1661
self.addCleanup(target.unlock)
1662
self.assertEqual([], list(target._iter_changes(revision_tree)))
706
1665
class MockTransform(object):
708
1667
def has_named_child(self, by_parent, parent_id, name):
732
1693
self.assertEqual(name, 'name.~1~')
733
1694
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
734
1695
self.assertEqual(name, 'name.~4~')
1698
class TestFileMover(tests.TestCaseWithTransport):
1700
def test_file_mover(self):
1701
self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
1702
mover = _FileMover()
1703
mover.rename('a', 'q')
1704
self.failUnlessExists('q')
1705
self.failIfExists('a')
1706
self.failUnlessExists('q/b')
1707
self.failUnlessExists('c')
1708
self.failUnlessExists('c/d')
1710
def test_pre_delete_rollback(self):
1711
self.build_tree(['a/'])
1712
mover = _FileMover()
1713
mover.pre_delete('a', 'q')
1714
self.failUnlessExists('q')
1715
self.failIfExists('a')
1717
self.failIfExists('q')
1718
self.failUnlessExists('a')
1720
def test_apply_deletions(self):
1721
self.build_tree(['a/', 'b/'])
1722
mover = _FileMover()
1723
mover.pre_delete('a', 'q')
1724
mover.pre_delete('b', 'r')
1725
self.failUnlessExists('q')
1726
self.failUnlessExists('r')
1727
self.failIfExists('a')
1728
self.failIfExists('b')
1729
mover.apply_deletions()
1730
self.failIfExists('q')
1731
self.failIfExists('r')
1732
self.failIfExists('a')
1733
self.failIfExists('b')
1735
def test_file_mover_rollback(self):
1736
self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
1737
mover = _FileMover()
1738
mover.rename('c/d', 'c/f')
1739
mover.rename('c/e', 'c/d')
1741
mover.rename('a', 'c')
1742
except errors.FileExists, e:
1744
self.failUnlessExists('a')
1745
self.failUnlessExists('c/d')
1748
class Bogus(Exception):
1752
class TestTransformRollback(tests.TestCaseWithTransport):
1754
class ExceptionFileMover(_FileMover):
1756
def __init__(self, bad_source=None, bad_target=None):
1757
_FileMover.__init__(self)
1758
self.bad_source = bad_source
1759
self.bad_target = bad_target
1761
def rename(self, source, target):
1762
if (self.bad_source is not None and
1763
source.endswith(self.bad_source)):
1765
elif (self.bad_target is not None and
1766
target.endswith(self.bad_target)):
1769
_FileMover.rename(self, source, target)
1771
def test_rollback_rename(self):
1772
tree = self.make_branch_and_tree('.')
1773
self.build_tree(['a/', 'a/b'])
1774
tt = TreeTransform(tree)
1775
self.addCleanup(tt.finalize)
1776
a_id = tt.trans_id_tree_path('a')
1777
tt.adjust_path('c', tt.root, a_id)
1778
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1779
self.assertRaises(Bogus, tt.apply,
1780
_mover=self.ExceptionFileMover(bad_source='a'))
1781
self.failUnlessExists('a')
1782
self.failUnlessExists('a/b')
1784
self.failUnlessExists('c')
1785
self.failUnlessExists('c/d')
1787
def test_rollback_rename_into_place(self):
1788
tree = self.make_branch_and_tree('.')
1789
self.build_tree(['a/', 'a/b'])
1790
tt = TreeTransform(tree)
1791
self.addCleanup(tt.finalize)
1792
a_id = tt.trans_id_tree_path('a')
1793
tt.adjust_path('c', tt.root, a_id)
1794
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1795
self.assertRaises(Bogus, tt.apply,
1796
_mover=self.ExceptionFileMover(bad_target='c/d'))
1797
self.failUnlessExists('a')
1798
self.failUnlessExists('a/b')
1800
self.failUnlessExists('c')
1801
self.failUnlessExists('c/d')
1803
def test_rollback_deletion(self):
1804
tree = self.make_branch_and_tree('.')
1805
self.build_tree(['a/', 'a/b'])
1806
tt = TreeTransform(tree)
1807
self.addCleanup(tt.finalize)
1808
a_id = tt.trans_id_tree_path('a')
1809
tt.delete_contents(a_id)
1810
tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
1811
self.assertRaises(Bogus, tt.apply,
1812
_mover=self.ExceptionFileMover(bad_target='d'))
1813
self.failUnlessExists('a')
1814
self.failUnlessExists('a/b')
1816
def test_resolve_no_parent(self):
1817
wt = self.make_branch_and_tree('.')
1818
tt = TreeTransform(wt)
1819
self.addCleanup(tt.finalize)
1820
parent = tt.trans_id_file_id('parent-id')
1821
tt.new_file('file', parent, 'Contents')
1822
resolve_conflicts(tt)
1825
class TestTransformPreview(tests.TestCaseWithTransport):
1827
def create_tree(self):
1828
tree = self.make_branch_and_tree('.')
1829
self.build_tree_contents([('a', 'content 1')])
1830
tree.add('a', 'a-id')
1831
tree.commit('rev1', rev_id='rev1')
1832
return tree.branch.repository.revision_tree('rev1')
1834
def get_empty_preview(self):
1835
repository = self.make_repository('repo')
1836
tree = repository.revision_tree(_mod_revision.NULL_REVISION)
1837
preview = TransformPreview(tree)
1838
self.addCleanup(preview.finalize)
1841
def test_transform_preview(self):
1842
revision_tree = self.create_tree()
1843
preview = TransformPreview(revision_tree)
1844
self.addCleanup(preview.finalize)
1846
def test_transform_preview_tree(self):
1847
revision_tree = self.create_tree()
1848
preview = TransformPreview(revision_tree)
1849
self.addCleanup(preview.finalize)
1850
preview.get_preview_tree()
1852
def test_transform_new_file(self):
1853
revision_tree = self.create_tree()
1854
preview = TransformPreview(revision_tree)
1855
self.addCleanup(preview.finalize)
1856
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
1857
preview_tree = preview.get_preview_tree()
1858
self.assertEqual(preview_tree.kind('file2-id'), 'file')
1860
preview_tree.get_file('file2-id').read(), 'content B\n')
1862
def test_diff_preview_tree(self):
1863
revision_tree = self.create_tree()
1864
preview = TransformPreview(revision_tree)
1865
self.addCleanup(preview.finalize)
1866
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
1867
preview_tree = preview.get_preview_tree()
1869
show_diff_trees(revision_tree, preview_tree, out)
1870
lines = out.getvalue().splitlines()
1871
self.assertEqual(lines[0], "=== added file 'file2'")
1872
# 3 lines of diff administrivia
1873
self.assertEqual(lines[4], "+content B")
1875
def test_transform_conflicts(self):
1876
revision_tree = self.create_tree()
1877
preview = TransformPreview(revision_tree)
1878
self.addCleanup(preview.finalize)
1879
preview.new_file('a', preview.root, 'content 2')
1880
resolve_conflicts(preview)
1881
trans_id = preview.trans_id_file_id('a-id')
1882
self.assertEqual('a.moved', preview.final_name(trans_id))
1884
def get_tree_and_preview_tree(self):
1885
revision_tree = self.create_tree()
1886
preview = TransformPreview(revision_tree)
1887
self.addCleanup(preview.finalize)
1888
a_trans_id = preview.trans_id_file_id('a-id')
1889
preview.delete_contents(a_trans_id)
1890
preview.create_file('b content', a_trans_id)
1891
preview_tree = preview.get_preview_tree()
1892
return revision_tree, preview_tree
1894
def test_iter_changes(self):
1895
revision_tree, preview_tree = self.get_tree_and_preview_tree()
1896
root = revision_tree.inventory.root.file_id
1897
self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
1898
(root, root), ('a', 'a'), ('file', 'file'),
1900
list(preview_tree._iter_changes(revision_tree)))
1902
def test_wrong_tree_value_error(self):
1903
revision_tree, preview_tree = self.get_tree_and_preview_tree()
1904
e = self.assertRaises(ValueError, preview_tree._iter_changes,
1906
self.assertEqual('from_tree must be transform source tree.', str(e))
1908
def test_include_unchanged_value_error(self):
1909
revision_tree, preview_tree = self.get_tree_and_preview_tree()
1910
e = self.assertRaises(ValueError, preview_tree._iter_changes,
1911
revision_tree, include_unchanged=True)
1912
self.assertEqual('include_unchanged is not supported', str(e))
1914
def test_specific_files(self):
1915
revision_tree, preview_tree = self.get_tree_and_preview_tree()
1916
e = self.assertRaises(ValueError, preview_tree._iter_changes,
1917
revision_tree, specific_files=['pete'])
1918
self.assertEqual('specific_files is not supported', str(e))
1920
def test_want_unversioned_value_error(self):
1921
revision_tree, preview_tree = self.get_tree_and_preview_tree()
1922
e = self.assertRaises(ValueError, preview_tree._iter_changes,
1923
revision_tree, want_unversioned=True)
1924
self.assertEqual('want_unversioned is not supported', str(e))
1926
def test_ignore_extra_trees_no_specific_files(self):
1927
# extra_trees is harmless without specific_files, so we'll silently
1928
# accept it, even though we won't use it.
1929
revision_tree, preview_tree = self.get_tree_and_preview_tree()
1930
preview_tree._iter_changes(revision_tree, extra_trees=[preview_tree])
1932
def test_ignore_require_versioned_no_specific_files(self):
1933
# require_versioned is meaningless without specific_files.
1934
revision_tree, preview_tree = self.get_tree_and_preview_tree()
1935
preview_tree._iter_changes(revision_tree, require_versioned=False)
1937
def test_ignore_pb(self):
1938
# pb could be supported, but TT.iter_changes doesn't support it.
1939
revision_tree, preview_tree = self.get_tree_and_preview_tree()
1940
preview_tree._iter_changes(revision_tree, pb=progress.DummyProgress())
1942
def test_kind(self):
1943
revision_tree = self.create_tree()
1944
preview = TransformPreview(revision_tree)
1945
self.addCleanup(preview.finalize)
1946
preview.new_file('file', preview.root, 'contents', 'file-id')
1947
preview.new_directory('directory', preview.root, 'dir-id')
1948
preview_tree = preview.get_preview_tree()
1949
self.assertEqual('file', preview_tree.kind('file-id'))
1950
self.assertEqual('directory', preview_tree.kind('dir-id'))
1952
def test_get_file_mtime(self):
1953
preview = self.get_empty_preview()
1954
file_trans_id = preview.new_file('file', preview.root, 'contents',
1956
limbo_path = preview._limbo_name(file_trans_id)
1957
preview_tree = preview.get_preview_tree()
1958
self.assertEqual(os.stat(limbo_path).st_mtime,
1959
preview_tree.get_file_mtime('file-id'))
1961
def test_get_file(self):
1962
preview = self.get_empty_preview()
1963
preview.new_file('file', preview.root, 'contents', 'file-id')
1964
preview_tree = preview.get_preview_tree()
1965
tree_file = preview_tree.get_file('file-id')
1967
self.assertEqual('contents', tree_file.read())
1971
def test_get_symlink_target(self):
1972
self.requireFeature(SymlinkFeature)
1973
preview = self.get_empty_preview()
1974
preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
1975
preview_tree = preview.get_preview_tree()
1976
self.assertEqual('target',
1977
preview_tree.get_symlink_target('symlink-id'))