185
232
transform3.delete_contents(oz_id)
186
233
self.assertEqual(transform3.find_conflicts(),
187
234
[('missing parent', oz_id)])
188
root_id = transform3.trans_id_tree_file_id('TREE_ROOT')
235
root_id = transform3.root
189
236
tip_id = transform3.trans_id_tree_file_id('tip-id')
190
237
transform3.adjust_path('tip', root_id, tip_id)
191
238
transform3.apply()
240
def test_conflict_on_case_insensitive(self):
241
tree = self.make_branch_and_tree('tree')
242
# Don't try this at home, kids!
243
# Force the tree to report that it is case sensitive, for conflict
245
tree.case_sensitive = True
246
transform = TreeTransform(tree)
247
self.addCleanup(transform.finalize)
248
transform.new_file('file', transform.root, 'content')
249
transform.new_file('FiLe', transform.root, 'content')
250
result = transform.find_conflicts()
251
self.assertEqual([], result)
252
# Force the tree to report that it is case insensitive, for conflict
254
tree.case_sensitive = False
255
result = transform.find_conflicts()
256
self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
258
def test_conflict_on_case_insensitive_existing(self):
259
tree = self.make_branch_and_tree('tree')
260
self.build_tree(['tree/FiLe'])
261
# Don't try this at home, kids!
262
# Force the tree to report that it is case sensitive, for conflict
264
tree.case_sensitive = True
265
transform = TreeTransform(tree)
266
self.addCleanup(transform.finalize)
267
transform.new_file('file', transform.root, 'content')
268
result = transform.find_conflicts()
269
self.assertEqual([], result)
270
# Force the tree to report that it is case insensitive, for conflict
272
tree.case_sensitive = False
273
result = transform.find_conflicts()
274
self.assertEqual([('duplicate', 'new-1', 'new-2', 'file')], result)
276
def test_resolve_case_insensitive_conflict(self):
277
tree = self.make_branch_and_tree('tree')
278
# Don't try this at home, kids!
279
# Force the tree to report that it is case insensitive, for conflict
281
tree.case_sensitive = False
282
transform = TreeTransform(tree)
283
self.addCleanup(transform.finalize)
284
transform.new_file('file', transform.root, 'content')
285
transform.new_file('FiLe', transform.root, 'content')
286
resolve_conflicts(transform)
288
self.failUnlessExists('tree/file')
289
self.failUnlessExists('tree/FiLe.moved')
291
def test_resolve_checkout_case_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,
302
pass_func=lambda t, c: resolve_checkout(t, c, []))
304
self.failUnlessExists('tree/file')
305
self.failUnlessExists('tree/FiLe.moved')
307
def test_apply_case_conflict(self):
308
"""Ensure that a transform with case conflicts can always be applied"""
309
tree = self.make_branch_and_tree('tree')
310
transform = TreeTransform(tree)
311
self.addCleanup(transform.finalize)
312
transform.new_file('file', transform.root, 'content')
313
transform.new_file('FiLe', transform.root, 'content')
314
dir = transform.new_directory('dir', transform.root)
315
transform.new_file('dirfile', dir, 'content')
316
transform.new_file('dirFiLe', dir, 'content')
317
resolve_conflicts(transform)
319
self.failUnlessExists('tree/file')
320
if not os.path.exists('tree/FiLe.moved'):
321
self.failUnlessExists('tree/FiLe')
322
self.failUnlessExists('tree/dir/dirfile')
323
if not os.path.exists('tree/dir/dirFiLe.moved'):
324
self.failUnlessExists('tree/dir/dirFiLe')
326
def test_case_insensitive_limbo(self):
327
tree = self.make_branch_and_tree('tree')
328
# Don't try this at home, kids!
329
# Force the tree to report that it is case insensitive
330
tree.case_sensitive = False
331
transform = TreeTransform(tree)
332
self.addCleanup(transform.finalize)
333
dir = transform.new_directory('dir', transform.root)
334
first = transform.new_file('file', dir, 'content')
335
second = transform.new_file('FiLe', dir, 'content')
336
self.assertContainsRe(transform._limbo_name(first), 'new-1/file')
337
self.assertNotContainsRe(transform._limbo_name(second), 'new-1/FiLe')
193
339
def test_add_del(self):
194
340
start, root = self.get_transform()
195
341
start.new_directory('a', root, 'a')
485
666
create.new_file('vfile', root, 'myfile-text', 'myfile-id')
486
667
create.new_file('uvfile', root, 'othertext')
488
self.assertEqual(find_interesting(wt, wt, ['vfile']),
490
self.assertRaises(NotVersionedError, find_interesting, wt, wt,
669
result = self.applyDeprecated(symbol_versioning.zero_fifteen,
670
find_interesting, wt, wt, ['vfile'])
671
self.assertEqual(result, set(['myfile-id']))
673
def test_set_executability_order(self):
674
"""Ensure that executability behaves the same, no matter what order.
676
- create file and set executability simultaneously
677
- create file and set executability afterward
678
- unsetting the executability of a file whose executability has not been
679
declared should throw an exception (this may happen when a
680
merge attempts to create a file with a duplicate ID)
682
transform, root = self.get_transform()
685
self.addCleanup(wt.unlock)
686
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
688
sac = transform.new_file('set_after_creation', root,
689
'Set after creation', 'sac')
690
transform.set_executability(True, sac)
691
uws = transform.new_file('unset_without_set', root, 'Unset badly',
693
self.assertRaises(KeyError, transform.set_executability, None, uws)
695
self.assertTrue(wt.is_executable('soc'))
696
self.assertTrue(wt.is_executable('sac'))
698
def test_preserve_mode(self):
699
"""File mode is preserved when replacing content"""
700
if sys.platform == 'win32':
701
raise TestSkipped('chmod has no effect on win32')
702
transform, root = self.get_transform()
703
transform.new_file('file1', root, 'contents', 'file1-id', True)
705
self.assertTrue(self.wt.is_executable('file1-id'))
706
transform, root = self.get_transform()
707
file1_id = transform.trans_id_tree_file_id('file1-id')
708
transform.delete_contents(file1_id)
709
transform.create_file('contents2', file1_id)
711
self.assertTrue(self.wt.is_executable('file1-id'))
713
def test__set_mode_stats_correctly(self):
714
"""_set_mode stats to determine file mode."""
715
if sys.platform == 'win32':
716
raise TestSkipped('chmod has no effect on win32')
720
def instrumented_stat(path):
721
stat_paths.append(path)
722
return real_stat(path)
724
transform, root = self.get_transform()
726
bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
727
file_id='bar-id-1', executable=False)
730
transform, root = self.get_transform()
731
bar1_id = transform.trans_id_tree_path('bar')
732
bar2_id = transform.trans_id_tree_path('bar2')
734
os.stat = instrumented_stat
735
transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
740
bar1_abspath = self.wt.abspath('bar')
741
self.assertEqual([bar1_abspath], stat_paths)
743
def test_iter_changes(self):
744
self.wt.set_root_id('eert_toor')
745
transform, root = self.get_transform()
746
transform.new_file('old', root, 'blah', 'id-1', True)
748
transform, root = self.get_transform()
750
self.assertEqual([], list(transform._iter_changes()))
751
old = transform.trans_id_tree_file_id('id-1')
752
transform.unversion_file(old)
753
self.assertEqual([('id-1', ('old', None), False, (True, False),
754
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
755
(True, True))], list(transform._iter_changes()))
756
transform.new_directory('new', root, 'id-1')
757
self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
758
('eert_toor', 'eert_toor'), ('old', 'new'),
759
('file', 'directory'),
760
(True, False))], list(transform._iter_changes()))
764
def test_iter_changes_new(self):
765
self.wt.set_root_id('eert_toor')
766
transform, root = self.get_transform()
767
transform.new_file('old', root, 'blah')
769
transform, root = self.get_transform()
771
old = transform.trans_id_tree_path('old')
772
transform.version_file('id-1', old)
773
self.assertEqual([('id-1', (None, 'old'), False, (False, True),
774
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
775
(False, False))], list(transform._iter_changes()))
779
def test_iter_changes_modifications(self):
780
self.wt.set_root_id('eert_toor')
781
transform, root = self.get_transform()
782
transform.new_file('old', root, 'blah', 'id-1')
783
transform.new_file('new', root, 'blah')
784
transform.new_directory('subdir', root, 'subdir-id')
786
transform, root = self.get_transform()
788
old = transform.trans_id_tree_path('old')
789
subdir = transform.trans_id_tree_file_id('subdir-id')
790
new = transform.trans_id_tree_path('new')
791
self.assertEqual([], list(transform._iter_changes()))
794
transform.delete_contents(old)
795
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
796
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
797
(False, False))], list(transform._iter_changes()))
800
transform.create_file('blah', old)
801
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
802
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
803
(False, False))], list(transform._iter_changes()))
804
transform.cancel_deletion(old)
805
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
806
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
807
(False, False))], list(transform._iter_changes()))
808
transform.cancel_creation(old)
810
# move file_id to a different file
811
self.assertEqual([], list(transform._iter_changes()))
812
transform.unversion_file(old)
813
transform.version_file('id-1', new)
814
transform.adjust_path('old', root, new)
815
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
816
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
817
(False, False))], list(transform._iter_changes()))
818
transform.cancel_versioning(new)
819
transform._removed_id = set()
822
self.assertEqual([], list(transform._iter_changes()))
823
transform.set_executability(True, old)
824
self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
825
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
826
(False, True))], list(transform._iter_changes()))
827
transform.set_executability(None, old)
830
self.assertEqual([], list(transform._iter_changes()))
831
transform.adjust_path('new', root, old)
832
transform._new_parent = {}
833
self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
834
('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
835
(False, False))], list(transform._iter_changes()))
836
transform._new_name = {}
839
self.assertEqual([], list(transform._iter_changes()))
840
transform.adjust_path('new', subdir, old)
841
transform._new_name = {}
842
self.assertEqual([('id-1', ('old', 'subdir/old'), False,
843
(True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
844
('file', 'file'), (False, False))],
845
list(transform._iter_changes()))
846
transform._new_path = {}
851
def test_iter_changes_modified_bleed(self):
852
self.wt.set_root_id('eert_toor')
853
"""Modified flag should not bleed from one change to another"""
854
# unfortunately, we have no guarantee that file1 (which is modified)
855
# will be applied before file2. And if it's applied after file2, it
856
# obviously can't bleed into file2's change output. But for now, it
858
transform, root = self.get_transform()
859
transform.new_file('file1', root, 'blah', 'id-1')
860
transform.new_file('file2', root, 'blah', 'id-2')
862
transform, root = self.get_transform()
864
transform.delete_contents(transform.trans_id_file_id('id-1'))
865
transform.set_executability(True,
866
transform.trans_id_file_id('id-2'))
867
self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
868
('eert_toor', 'eert_toor'), ('file1', u'file1'),
869
('file', None), (False, False)),
870
('id-2', (u'file2', u'file2'), False, (True, True),
871
('eert_toor', 'eert_toor'), ('file2', u'file2'),
872
('file', 'file'), (False, True))],
873
list(transform._iter_changes()))
877
def test_iter_changes_move_missing(self):
878
"""Test moving ids with no files around"""
879
self.wt.set_root_id('toor_eert')
880
# Need two steps because versioning a non-existant file is a conflict.
881
transform, root = self.get_transform()
882
transform.new_directory('floater', root, 'floater-id')
884
transform, root = self.get_transform()
885
transform.delete_contents(transform.trans_id_tree_path('floater'))
887
transform, root = self.get_transform()
888
floater = transform.trans_id_tree_path('floater')
890
transform.adjust_path('flitter', root, floater)
891
self.assertEqual([('floater-id', ('floater', 'flitter'), False,
892
(True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
893
(None, None), (False, False))], list(transform._iter_changes()))
897
def test_iter_changes_pointless(self):
898
"""Ensure that no-ops are not treated as modifications"""
899
self.wt.set_root_id('eert_toor')
900
transform, root = self.get_transform()
901
transform.new_file('old', root, 'blah', 'id-1')
902
transform.new_directory('subdir', root, 'subdir-id')
904
transform, root = self.get_transform()
906
old = transform.trans_id_tree_path('old')
907
subdir = transform.trans_id_tree_file_id('subdir-id')
908
self.assertEqual([], list(transform._iter_changes()))
909
transform.delete_contents(subdir)
910
transform.create_directory(subdir)
911
transform.set_executability(False, old)
912
transform.unversion_file(old)
913
transform.version_file('id-1', old)
914
transform.adjust_path('old', root, old)
915
self.assertEqual([], list(transform._iter_changes()))
919
def test_rename_count(self):
920
transform, root = self.get_transform()
921
transform.new_file('name1', root, 'contents')
922
self.assertEqual(transform.rename_count, 0)
924
self.assertEqual(transform.rename_count, 1)
925
transform2, root = self.get_transform()
926
transform2.adjust_path('name2', root,
927
transform2.trans_id_tree_path('name1'))
928
self.assertEqual(transform2.rename_count, 0)
930
self.assertEqual(transform2.rename_count, 2)
932
def test_change_parent(self):
933
"""Ensure that after we change a parent, the results are still right.
935
Renames and parent changes on pending transforms can happen as part
936
of conflict resolution, and are explicitly permitted by the
939
This test ensures they work correctly with the rename-avoidance
942
transform, root = self.get_transform()
943
parent1 = transform.new_directory('parent1', root)
944
child1 = transform.new_file('child1', parent1, 'contents')
945
parent2 = transform.new_directory('parent2', root)
946
transform.adjust_path('child1', parent2, child1)
948
self.failIfExists(self.wt.abspath('parent1/child1'))
949
self.failUnlessExists(self.wt.abspath('parent2/child1'))
950
# rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
951
# no rename for child1 (counting only renames during apply)
952
self.failUnlessEqual(2, transform.rename_count)
954
def test_cancel_parent(self):
955
"""Cancelling a parent doesn't cause deletion of a non-empty directory
957
This is like the test_change_parent, except that we cancel the parent
958
before adjusting the path. The transform must detect that the
959
directory is non-empty, and move children to safe locations.
961
transform, root = self.get_transform()
962
parent1 = transform.new_directory('parent1', root)
963
child1 = transform.new_file('child1', parent1, 'contents')
964
child2 = transform.new_file('child2', parent1, 'contents')
966
transform.cancel_creation(parent1)
968
self.fail('Failed to move child1 before deleting parent1')
969
transform.cancel_creation(child2)
970
transform.create_directory(parent1)
972
transform.cancel_creation(parent1)
973
# If the transform incorrectly believes that child2 is still in
974
# parent1's limbo directory, it will try to rename it and fail
975
# because was already moved by the first cancel_creation.
977
self.fail('Transform still thinks child2 is a child of parent1')
978
parent2 = transform.new_directory('parent2', root)
979
transform.adjust_path('child1', parent2, child1)
981
self.failIfExists(self.wt.abspath('parent1'))
982
self.failUnlessExists(self.wt.abspath('parent2/child1'))
983
# rename limbo/new-3 => parent2, rename limbo/new-2 => child1
984
self.failUnlessEqual(2, transform.rename_count)
986
def test_adjust_and_cancel(self):
987
"""Make sure adjust_path keeps track of limbo children properly"""
988
transform, root = self.get_transform()
989
parent1 = transform.new_directory('parent1', root)
990
child1 = transform.new_file('child1', parent1, 'contents')
991
parent2 = transform.new_directory('parent2', root)
992
transform.adjust_path('child1', parent2, child1)
993
transform.cancel_creation(child1)
995
transform.cancel_creation(parent1)
996
# if the transform thinks child1 is still in parent1's limbo
997
# directory, it will attempt to move it and fail.
999
self.fail('Transform still thinks child1 is a child of parent1')
1000
transform.finalize()
1002
def test_noname_contents(self):
1003
"""TreeTransform should permit deferring naming files."""
1004
transform, root = self.get_transform()
1005
parent = transform.trans_id_file_id('parent-id')
1007
transform.create_directory(parent)
1009
self.fail("Can't handle contents with no name")
1010
transform.finalize()
1012
def test_noname_contents_nested(self):
1013
"""TreeTransform should permit deferring naming files."""
1014
transform, root = self.get_transform()
1015
parent = transform.trans_id_file_id('parent-id')
1017
transform.create_directory(parent)
1019
self.fail("Can't handle contents with no name")
1020
child = transform.new_directory('child', parent)
1021
transform.adjust_path('parent', root, parent)
1023
self.failUnlessExists(self.wt.abspath('parent/child'))
1024
self.assertEqual(1, transform.rename_count)
1026
def test_reuse_name(self):
1027
"""Avoid reusing the same limbo name for different files"""
1028
transform, root = self.get_transform()
1029
parent = transform.new_directory('parent', root)
1030
child1 = transform.new_directory('child', parent)
1032
child2 = transform.new_directory('child', parent)
1034
self.fail('Tranform tried to use the same limbo name twice')
1035
transform.adjust_path('child2', parent, child2)
1037
# limbo/new-1 => parent, limbo/new-3 => parent/child2
1038
# child2 is put into top-level limbo because child1 has already
1039
# claimed the direct limbo path when child2 is created. There is no
1040
# advantage in renaming files once they're in top-level limbo, except
1042
self.assertEqual(2, transform.rename_count)
1044
def test_reuse_when_first_moved(self):
1045
"""Don't avoid direct paths when it is safe to use them"""
1046
transform, root = self.get_transform()
1047
parent = transform.new_directory('parent', root)
1048
child1 = transform.new_directory('child', parent)
1049
transform.adjust_path('child1', parent, child1)
1050
child2 = transform.new_directory('child', parent)
1052
# limbo/new-1 => parent
1053
self.assertEqual(1, transform.rename_count)
1055
def test_reuse_after_cancel(self):
1056
"""Don't avoid direct paths when it is safe to use them"""
1057
transform, root = self.get_transform()
1058
parent2 = transform.new_directory('parent2', root)
1059
child1 = transform.new_directory('child1', parent2)
1060
transform.cancel_creation(parent2)
1061
transform.create_directory(parent2)
1062
child2 = transform.new_directory('child1', parent2)
1063
transform.adjust_path('child2', parent2, child1)
1065
# limbo/new-1 => parent2, limbo/new-2 => parent2/child1
1066
self.assertEqual(2, transform.rename_count)
1068
def test_finalize_order(self):
1069
"""Finalize must be done in child-to-parent order"""
1070
transform, root = self.get_transform()
1071
parent = transform.new_directory('parent', root)
1072
child = transform.new_directory('child', parent)
1074
transform.finalize()
1076
self.fail('Tried to remove parent before child1')
1078
def test_cancel_with_cancelled_child_should_succeed(self):
1079
transform, root = self.get_transform()
1080
parent = transform.new_directory('parent', root)
1081
child = transform.new_directory('child', parent)
1082
transform.cancel_creation(child)
1083
transform.cancel_creation(parent)
1084
transform.finalize()
1086
def test_change_entry(self):
1087
txt = 'bzrlib.transform.change_entry was deprecated in version 0.90.'
1088
self.callDeprecated([txt], change_entry, None, None, None, None, None,
494
1092
class TransformGroup(object):
495
def __init__(self, dirname):
1093
def __init__(self, dirname, root_id):
496
1094
self.name = dirname
497
1095
os.mkdir(dirname)
498
1096
self.wt = BzrDir.create_standalone_workingtree(dirname)
1097
self.wt.set_root_id(root_id)
499
1098
self.b = self.wt.branch
500
1099
self.tt = TreeTransform(self.wt)
501
1100
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
503
1103
def conflict_text(tree, merge):
504
1104
template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
505
1105
return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
698
1302
a.add(['foo', 'foo/bar', 'foo/baz'])
699
1303
a.commit('initial commit')
700
1304
b = BzrDir.create_standalone_workingtree('b')
701
build_tree(a.basis_tree(), b)
1305
basis = a.basis_tree()
1307
self.addCleanup(basis.unlock)
1308
build_tree(basis, b)
702
1309
self.assertIs(os.path.isdir('b/foo'), True)
703
1310
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
704
1311
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1313
def test_build_with_references(self):
1314
tree = self.make_branch_and_tree('source',
1315
format='dirstate-with-subtree')
1316
subtree = self.make_branch_and_tree('source/subtree',
1317
format='dirstate-with-subtree')
1318
tree.add_reference(subtree)
1319
tree.commit('a revision')
1320
tree.branch.create_checkout('target')
1321
self.failUnlessExists('target')
1322
self.failUnlessExists('target/subtree')
1324
def test_file_conflict_handling(self):
1325
"""Ensure that when building trees, conflict handling is done"""
1326
source = self.make_branch_and_tree('source')
1327
target = self.make_branch_and_tree('target')
1328
self.build_tree(['source/file', 'target/file'])
1329
source.add('file', 'new-file')
1330
source.commit('added file')
1331
build_tree(source.basis_tree(), target)
1332
self.assertEqual([DuplicateEntry('Moved existing file to',
1333
'file.moved', 'file', None, 'new-file')],
1335
target2 = self.make_branch_and_tree('target2')
1336
target_file = file('target2/file', 'wb')
1338
source_file = file('source/file', 'rb')
1340
target_file.write(source_file.read())
1345
build_tree(source.basis_tree(), target2)
1346
self.assertEqual([], target2.conflicts())
1348
def test_symlink_conflict_handling(self):
1349
"""Ensure that when building trees, conflict handling is done"""
1350
self.requireFeature(SymlinkFeature)
1351
source = self.make_branch_and_tree('source')
1352
os.symlink('foo', 'source/symlink')
1353
source.add('symlink', 'new-symlink')
1354
source.commit('added file')
1355
target = self.make_branch_and_tree('target')
1356
os.symlink('bar', 'target/symlink')
1357
build_tree(source.basis_tree(), target)
1358
self.assertEqual([DuplicateEntry('Moved existing file to',
1359
'symlink.moved', 'symlink', None, 'new-symlink')],
1361
target = self.make_branch_and_tree('target2')
1362
os.symlink('foo', 'target2/symlink')
1363
build_tree(source.basis_tree(), target)
1364
self.assertEqual([], target.conflicts())
1366
def test_directory_conflict_handling(self):
1367
"""Ensure that when building trees, conflict handling is done"""
1368
source = self.make_branch_and_tree('source')
1369
target = self.make_branch_and_tree('target')
1370
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1371
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1372
source.commit('added file')
1373
build_tree(source.basis_tree(), target)
1374
self.assertEqual([], target.conflicts())
1375
self.failUnlessExists('target/dir1/file')
1377
# Ensure contents are merged
1378
target = self.make_branch_and_tree('target2')
1379
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1380
build_tree(source.basis_tree(), target)
1381
self.assertEqual([], target.conflicts())
1382
self.failUnlessExists('target2/dir1/file2')
1383
self.failUnlessExists('target2/dir1/file')
1385
# Ensure new contents are suppressed for existing branches
1386
target = self.make_branch_and_tree('target3')
1387
self.make_branch('target3/dir1')
1388
self.build_tree(['target3/dir1/file2'])
1389
build_tree(source.basis_tree(), target)
1390
self.failIfExists('target3/dir1/file')
1391
self.failUnlessExists('target3/dir1/file2')
1392
self.failUnlessExists('target3/dir1.diverted/file')
1393
self.assertEqual([DuplicateEntry('Diverted to',
1394
'dir1.diverted', 'dir1', 'new-dir1', None)],
1397
target = self.make_branch_and_tree('target4')
1398
self.build_tree(['target4/dir1/'])
1399
self.make_branch('target4/dir1/file')
1400
build_tree(source.basis_tree(), target)
1401
self.failUnlessExists('target4/dir1/file')
1402
self.assertEqual('directory', file_kind('target4/dir1/file'))
1403
self.failUnlessExists('target4/dir1/file.diverted')
1404
self.assertEqual([DuplicateEntry('Diverted to',
1405
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1408
def test_mixed_conflict_handling(self):
1409
"""Ensure that when building trees, conflict handling is done"""
1410
source = self.make_branch_and_tree('source')
1411
target = self.make_branch_and_tree('target')
1412
self.build_tree(['source/name', 'target/name/'])
1413
source.add('name', 'new-name')
1414
source.commit('added file')
1415
build_tree(source.basis_tree(), target)
1416
self.assertEqual([DuplicateEntry('Moved existing file to',
1417
'name.moved', 'name', None, 'new-name')], target.conflicts())
1419
def test_raises_in_populated(self):
1420
source = self.make_branch_and_tree('source')
1421
self.build_tree(['source/name'])
1423
source.commit('added name')
1424
target = self.make_branch_and_tree('target')
1425
self.build_tree(['target/name'])
1427
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1428
build_tree, source.basis_tree(), target)
1430
def test_build_tree_rename_count(self):
1431
source = self.make_branch_and_tree('source')
1432
self.build_tree(['source/file1', 'source/dir1/'])
1433
source.add(['file1', 'dir1'])
1434
source.commit('add1')
1435
target1 = self.make_branch_and_tree('target1')
1436
transform_result = build_tree(source.basis_tree(), target1)
1437
self.assertEqual(2, transform_result.rename_count)
1439
self.build_tree(['source/dir1/file2'])
1440
source.add(['dir1/file2'])
1441
source.commit('add3')
1442
target2 = self.make_branch_and_tree('target2')
1443
transform_result = build_tree(source.basis_tree(), target2)
1444
# children of non-root directories should not be renamed
1445
self.assertEqual(2, transform_result.rename_count)
706
1448
class MockTransform(object):
708
1450
def has_named_child(self, by_parent, parent_id, name):
732
1475
self.assertEqual(name, 'name.~1~')
733
1476
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
734
1477
self.assertEqual(name, 'name.~4~')
1480
class TestFileMover(tests.TestCaseWithTransport):
1482
def test_file_mover(self):
1483
self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
1484
mover = _FileMover()
1485
mover.rename('a', 'q')
1486
self.failUnlessExists('q')
1487
self.failIfExists('a')
1488
self.failUnlessExists('q/b')
1489
self.failUnlessExists('c')
1490
self.failUnlessExists('c/d')
1492
def test_pre_delete_rollback(self):
1493
self.build_tree(['a/'])
1494
mover = _FileMover()
1495
mover.pre_delete('a', 'q')
1496
self.failUnlessExists('q')
1497
self.failIfExists('a')
1499
self.failIfExists('q')
1500
self.failUnlessExists('a')
1502
def test_apply_deletions(self):
1503
self.build_tree(['a/', 'b/'])
1504
mover = _FileMover()
1505
mover.pre_delete('a', 'q')
1506
mover.pre_delete('b', 'r')
1507
self.failUnlessExists('q')
1508
self.failUnlessExists('r')
1509
self.failIfExists('a')
1510
self.failIfExists('b')
1511
mover.apply_deletions()
1512
self.failIfExists('q')
1513
self.failIfExists('r')
1514
self.failIfExists('a')
1515
self.failIfExists('b')
1517
def test_file_mover_rollback(self):
1518
self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
1519
mover = _FileMover()
1520
mover.rename('c/d', 'c/f')
1521
mover.rename('c/e', 'c/d')
1523
mover.rename('a', 'c')
1526
self.failUnlessExists('a')
1527
self.failUnlessExists('c/d')
1530
class Bogus(Exception):
1534
class TestTransformRollback(tests.TestCaseWithTransport):
1536
class ExceptionFileMover(_FileMover):
1538
def __init__(self, bad_source=None, bad_target=None):
1539
_FileMover.__init__(self)
1540
self.bad_source = bad_source
1541
self.bad_target = bad_target
1543
def rename(self, source, target):
1544
if (self.bad_source is not None and
1545
source.endswith(self.bad_source)):
1547
elif (self.bad_target is not None and
1548
target.endswith(self.bad_target)):
1551
_FileMover.rename(self, source, target)
1553
def test_rollback_rename(self):
1554
tree = self.make_branch_and_tree('.')
1555
self.build_tree(['a/', 'a/b'])
1556
tt = TreeTransform(tree)
1557
self.addCleanup(tt.finalize)
1558
a_id = tt.trans_id_tree_path('a')
1559
tt.adjust_path('c', tt.root, a_id)
1560
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1561
self.assertRaises(Bogus, tt.apply,
1562
_mover=self.ExceptionFileMover(bad_source='a'))
1563
self.failUnlessExists('a')
1564
self.failUnlessExists('a/b')
1566
self.failUnlessExists('c')
1567
self.failUnlessExists('c/d')
1569
def test_rollback_rename_into_place(self):
1570
tree = self.make_branch_and_tree('.')
1571
self.build_tree(['a/', 'a/b'])
1572
tt = TreeTransform(tree)
1573
self.addCleanup(tt.finalize)
1574
a_id = tt.trans_id_tree_path('a')
1575
tt.adjust_path('c', tt.root, a_id)
1576
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1577
self.assertRaises(Bogus, tt.apply,
1578
_mover=self.ExceptionFileMover(bad_target='c/d'))
1579
self.failUnlessExists('a')
1580
self.failUnlessExists('a/b')
1582
self.failUnlessExists('c')
1583
self.failUnlessExists('c/d')
1585
def test_rollback_deletion(self):
1586
tree = self.make_branch_and_tree('.')
1587
self.build_tree(['a/', 'a/b'])
1588
tt = TreeTransform(tree)
1589
self.addCleanup(tt.finalize)
1590
a_id = tt.trans_id_tree_path('a')
1591
tt.delete_contents(a_id)
1592
tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
1593
self.assertRaises(Bogus, tt.apply,
1594
_mover=self.ExceptionFileMover(bad_target='d'))
1595
self.failUnlessExists('a')
1596
self.failUnlessExists('a/b')
1598
def test_resolve_no_parent(self):
1599
wt = self.make_branch_and_tree('.')
1600
tt = TreeTransform(wt)
1601
self.addCleanup(tt.finalize)
1602
parent = tt.trans_id_file_id('parent-id')
1603
tt.new_file('file', parent, 'Contents')
1604
resolve_conflicts(tt)