185
238
transform3.delete_contents(oz_id)
186
239
self.assertEqual(transform3.find_conflicts(),
187
240
[('missing parent', oz_id)])
188
root_id = transform3.trans_id_tree_file_id('TREE_ROOT')
241
root_id = transform3.root
189
242
tip_id = transform3.trans_id_tree_file_id('tip-id')
190
243
transform3.adjust_path('tip', root_id, tip_id)
191
244
transform3.apply()
246
def test_conflict_on_case_insensitive(self):
247
tree = self.make_branch_and_tree('tree')
248
# Don't try this at home, kids!
249
# Force the tree to report that it is case sensitive, for conflict
251
tree.case_sensitive = True
252
transform = TreeTransform(tree)
253
self.addCleanup(transform.finalize)
254
transform.new_file('file', transform.root, 'content')
255
transform.new_file('FiLe', transform.root, 'content')
256
result = transform.find_conflicts()
257
self.assertEqual([], result)
259
# Force the tree to report that it is case insensitive, for conflict
261
tree.case_sensitive = False
262
transform = TreeTransform(tree)
263
self.addCleanup(transform.finalize)
264
transform.new_file('file', transform.root, 'content')
265
transform.new_file('FiLe', transform.root, 'content')
266
result = transform.find_conflicts()
267
self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
269
def test_conflict_on_case_insensitive_existing(self):
270
tree = self.make_branch_and_tree('tree')
271
self.build_tree(['tree/FiLe'])
272
# Don't try this at home, kids!
273
# Force the tree to report that it is case sensitive, for conflict
275
tree.case_sensitive = True
276
transform = TreeTransform(tree)
277
self.addCleanup(transform.finalize)
278
transform.new_file('file', transform.root, 'content')
279
result = transform.find_conflicts()
280
self.assertEqual([], result)
282
# Force the tree to report that it is case insensitive, for conflict
284
tree.case_sensitive = False
285
transform = TreeTransform(tree)
286
self.addCleanup(transform.finalize)
287
transform.new_file('file', transform.root, 'content')
288
result = transform.find_conflicts()
289
self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
291
def test_resolve_case_insensitive_conflict(self):
292
tree = self.make_branch_and_tree('tree')
293
# Don't try this at home, kids!
294
# Force the tree to report that it is case insensitive, for conflict
296
tree.case_sensitive = False
297
transform = TreeTransform(tree)
298
self.addCleanup(transform.finalize)
299
transform.new_file('file', transform.root, 'content')
300
transform.new_file('FiLe', transform.root, 'content')
301
resolve_conflicts(transform)
303
self.failUnlessExists('tree/file')
304
self.failUnlessExists('tree/FiLe.moved')
306
def test_resolve_checkout_case_conflict(self):
307
tree = self.make_branch_and_tree('tree')
308
# Don't try this at home, kids!
309
# Force the tree to report that it is case insensitive, for conflict
311
tree.case_sensitive = False
312
transform = TreeTransform(tree)
313
self.addCleanup(transform.finalize)
314
transform.new_file('file', transform.root, 'content')
315
transform.new_file('FiLe', transform.root, 'content')
316
resolve_conflicts(transform,
317
pass_func=lambda t, c: resolve_checkout(t, c, []))
319
self.failUnlessExists('tree/file')
320
self.failUnlessExists('tree/FiLe.moved')
322
def test_apply_case_conflict(self):
323
"""Ensure that a transform with case conflicts can always be applied"""
324
tree = self.make_branch_and_tree('tree')
325
transform = TreeTransform(tree)
326
self.addCleanup(transform.finalize)
327
transform.new_file('file', transform.root, 'content')
328
transform.new_file('FiLe', transform.root, 'content')
329
dir = transform.new_directory('dir', transform.root)
330
transform.new_file('dirfile', dir, 'content')
331
transform.new_file('dirFiLe', dir, 'content')
332
resolve_conflicts(transform)
334
self.failUnlessExists('tree/file')
335
if not os.path.exists('tree/FiLe.moved'):
336
self.failUnlessExists('tree/FiLe')
337
self.failUnlessExists('tree/dir/dirfile')
338
if not os.path.exists('tree/dir/dirFiLe.moved'):
339
self.failUnlessExists('tree/dir/dirFiLe')
341
def test_case_insensitive_limbo(self):
342
tree = self.make_branch_and_tree('tree')
343
# Don't try this at home, kids!
344
# Force the tree to report that it is case insensitive
345
tree.case_sensitive = False
346
transform = TreeTransform(tree)
347
self.addCleanup(transform.finalize)
348
dir = transform.new_directory('dir', transform.root)
349
first = transform.new_file('file', dir, 'content')
350
second = transform.new_file('FiLe', dir, 'content')
351
self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
352
self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
193
354
def test_add_del(self):
194
355
start, root = self.get_transform()
195
356
start.new_directory('a', root, 'a')
485
736
create.new_file('vfile', root, 'myfile-text', 'myfile-id')
486
737
create.new_file('uvfile', root, 'othertext')
488
self.assertEqual(find_interesting(wt, wt, ['vfile']),
490
self.assertRaises(NotVersionedError, find_interesting, wt, wt,
739
result = self.applyDeprecated(symbol_versioning.zero_fifteen,
740
find_interesting, wt, wt, ['vfile'])
741
self.assertEqual(result, set(['myfile-id']))
743
def test_set_executability_order(self):
744
"""Ensure that executability behaves the same, no matter what order.
746
- create file and set executability simultaneously
747
- create file and set executability afterward
748
- unsetting the executability of a file whose executability has not been
749
declared should throw an exception (this may happen when a
750
merge attempts to create a file with a duplicate ID)
752
transform, root = self.get_transform()
755
self.addCleanup(wt.unlock)
756
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
758
sac = transform.new_file('set_after_creation', root,
759
'Set after creation', 'sac')
760
transform.set_executability(True, sac)
761
uws = transform.new_file('unset_without_set', root, 'Unset badly',
763
self.assertRaises(KeyError, transform.set_executability, None, uws)
765
self.assertTrue(wt.is_executable('soc'))
766
self.assertTrue(wt.is_executable('sac'))
768
def test_preserve_mode(self):
769
"""File mode is preserved when replacing content"""
770
if sys.platform == 'win32':
771
raise TestSkipped('chmod has no effect on win32')
772
transform, root = self.get_transform()
773
transform.new_file('file1', root, 'contents', 'file1-id', True)
776
self.addCleanup(self.wt.unlock)
777
self.assertTrue(self.wt.is_executable('file1-id'))
778
transform, root = self.get_transform()
779
file1_id = transform.trans_id_tree_file_id('file1-id')
780
transform.delete_contents(file1_id)
781
transform.create_file('contents2', file1_id)
783
self.assertTrue(self.wt.is_executable('file1-id'))
785
def test__set_mode_stats_correctly(self):
786
"""_set_mode stats to determine file mode."""
787
if sys.platform == 'win32':
788
raise TestSkipped('chmod has no effect on win32')
792
def instrumented_stat(path):
793
stat_paths.append(path)
794
return real_stat(path)
796
transform, root = self.get_transform()
798
bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
799
file_id='bar-id-1', executable=False)
802
transform, root = self.get_transform()
803
bar1_id = transform.trans_id_tree_path('bar')
804
bar2_id = transform.trans_id_tree_path('bar2')
806
os.stat = instrumented_stat
807
transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
812
bar1_abspath = self.wt.abspath('bar')
813
self.assertEqual([bar1_abspath], stat_paths)
815
def test_iter_changes(self):
816
self.wt.set_root_id('eert_toor')
817
transform, root = self.get_transform()
818
transform.new_file('old', root, 'blah', 'id-1', True)
820
transform, root = self.get_transform()
822
self.assertEqual([], list(transform._iter_changes()))
823
old = transform.trans_id_tree_file_id('id-1')
824
transform.unversion_file(old)
825
self.assertEqual([('id-1', ('old', None), False, (True, False),
826
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
827
(True, True))], list(transform._iter_changes()))
828
transform.new_directory('new', root, 'id-1')
829
self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
830
('eert_toor', 'eert_toor'), ('old', 'new'),
831
('file', 'directory'),
832
(True, False))], list(transform._iter_changes()))
836
def test_iter_changes_new(self):
837
self.wt.set_root_id('eert_toor')
838
transform, root = self.get_transform()
839
transform.new_file('old', root, 'blah')
841
transform, root = self.get_transform()
843
old = transform.trans_id_tree_path('old')
844
transform.version_file('id-1', old)
845
self.assertEqual([('id-1', (None, 'old'), False, (False, True),
846
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
847
(False, False))], list(transform._iter_changes()))
851
def test_iter_changes_modifications(self):
852
self.wt.set_root_id('eert_toor')
853
transform, root = self.get_transform()
854
transform.new_file('old', root, 'blah', 'id-1')
855
transform.new_file('new', root, 'blah')
856
transform.new_directory('subdir', root, 'subdir-id')
858
transform, root = self.get_transform()
860
old = transform.trans_id_tree_path('old')
861
subdir = transform.trans_id_tree_file_id('subdir-id')
862
new = transform.trans_id_tree_path('new')
863
self.assertEqual([], list(transform._iter_changes()))
866
transform.delete_contents(old)
867
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
868
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
869
(False, False))], list(transform._iter_changes()))
872
transform.create_file('blah', old)
873
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
874
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
875
(False, False))], list(transform._iter_changes()))
876
transform.cancel_deletion(old)
877
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
878
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
879
(False, False))], list(transform._iter_changes()))
880
transform.cancel_creation(old)
882
# move file_id to a different file
883
self.assertEqual([], list(transform._iter_changes()))
884
transform.unversion_file(old)
885
transform.version_file('id-1', new)
886
transform.adjust_path('old', root, new)
887
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
888
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
889
(False, False))], list(transform._iter_changes()))
890
transform.cancel_versioning(new)
891
transform._removed_id = set()
894
self.assertEqual([], list(transform._iter_changes()))
895
transform.set_executability(True, old)
896
self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
897
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
898
(False, True))], list(transform._iter_changes()))
899
transform.set_executability(None, old)
902
self.assertEqual([], list(transform._iter_changes()))
903
transform.adjust_path('new', root, old)
904
transform._new_parent = {}
905
self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
906
('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
907
(False, False))], list(transform._iter_changes()))
908
transform._new_name = {}
911
self.assertEqual([], list(transform._iter_changes()))
912
transform.adjust_path('new', subdir, old)
913
transform._new_name = {}
914
self.assertEqual([('id-1', ('old', 'subdir/old'), False,
915
(True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
916
('file', 'file'), (False, False))],
917
list(transform._iter_changes()))
918
transform._new_path = {}
923
def test_iter_changes_modified_bleed(self):
924
self.wt.set_root_id('eert_toor')
925
"""Modified flag should not bleed from one change to another"""
926
# unfortunately, we have no guarantee that file1 (which is modified)
927
# will be applied before file2. And if it's applied after file2, it
928
# obviously can't bleed into file2's change output. But for now, it
930
transform, root = self.get_transform()
931
transform.new_file('file1', root, 'blah', 'id-1')
932
transform.new_file('file2', root, 'blah', 'id-2')
934
transform, root = self.get_transform()
936
transform.delete_contents(transform.trans_id_file_id('id-1'))
937
transform.set_executability(True,
938
transform.trans_id_file_id('id-2'))
939
self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
940
('eert_toor', 'eert_toor'), ('file1', u'file1'),
941
('file', None), (False, False)),
942
('id-2', (u'file2', u'file2'), False, (True, True),
943
('eert_toor', 'eert_toor'), ('file2', u'file2'),
944
('file', 'file'), (False, True))],
945
list(transform._iter_changes()))
949
def test_iter_changes_move_missing(self):
950
"""Test moving ids with no files around"""
951
self.wt.set_root_id('toor_eert')
952
# Need two steps because versioning a non-existant file is a conflict.
953
transform, root = self.get_transform()
954
transform.new_directory('floater', root, 'floater-id')
956
transform, root = self.get_transform()
957
transform.delete_contents(transform.trans_id_tree_path('floater'))
959
transform, root = self.get_transform()
960
floater = transform.trans_id_tree_path('floater')
962
transform.adjust_path('flitter', root, floater)
963
self.assertEqual([('floater-id', ('floater', 'flitter'), False,
964
(True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
965
(None, None), (False, False))], list(transform._iter_changes()))
969
def test_iter_changes_pointless(self):
970
"""Ensure that no-ops are not treated as modifications"""
971
self.wt.set_root_id('eert_toor')
972
transform, root = self.get_transform()
973
transform.new_file('old', root, 'blah', 'id-1')
974
transform.new_directory('subdir', root, 'subdir-id')
976
transform, root = self.get_transform()
978
old = transform.trans_id_tree_path('old')
979
subdir = transform.trans_id_tree_file_id('subdir-id')
980
self.assertEqual([], list(transform._iter_changes()))
981
transform.delete_contents(subdir)
982
transform.create_directory(subdir)
983
transform.set_executability(False, old)
984
transform.unversion_file(old)
985
transform.version_file('id-1', old)
986
transform.adjust_path('old', root, old)
987
self.assertEqual([], list(transform._iter_changes()))
991
def test_rename_count(self):
992
transform, root = self.get_transform()
993
transform.new_file('name1', root, 'contents')
994
self.assertEqual(transform.rename_count, 0)
996
self.assertEqual(transform.rename_count, 1)
997
transform2, root = self.get_transform()
998
transform2.adjust_path('name2', root,
999
transform2.trans_id_tree_path('name1'))
1000
self.assertEqual(transform2.rename_count, 0)
1002
self.assertEqual(transform2.rename_count, 2)
1004
def test_change_parent(self):
1005
"""Ensure that after we change a parent, the results are still right.
1007
Renames and parent changes on pending transforms can happen as part
1008
of conflict resolution, and are explicitly permitted by the
1011
This test ensures they work correctly with the rename-avoidance
1014
transform, root = self.get_transform()
1015
parent1 = transform.new_directory('parent1', root)
1016
child1 = transform.new_file('child1', parent1, 'contents')
1017
parent2 = transform.new_directory('parent2', root)
1018
transform.adjust_path('child1', parent2, child1)
1020
self.failIfExists(self.wt.abspath('parent1/child1'))
1021
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1022
# rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1023
# no rename for child1 (counting only renames during apply)
1024
self.failUnlessEqual(2, transform.rename_count)
1026
def test_cancel_parent(self):
1027
"""Cancelling a parent doesn't cause deletion of a non-empty directory
1029
This is like the test_change_parent, except that we cancel the parent
1030
before adjusting the path. The transform must detect that the
1031
directory is non-empty, and move children to safe locations.
1033
transform, root = self.get_transform()
1034
parent1 = transform.new_directory('parent1', root)
1035
child1 = transform.new_file('child1', parent1, 'contents')
1036
child2 = transform.new_file('child2', parent1, 'contents')
1038
transform.cancel_creation(parent1)
1040
self.fail('Failed to move child1 before deleting parent1')
1041
transform.cancel_creation(child2)
1042
transform.create_directory(parent1)
1044
transform.cancel_creation(parent1)
1045
# If the transform incorrectly believes that child2 is still in
1046
# parent1's limbo directory, it will try to rename it and fail
1047
# because was already moved by the first cancel_creation.
1049
self.fail('Transform still thinks child2 is a child of parent1')
1050
parent2 = transform.new_directory('parent2', root)
1051
transform.adjust_path('child1', parent2, child1)
1053
self.failIfExists(self.wt.abspath('parent1'))
1054
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1055
# rename limbo/new-3 => parent2, rename limbo/new-2 => child1
1056
self.failUnlessEqual(2, transform.rename_count)
1058
def test_adjust_and_cancel(self):
1059
"""Make sure adjust_path keeps track of limbo children properly"""
1060
transform, root = self.get_transform()
1061
parent1 = transform.new_directory('parent1', root)
1062
child1 = transform.new_file('child1', parent1, 'contents')
1063
parent2 = transform.new_directory('parent2', root)
1064
transform.adjust_path('child1', parent2, child1)
1065
transform.cancel_creation(child1)
1067
transform.cancel_creation(parent1)
1068
# if the transform thinks child1 is still in parent1's limbo
1069
# directory, it will attempt to move it and fail.
1071
self.fail('Transform still thinks child1 is a child of parent1')
1072
transform.finalize()
1074
def test_noname_contents(self):
1075
"""TreeTransform should permit deferring naming files."""
1076
transform, root = self.get_transform()
1077
parent = transform.trans_id_file_id('parent-id')
1079
transform.create_directory(parent)
1081
self.fail("Can't handle contents with no name")
1082
transform.finalize()
1084
def test_noname_contents_nested(self):
1085
"""TreeTransform should permit deferring naming files."""
1086
transform, root = self.get_transform()
1087
parent = transform.trans_id_file_id('parent-id')
1089
transform.create_directory(parent)
1091
self.fail("Can't handle contents with no name")
1092
child = transform.new_directory('child', parent)
1093
transform.adjust_path('parent', root, parent)
1095
self.failUnlessExists(self.wt.abspath('parent/child'))
1096
self.assertEqual(1, transform.rename_count)
1098
def test_reuse_name(self):
1099
"""Avoid reusing the same limbo name for different files"""
1100
transform, root = self.get_transform()
1101
parent = transform.new_directory('parent', root)
1102
child1 = transform.new_directory('child', parent)
1104
child2 = transform.new_directory('child', parent)
1106
self.fail('Tranform tried to use the same limbo name twice')
1107
transform.adjust_path('child2', parent, child2)
1109
# limbo/new-1 => parent, limbo/new-3 => parent/child2
1110
# child2 is put into top-level limbo because child1 has already
1111
# claimed the direct limbo path when child2 is created. There is no
1112
# advantage in renaming files once they're in top-level limbo, except
1114
self.assertEqual(2, transform.rename_count)
1116
def test_reuse_when_first_moved(self):
1117
"""Don't avoid direct paths when it is safe to use them"""
1118
transform, root = self.get_transform()
1119
parent = transform.new_directory('parent', root)
1120
child1 = transform.new_directory('child', parent)
1121
transform.adjust_path('child1', parent, child1)
1122
child2 = transform.new_directory('child', parent)
1124
# limbo/new-1 => parent
1125
self.assertEqual(1, transform.rename_count)
1127
def test_reuse_after_cancel(self):
1128
"""Don't avoid direct paths when it is safe to use them"""
1129
transform, root = self.get_transform()
1130
parent2 = transform.new_directory('parent2', root)
1131
child1 = transform.new_directory('child1', parent2)
1132
transform.cancel_creation(parent2)
1133
transform.create_directory(parent2)
1134
child2 = transform.new_directory('child1', parent2)
1135
transform.adjust_path('child2', parent2, child1)
1137
# limbo/new-1 => parent2, limbo/new-2 => parent2/child1
1138
self.assertEqual(2, transform.rename_count)
1140
def test_finalize_order(self):
1141
"""Finalize must be done in child-to-parent order"""
1142
transform, root = self.get_transform()
1143
parent = transform.new_directory('parent', root)
1144
child = transform.new_directory('child', parent)
1146
transform.finalize()
1148
self.fail('Tried to remove parent before child1')
1150
def test_cancel_with_cancelled_child_should_succeed(self):
1151
transform, root = self.get_transform()
1152
parent = transform.new_directory('parent', root)
1153
child = transform.new_directory('child', parent)
1154
transform.cancel_creation(child)
1155
transform.cancel_creation(parent)
1156
transform.finalize()
1158
def test_change_entry(self):
1159
txt = 'bzrlib.transform.change_entry was deprecated in version 0.90.'
1160
self.callDeprecated([txt], change_entry, None, None, None, None, None,
1163
def test_case_insensitive_clash(self):
1164
self.requireFeature(CaseInsensitiveFilesystemFeature)
1166
wt = self.make_branch_and_tree('.')
1167
tt = TreeTransform(wt) # TreeTransform obtains write lock
1169
tt.new_file('foo', tt.root, 'bar')
1170
tt.new_file('Foo', tt.root, 'spam')
1171
# Lie to tt that we've already resolved all conflicts.
1172
tt.apply(no_conflicts=True)
1176
err = self.assertRaises(errors.FileExists, tt_helper)
1177
self.assertContainsRe(str(err),
1178
"^File exists: .+/foo")
1180
def test_two_directories_clash(self):
1182
wt = self.make_branch_and_tree('.')
1183
tt = TreeTransform(wt) # TreeTransform obtains write lock
1185
foo_1 = tt.new_directory('foo', tt.root)
1186
tt.new_directory('bar', foo_1)
1187
foo_2 = tt.new_directory('foo', tt.root)
1188
tt.new_directory('baz', foo_2)
1189
# Lie to tt that we've already resolved all conflicts.
1190
tt.apply(no_conflicts=True)
1194
err = self.assertRaises(errors.FileExists, tt_helper)
1195
self.assertContainsRe(str(err),
1196
"^File exists: .+/foo")
1198
def test_two_directories_clash_finalize(self):
1200
wt = self.make_branch_and_tree('.')
1201
tt = TreeTransform(wt) # TreeTransform obtains write lock
1203
foo_1 = tt.new_directory('foo', tt.root)
1204
tt.new_directory('bar', foo_1)
1205
foo_2 = tt.new_directory('foo', tt.root)
1206
tt.new_directory('baz', foo_2)
1207
# Lie to tt that we've already resolved all conflicts.
1208
tt.apply(no_conflicts=True)
1212
err = self.assertRaises(errors.FileExists, tt_helper)
1213
self.assertContainsRe(str(err),
1214
"^File exists: .+/foo")
494
1217
class TransformGroup(object):
495
def __init__(self, dirname):
1219
def __init__(self, dirname, root_id):
496
1220
self.name = dirname
497
1221
os.mkdir(dirname)
498
1222
self.wt = BzrDir.create_standalone_workingtree(dirname)
1223
self.wt.set_root_id(root_id)
499
1224
self.b = self.wt.branch
500
1225
self.tt = TreeTransform(self.wt)
501
1226
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
503
1229
def conflict_text(tree, merge):
504
1230
template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
505
1231
return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
698
1429
a.add(['foo', 'foo/bar', 'foo/baz'])
699
1430
a.commit('initial commit')
700
1431
b = BzrDir.create_standalone_workingtree('b')
701
build_tree(a.basis_tree(), b)
1432
basis = a.basis_tree()
1434
self.addCleanup(basis.unlock)
1435
build_tree(basis, b)
702
1436
self.assertIs(os.path.isdir('b/foo'), True)
703
1437
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
704
1438
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1440
def test_build_with_references(self):
1441
tree = self.make_branch_and_tree('source',
1442
format='dirstate-with-subtree')
1443
subtree = self.make_branch_and_tree('source/subtree',
1444
format='dirstate-with-subtree')
1445
tree.add_reference(subtree)
1446
tree.commit('a revision')
1447
tree.branch.create_checkout('target')
1448
self.failUnlessExists('target')
1449
self.failUnlessExists('target/subtree')
1451
def test_file_conflict_handling(self):
1452
"""Ensure that when building trees, conflict handling is done"""
1453
source = self.make_branch_and_tree('source')
1454
target = self.make_branch_and_tree('target')
1455
self.build_tree(['source/file', 'target/file'])
1456
source.add('file', 'new-file')
1457
source.commit('added file')
1458
build_tree(source.basis_tree(), target)
1459
self.assertEqual([DuplicateEntry('Moved existing file to',
1460
'file.moved', 'file', None, 'new-file')],
1462
target2 = self.make_branch_and_tree('target2')
1463
target_file = file('target2/file', 'wb')
1465
source_file = file('source/file', 'rb')
1467
target_file.write(source_file.read())
1472
build_tree(source.basis_tree(), target2)
1473
self.assertEqual([], target2.conflicts())
1475
def test_symlink_conflict_handling(self):
1476
"""Ensure that when building trees, conflict handling is done"""
1477
self.requireFeature(SymlinkFeature)
1478
source = self.make_branch_and_tree('source')
1479
os.symlink('foo', 'source/symlink')
1480
source.add('symlink', 'new-symlink')
1481
source.commit('added file')
1482
target = self.make_branch_and_tree('target')
1483
os.symlink('bar', 'target/symlink')
1484
build_tree(source.basis_tree(), target)
1485
self.assertEqual([DuplicateEntry('Moved existing file to',
1486
'symlink.moved', 'symlink', None, 'new-symlink')],
1488
target = self.make_branch_and_tree('target2')
1489
os.symlink('foo', 'target2/symlink')
1490
build_tree(source.basis_tree(), target)
1491
self.assertEqual([], target.conflicts())
1493
def test_directory_conflict_handling(self):
1494
"""Ensure that when building trees, conflict handling is done"""
1495
source = self.make_branch_and_tree('source')
1496
target = self.make_branch_and_tree('target')
1497
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1498
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1499
source.commit('added file')
1500
build_tree(source.basis_tree(), target)
1501
self.assertEqual([], target.conflicts())
1502
self.failUnlessExists('target/dir1/file')
1504
# Ensure contents are merged
1505
target = self.make_branch_and_tree('target2')
1506
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1507
build_tree(source.basis_tree(), target)
1508
self.assertEqual([], target.conflicts())
1509
self.failUnlessExists('target2/dir1/file2')
1510
self.failUnlessExists('target2/dir1/file')
1512
# Ensure new contents are suppressed for existing branches
1513
target = self.make_branch_and_tree('target3')
1514
self.make_branch('target3/dir1')
1515
self.build_tree(['target3/dir1/file2'])
1516
build_tree(source.basis_tree(), target)
1517
self.failIfExists('target3/dir1/file')
1518
self.failUnlessExists('target3/dir1/file2')
1519
self.failUnlessExists('target3/dir1.diverted/file')
1520
self.assertEqual([DuplicateEntry('Diverted to',
1521
'dir1.diverted', 'dir1', 'new-dir1', None)],
1524
target = self.make_branch_and_tree('target4')
1525
self.build_tree(['target4/dir1/'])
1526
self.make_branch('target4/dir1/file')
1527
build_tree(source.basis_tree(), target)
1528
self.failUnlessExists('target4/dir1/file')
1529
self.assertEqual('directory', file_kind('target4/dir1/file'))
1530
self.failUnlessExists('target4/dir1/file.diverted')
1531
self.assertEqual([DuplicateEntry('Diverted to',
1532
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1535
def test_mixed_conflict_handling(self):
1536
"""Ensure that when building trees, conflict handling is done"""
1537
source = self.make_branch_and_tree('source')
1538
target = self.make_branch_and_tree('target')
1539
self.build_tree(['source/name', 'target/name/'])
1540
source.add('name', 'new-name')
1541
source.commit('added file')
1542
build_tree(source.basis_tree(), target)
1543
self.assertEqual([DuplicateEntry('Moved existing file to',
1544
'name.moved', 'name', None, 'new-name')], target.conflicts())
1546
def test_raises_in_populated(self):
1547
source = self.make_branch_and_tree('source')
1548
self.build_tree(['source/name'])
1550
source.commit('added name')
1551
target = self.make_branch_and_tree('target')
1552
self.build_tree(['target/name'])
1554
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1555
build_tree, source.basis_tree(), target)
1557
def test_build_tree_rename_count(self):
1558
source = self.make_branch_and_tree('source')
1559
self.build_tree(['source/file1', 'source/dir1/'])
1560
source.add(['file1', 'dir1'])
1561
source.commit('add1')
1562
target1 = self.make_branch_and_tree('target1')
1563
transform_result = build_tree(source.basis_tree(), target1)
1564
self.assertEqual(2, transform_result.rename_count)
1566
self.build_tree(['source/dir1/file2'])
1567
source.add(['dir1/file2'])
1568
source.commit('add3')
1569
target2 = self.make_branch_and_tree('target2')
1570
transform_result = build_tree(source.basis_tree(), target2)
1571
# children of non-root directories should not be renamed
1572
self.assertEqual(2, transform_result.rename_count)
1574
def test_build_tree_accelerator_tree(self):
1575
source = self.make_branch_and_tree('source')
1576
self.build_tree_contents([('source/file1', 'A')])
1577
self.build_tree_contents([('source/file2', 'B')])
1578
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1579
source.commit('commit files')
1580
self.build_tree_contents([('source/file2', 'C')])
1582
real_source_get_file = source.get_file
1583
def get_file(file_id, path=None):
1584
calls.append(file_id)
1585
return real_source_get_file(file_id, path)
1586
source.get_file = get_file
1588
self.addCleanup(source.unlock)
1589
target = self.make_branch_and_tree('target')
1590
revision_tree = source.basis_tree()
1591
revision_tree.lock_read()
1592
self.addCleanup(revision_tree.unlock)
1593
build_tree(revision_tree, target, source)
1594
self.assertEqual(['file1-id'], calls)
1596
self.addCleanup(target.unlock)
1597
self.assertEqual([], list(target._iter_changes(revision_tree)))
1599
def test_build_tree_accelerator_tree_missing_file(self):
1600
source = self.make_branch_and_tree('source')
1601
self.build_tree_contents([('source/file1', 'A')])
1602
self.build_tree_contents([('source/file2', 'B')])
1603
source.add(['file1', 'file2'])
1604
source.commit('commit files')
1605
os.unlink('source/file1')
1606
source.remove(['file2'])
1607
target = self.make_branch_and_tree('target')
1608
revision_tree = source.basis_tree()
1609
revision_tree.lock_read()
1610
self.addCleanup(revision_tree.unlock)
1611
build_tree(revision_tree, target, source)
1613
self.addCleanup(target.unlock)
1614
self.assertEqual([], list(target._iter_changes(revision_tree)))
1616
def test_build_tree_accelerator_wrong_kind(self):
1617
self.requireFeature(SymlinkFeature)
1618
source = self.make_branch_and_tree('source')
1619
self.build_tree_contents([('source/file1', '')])
1620
self.build_tree_contents([('source/file2', '')])
1621
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1622
source.commit('commit files')
1623
os.unlink('source/file2')
1624
self.build_tree_contents([('source/file2/', 'C')])
1625
os.unlink('source/file1')
1626
os.symlink('file2', 'source/file1')
1628
real_source_get_file = source.get_file
1629
def get_file(file_id, path=None):
1630
calls.append(file_id)
1631
return real_source_get_file(file_id, path)
1632
source.get_file = get_file
1634
self.addCleanup(source.unlock)
1635
target = self.make_branch_and_tree('target')
1636
revision_tree = source.basis_tree()
1637
revision_tree.lock_read()
1638
self.addCleanup(revision_tree.unlock)
1639
build_tree(revision_tree, target, source)
1640
self.assertEqual([], calls)
1642
self.addCleanup(target.unlock)
1643
self.assertEqual([], list(target._iter_changes(revision_tree)))
1645
def test_build_tree_accelerator_tree_moved(self):
1646
source = self.make_branch_and_tree('source')
1647
self.build_tree_contents([('source/file1', 'A')])
1648
source.add(['file1'], ['file1-id'])
1649
source.commit('commit files')
1650
source.rename_one('file1', 'file2')
1652
self.addCleanup(source.unlock)
1653
target = self.make_branch_and_tree('target')
1654
revision_tree = source.basis_tree()
1655
revision_tree.lock_read()
1656
self.addCleanup(revision_tree.unlock)
1657
build_tree(revision_tree, target, source)
1659
self.addCleanup(target.unlock)
1660
self.assertEqual([], list(target._iter_changes(revision_tree)))
706
1663
class MockTransform(object):
708
1665
def has_named_child(self, by_parent, parent_id, name):
732
1691
self.assertEqual(name, 'name.~1~')
733
1692
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
734
1693
self.assertEqual(name, 'name.~4~')
1696
class TestFileMover(tests.TestCaseWithTransport):
1698
def test_file_mover(self):
1699
self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
1700
mover = _FileMover()
1701
mover.rename('a', 'q')
1702
self.failUnlessExists('q')
1703
self.failIfExists('a')
1704
self.failUnlessExists('q/b')
1705
self.failUnlessExists('c')
1706
self.failUnlessExists('c/d')
1708
def test_pre_delete_rollback(self):
1709
self.build_tree(['a/'])
1710
mover = _FileMover()
1711
mover.pre_delete('a', 'q')
1712
self.failUnlessExists('q')
1713
self.failIfExists('a')
1715
self.failIfExists('q')
1716
self.failUnlessExists('a')
1718
def test_apply_deletions(self):
1719
self.build_tree(['a/', 'b/'])
1720
mover = _FileMover()
1721
mover.pre_delete('a', 'q')
1722
mover.pre_delete('b', 'r')
1723
self.failUnlessExists('q')
1724
self.failUnlessExists('r')
1725
self.failIfExists('a')
1726
self.failIfExists('b')
1727
mover.apply_deletions()
1728
self.failIfExists('q')
1729
self.failIfExists('r')
1730
self.failIfExists('a')
1731
self.failIfExists('b')
1733
def test_file_mover_rollback(self):
1734
self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
1735
mover = _FileMover()
1736
mover.rename('c/d', 'c/f')
1737
mover.rename('c/e', 'c/d')
1739
mover.rename('a', 'c')
1740
except errors.FileExists, e:
1742
self.failUnlessExists('a')
1743
self.failUnlessExists('c/d')
1746
class Bogus(Exception):
1750
class TestTransformRollback(tests.TestCaseWithTransport):
1752
class ExceptionFileMover(_FileMover):
1754
def __init__(self, bad_source=None, bad_target=None):
1755
_FileMover.__init__(self)
1756
self.bad_source = bad_source
1757
self.bad_target = bad_target
1759
def rename(self, source, target):
1760
if (self.bad_source is not None and
1761
source.endswith(self.bad_source)):
1763
elif (self.bad_target is not None and
1764
target.endswith(self.bad_target)):
1767
_FileMover.rename(self, source, target)
1769
def test_rollback_rename(self):
1770
tree = self.make_branch_and_tree('.')
1771
self.build_tree(['a/', 'a/b'])
1772
tt = TreeTransform(tree)
1773
self.addCleanup(tt.finalize)
1774
a_id = tt.trans_id_tree_path('a')
1775
tt.adjust_path('c', tt.root, a_id)
1776
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1777
self.assertRaises(Bogus, tt.apply,
1778
_mover=self.ExceptionFileMover(bad_source='a'))
1779
self.failUnlessExists('a')
1780
self.failUnlessExists('a/b')
1782
self.failUnlessExists('c')
1783
self.failUnlessExists('c/d')
1785
def test_rollback_rename_into_place(self):
1786
tree = self.make_branch_and_tree('.')
1787
self.build_tree(['a/', 'a/b'])
1788
tt = TreeTransform(tree)
1789
self.addCleanup(tt.finalize)
1790
a_id = tt.trans_id_tree_path('a')
1791
tt.adjust_path('c', tt.root, a_id)
1792
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1793
self.assertRaises(Bogus, tt.apply,
1794
_mover=self.ExceptionFileMover(bad_target='c/d'))
1795
self.failUnlessExists('a')
1796
self.failUnlessExists('a/b')
1798
self.failUnlessExists('c')
1799
self.failUnlessExists('c/d')
1801
def test_rollback_deletion(self):
1802
tree = self.make_branch_and_tree('.')
1803
self.build_tree(['a/', 'a/b'])
1804
tt = TreeTransform(tree)
1805
self.addCleanup(tt.finalize)
1806
a_id = tt.trans_id_tree_path('a')
1807
tt.delete_contents(a_id)
1808
tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
1809
self.assertRaises(Bogus, tt.apply,
1810
_mover=self.ExceptionFileMover(bad_target='d'))
1811
self.failUnlessExists('a')
1812
self.failUnlessExists('a/b')
1814
def test_resolve_no_parent(self):
1815
wt = self.make_branch_and_tree('.')
1816
tt = TreeTransform(wt)
1817
self.addCleanup(tt.finalize)
1818
parent = tt.trans_id_file_id('parent-id')
1819
tt.new_file('file', parent, 'Contents')
1820
resolve_conflicts(tt)
1823
class TestTransformPreview(tests.TestCaseWithTransport):
1825
def create_tree(self):
1826
tree = self.make_branch_and_tree('.')
1827
self.build_tree_contents([('a', 'content 1')])
1828
tree.add('a', 'a-id')
1829
tree.commit('rev1', rev_id='rev1')
1830
return tree.branch.repository.revision_tree('rev1')
1832
def get_empty_preview(self):
1833
repository = self.make_repository('repo')
1834
tree = repository.revision_tree(_mod_revision.NULL_REVISION)
1835
return TransformPreview(tree)
1837
def test_transform_preview(self):
1838
revision_tree = self.create_tree()
1839
preview = TransformPreview(revision_tree)
1841
def test_transform_preview_tree(self):
1842
revision_tree = self.create_tree()
1843
preview = TransformPreview(revision_tree)
1844
preview.get_preview_tree()
1846
def test_transform_new_file(self):
1847
revision_tree = self.create_tree()
1848
preview = TransformPreview(revision_tree)
1849
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
1850
preview_tree = preview.get_preview_tree()
1851
self.assertEqual(preview_tree.kind('file2-id'), 'file')
1853
preview_tree.get_file('file2-id').read(), 'content B\n')
1855
def test_diff_preview_tree(self):
1856
revision_tree = self.create_tree()
1857
preview = TransformPreview(revision_tree)
1858
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
1859
preview_tree = preview.get_preview_tree()
1861
show_diff_trees(revision_tree, preview_tree, out)
1862
lines = out.getvalue().splitlines()
1863
self.assertEqual(lines[0], "=== added file 'file2'")
1864
# 3 lines of diff administrivia
1865
self.assertEqual(lines[4], "+content B")
1867
def test_transform_conflicts(self):
1868
revision_tree = self.create_tree()
1869
preview = TransformPreview(revision_tree)
1870
preview.new_file('a', preview.root, 'content 2')
1871
resolve_conflicts(preview)
1872
trans_id = preview.trans_id_file_id('a-id')
1873
self.assertEqual('a.moved', preview.final_name(trans_id))
1875
def get_tree_and_preview_tree(self):
1876
revision_tree = self.create_tree()
1877
preview = TransformPreview(revision_tree)
1878
a_trans_id = preview.trans_id_file_id('a-id')
1879
preview.delete_contents(a_trans_id)
1880
preview.create_file('b content', a_trans_id)
1881
preview_tree = preview.get_preview_tree()
1882
return revision_tree, preview_tree
1884
def test_iter_changes(self):
1885
revision_tree, preview_tree = self.get_tree_and_preview_tree()
1886
root = revision_tree.inventory.root.file_id
1887
self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
1888
(root, root), ('a', 'a'), ('file', 'file'),
1890
list(preview_tree._iter_changes(revision_tree)))
1892
def test_wrong_tree_value_error(self):
1893
revision_tree, preview_tree = self.get_tree_and_preview_tree()
1894
e = self.assertRaises(ValueError, preview_tree._iter_changes,
1896
self.assertEqual('from_tree must be transform source tree.', str(e))
1898
def test_include_unchanged_value_error(self):
1899
revision_tree, preview_tree = self.get_tree_and_preview_tree()
1900
e = self.assertRaises(ValueError, preview_tree._iter_changes,
1901
revision_tree, include_unchanged=True)
1902
self.assertEqual('include_unchanged is not supported', str(e))
1904
def test_specific_files(self):
1905
revision_tree, preview_tree = self.get_tree_and_preview_tree()
1906
e = self.assertRaises(ValueError, preview_tree._iter_changes,
1907
revision_tree, specific_files=['pete'])
1908
self.assertEqual('specific_files is not supported', str(e))
1910
def test_want_unversioned_value_error(self):
1911
revision_tree, preview_tree = self.get_tree_and_preview_tree()
1912
e = self.assertRaises(ValueError, preview_tree._iter_changes,
1913
revision_tree, want_unversioned=True)
1914
self.assertEqual('want_unversioned is not supported', str(e))
1916
def test_ignore_extra_trees_no_specific_files(self):
1917
# extra_trees is harmless without specific_files, so we'll silently
1918
# accept it, even though we won't use it.
1919
revision_tree, preview_tree = self.get_tree_and_preview_tree()
1920
preview_tree._iter_changes(revision_tree, extra_trees=[preview_tree])
1922
def test_ignore_require_versioned_no_specific_files(self):
1923
# require_versioned is meaningless without specific_files.
1924
revision_tree, preview_tree = self.get_tree_and_preview_tree()
1925
preview_tree._iter_changes(revision_tree, require_versioned=False)
1927
def test_ignore_pb(self):
1928
# pb could be supported, but TT.iter_changes doesn't support it.
1929
revision_tree, preview_tree = self.get_tree_and_preview_tree()
1930
preview_tree._iter_changes(revision_tree, pb=progress.DummyProgress())
1932
def test_kind(self):
1933
revision_tree = self.create_tree()
1934
preview = TransformPreview(revision_tree)
1935
preview.new_file('file', preview.root, 'contents', 'file-id')
1936
preview.new_directory('directory', preview.root, 'dir-id')
1937
preview_tree = preview.get_preview_tree()
1938
self.assertEqual('file', preview_tree.kind('file-id'))
1939
self.assertEqual('directory', preview_tree.kind('dir-id'))
1941
def test_get_file_mtime(self):
1942
preview = self.get_empty_preview()
1943
file_trans_id = preview.new_file('file', preview.root, 'contents',
1945
limbo_path = preview._limbo_name(file_trans_id)
1946
preview_tree = preview.get_preview_tree()
1947
self.assertEqual(os.stat(limbo_path).st_mtime,
1948
preview_tree.get_file_mtime('file-id'))
1950
def test_get_file(self):
1951
preview = self.get_empty_preview()
1952
preview.new_file('file', preview.root, 'contents', 'file-id')
1953
preview_tree = preview.get_preview_tree()
1954
tree_file = preview_tree.get_file('file-id')
1956
self.assertEqual('contents', tree_file.read())