485
543
create.new_file('vfile', root, 'myfile-text', 'myfile-id')
486
544
create.new_file('uvfile', root, 'othertext')
488
self.assertEqual(find_interesting(wt, wt, ['vfile']),
490
self.assertRaises(NotVersionedError, find_interesting, wt, wt,
546
result = self.applyDeprecated(symbol_versioning.zero_fifteen,
547
find_interesting, wt, wt, ['vfile'])
548
self.assertEqual(result, set(['myfile-id']))
550
def test_set_executability_order(self):
551
"""Ensure that executability behaves the same, no matter what order.
553
- create file and set executability simultaneously
554
- create file and set executability afterward
555
- unsetting the executability of a file whose executability has not been
556
declared should throw an exception (this may happen when a
557
merge attempts to create a file with a duplicate ID)
559
transform, root = self.get_transform()
561
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
563
sac = transform.new_file('set_after_creation', root,
564
'Set after creation', 'sac')
565
transform.set_executability(True, sac)
566
uws = transform.new_file('unset_without_set', root, 'Unset badly',
568
self.assertRaises(KeyError, transform.set_executability, None, uws)
570
self.assertTrue(wt.is_executable('soc'))
571
self.assertTrue(wt.is_executable('sac'))
573
def test_preserve_mode(self):
574
"""File mode is preserved when replacing content"""
575
if sys.platform == 'win32':
576
raise TestSkipped('chmod has no effect on win32')
577
transform, root = self.get_transform()
578
transform.new_file('file1', root, 'contents', 'file1-id', True)
580
self.assertTrue(self.wt.is_executable('file1-id'))
581
transform, root = self.get_transform()
582
file1_id = transform.trans_id_tree_file_id('file1-id')
583
transform.delete_contents(file1_id)
584
transform.create_file('contents2', file1_id)
586
self.assertTrue(self.wt.is_executable('file1-id'))
588
def test__set_mode_stats_correctly(self):
589
"""_set_mode stats to determine file mode."""
590
if sys.platform == 'win32':
591
raise TestSkipped('chmod has no effect on win32')
595
def instrumented_stat(path):
596
stat_paths.append(path)
597
return real_stat(path)
599
transform, root = self.get_transform()
601
bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
602
file_id='bar-id-1', executable=False)
605
transform, root = self.get_transform()
606
bar1_id = transform.trans_id_tree_path('bar')
607
bar2_id = transform.trans_id_tree_path('bar2')
609
os.stat = instrumented_stat
610
transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
615
bar1_abspath = self.wt.abspath('bar')
616
self.assertEqual([bar1_abspath], stat_paths)
618
def test_iter_changes(self):
619
self.wt.set_root_id('eert_toor')
620
transform, root = self.get_transform()
621
transform.new_file('old', root, 'blah', 'id-1', True)
623
transform, root = self.get_transform()
625
self.assertEqual([], list(transform._iter_changes()))
626
old = transform.trans_id_tree_file_id('id-1')
627
transform.unversion_file(old)
628
self.assertEqual([('id-1', ('old', None), False, (True, False),
629
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
630
(True, True))], list(transform._iter_changes()))
631
transform.new_directory('new', root, 'id-1')
632
self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
633
('eert_toor', 'eert_toor'), ('old', 'new'),
634
('file', 'directory'),
635
(True, False))], list(transform._iter_changes()))
639
def test_iter_changes_new(self):
640
self.wt.set_root_id('eert_toor')
641
transform, root = self.get_transform()
642
transform.new_file('old', root, 'blah')
644
transform, root = self.get_transform()
646
old = transform.trans_id_tree_path('old')
647
transform.version_file('id-1', old)
648
self.assertEqual([('id-1', (None, 'old'), False, (False, True),
649
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
650
(False, False))], list(transform._iter_changes()))
654
def test_iter_changes_modifications(self):
655
self.wt.set_root_id('eert_toor')
656
transform, root = self.get_transform()
657
transform.new_file('old', root, 'blah', 'id-1')
658
transform.new_file('new', root, 'blah')
659
transform.new_directory('subdir', root, 'subdir-id')
661
transform, root = self.get_transform()
663
old = transform.trans_id_tree_path('old')
664
subdir = transform.trans_id_tree_file_id('subdir-id')
665
new = transform.trans_id_tree_path('new')
666
self.assertEqual([], list(transform._iter_changes()))
669
transform.delete_contents(old)
670
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
671
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
672
(False, False))], list(transform._iter_changes()))
675
transform.create_file('blah', old)
676
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
677
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
678
(False, False))], list(transform._iter_changes()))
679
transform.cancel_deletion(old)
680
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
681
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
682
(False, False))], list(transform._iter_changes()))
683
transform.cancel_creation(old)
685
# move file_id to a different file
686
self.assertEqual([], list(transform._iter_changes()))
687
transform.unversion_file(old)
688
transform.version_file('id-1', new)
689
transform.adjust_path('old', root, new)
690
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
691
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
692
(False, False))], list(transform._iter_changes()))
693
transform.cancel_versioning(new)
694
transform._removed_id = set()
697
self.assertEqual([], list(transform._iter_changes()))
698
transform.set_executability(True, old)
699
self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
700
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
701
(False, True))], list(transform._iter_changes()))
702
transform.set_executability(None, old)
705
self.assertEqual([], list(transform._iter_changes()))
706
transform.adjust_path('new', root, old)
707
transform._new_parent = {}
708
self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
709
('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
710
(False, False))], list(transform._iter_changes()))
711
transform._new_name = {}
714
self.assertEqual([], list(transform._iter_changes()))
715
transform.adjust_path('new', subdir, old)
716
transform._new_name = {}
717
self.assertEqual([('id-1', ('old', 'subdir/old'), False,
718
(True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
719
('file', 'file'), (False, False))],
720
list(transform._iter_changes()))
721
transform._new_path = {}
726
def test_iter_changes_modified_bleed(self):
727
self.wt.set_root_id('eert_toor')
728
"""Modified flag should not bleed from one change to another"""
729
# unfortunately, we have no guarantee that file1 (which is modified)
730
# will be applied before file2. And if it's applied after file2, it
731
# obviously can't bleed into file2's change output. But for now, it
733
transform, root = self.get_transform()
734
transform.new_file('file1', root, 'blah', 'id-1')
735
transform.new_file('file2', root, 'blah', 'id-2')
737
transform, root = self.get_transform()
739
transform.delete_contents(transform.trans_id_file_id('id-1'))
740
transform.set_executability(True,
741
transform.trans_id_file_id('id-2'))
742
self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
743
('eert_toor', 'eert_toor'), ('file1', u'file1'),
744
('file', None), (False, False)),
745
('id-2', (u'file2', u'file2'), False, (True, True),
746
('eert_toor', 'eert_toor'), ('file2', u'file2'),
747
('file', 'file'), (False, True))],
748
list(transform._iter_changes()))
752
def test_iter_changes_move_missing(self):
753
"""Test moving ids with no files around"""
754
self.wt.set_root_id('toor_eert')
755
# Need two steps because versioning a non-existant file is a conflict.
756
transform, root = self.get_transform()
757
transform.new_directory('floater', root, 'floater-id')
759
transform, root = self.get_transform()
760
transform.delete_contents(transform.trans_id_tree_path('floater'))
762
transform, root = self.get_transform()
763
floater = transform.trans_id_tree_path('floater')
765
transform.adjust_path('flitter', root, floater)
766
self.assertEqual([('floater-id', ('floater', 'flitter'), False,
767
(True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
768
(None, None), (False, False))], list(transform._iter_changes()))
772
def test_iter_changes_pointless(self):
773
"""Ensure that no-ops are not treated as modifications"""
774
self.wt.set_root_id('eert_toor')
775
transform, root = self.get_transform()
776
transform.new_file('old', root, 'blah', 'id-1')
777
transform.new_directory('subdir', root, 'subdir-id')
779
transform, root = self.get_transform()
781
old = transform.trans_id_tree_path('old')
782
subdir = transform.trans_id_tree_file_id('subdir-id')
783
self.assertEqual([], list(transform._iter_changes()))
784
transform.delete_contents(subdir)
785
transform.create_directory(subdir)
786
transform.set_executability(False, old)
787
transform.unversion_file(old)
788
transform.version_file('id-1', old)
789
transform.adjust_path('old', root, old)
790
self.assertEqual([], list(transform._iter_changes()))
794
def test_rename_count(self):
795
transform, root = self.get_transform()
796
transform.new_file('name1', root, 'contents')
797
self.assertEqual(transform.rename_count, 0)
799
self.assertEqual(transform.rename_count, 1)
800
transform2, root = self.get_transform()
801
transform2.adjust_path('name2', root,
802
transform2.trans_id_tree_path('name1'))
803
self.assertEqual(transform2.rename_count, 0)
805
self.assertEqual(transform2.rename_count, 2)
807
def test_change_parent(self):
808
"""Ensure that after we change a parent, the results are still right.
810
Renames and parent changes on pending transforms can happen as part
811
of conflict resolution, and are explicitly permitted by the
814
This test ensures they work correctly with the rename-avoidance
817
transform, root = self.get_transform()
818
parent1 = transform.new_directory('parent1', root)
819
child1 = transform.new_file('child1', parent1, 'contents')
820
parent2 = transform.new_directory('parent2', root)
821
transform.adjust_path('child1', parent2, child1)
823
self.failIfExists(self.wt.abspath('parent1/child1'))
824
self.failUnlessExists(self.wt.abspath('parent2/child1'))
825
# rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
826
# no rename for child1 (counting only renames during apply)
827
self.failUnlessEqual(2, transform.rename_count)
829
def test_cancel_parent(self):
830
"""Cancelling a parent doesn't cause deletion of a non-empty directory
832
This is like the test_change_parent, except that we cancel the parent
833
before adjusting the path. The transform must detect that the
834
directory is non-empty, and move children to safe locations.
836
transform, root = self.get_transform()
837
parent1 = transform.new_directory('parent1', root)
838
child1 = transform.new_file('child1', parent1, 'contents')
839
child2 = transform.new_file('child2', parent1, 'contents')
841
transform.cancel_creation(parent1)
843
self.fail('Failed to move child1 before deleting parent1')
844
transform.cancel_creation(child2)
845
transform.create_directory(parent1)
847
transform.cancel_creation(parent1)
848
# If the transform incorrectly believes that child2 is still in
849
# parent1's limbo directory, it will try to rename it and fail
850
# because was already moved by the first cancel_creation.
852
self.fail('Transform still thinks child2 is a child of parent1')
853
parent2 = transform.new_directory('parent2', root)
854
transform.adjust_path('child1', parent2, child1)
856
self.failIfExists(self.wt.abspath('parent1'))
857
self.failUnlessExists(self.wt.abspath('parent2/child1'))
858
# rename limbo/new-3 => parent2, rename limbo/new-2 => child1
859
self.failUnlessEqual(2, transform.rename_count)
861
def test_adjust_and_cancel(self):
862
"""Make sure adjust_path keeps track of limbo children properly"""
863
transform, root = self.get_transform()
864
parent1 = transform.new_directory('parent1', root)
865
child1 = transform.new_file('child1', parent1, 'contents')
866
parent2 = transform.new_directory('parent2', root)
867
transform.adjust_path('child1', parent2, child1)
868
transform.cancel_creation(child1)
870
transform.cancel_creation(parent1)
871
# if the transform thinks child1 is still in parent1's limbo
872
# directory, it will attempt to move it and fail.
874
self.fail('Transform still thinks child1 is a child of parent1')
877
def test_noname_contents(self):
878
"""TreeTransform should permit deferring naming files."""
879
transform, root = self.get_transform()
880
parent = transform.trans_id_file_id('parent-id')
882
transform.create_directory(parent)
884
self.fail("Can't handle contents with no name")
887
def test_noname_contents_nested(self):
888
"""TreeTransform should permit deferring naming files."""
889
transform, root = self.get_transform()
890
parent = transform.trans_id_file_id('parent-id')
892
transform.create_directory(parent)
894
self.fail("Can't handle contents with no name")
895
child = transform.new_directory('child', parent)
896
transform.adjust_path('parent', root, parent)
898
self.failUnlessExists(self.wt.abspath('parent/child'))
899
self.assertEqual(1, transform.rename_count)
901
def test_reuse_name(self):
902
"""Avoid reusing the same limbo name for different files"""
903
transform, root = self.get_transform()
904
parent = transform.new_directory('parent', root)
905
child1 = transform.new_directory('child', parent)
907
child2 = transform.new_directory('child', parent)
909
self.fail('Tranform tried to use the same limbo name twice')
910
transform.adjust_path('child2', parent, child2)
912
# limbo/new-1 => parent, limbo/new-3 => parent/child2
913
# child2 is put into top-level limbo because child1 has already
914
# claimed the direct limbo path when child2 is created. There is no
915
# advantage in renaming files once they're in top-level limbo, except
917
self.assertEqual(2, transform.rename_count)
919
def test_reuse_when_first_moved(self):
920
"""Don't avoid direct paths when it is safe to use them"""
921
transform, root = self.get_transform()
922
parent = transform.new_directory('parent', root)
923
child1 = transform.new_directory('child', parent)
924
transform.adjust_path('child1', parent, child1)
925
child2 = transform.new_directory('child', parent)
927
# limbo/new-1 => parent
928
self.assertEqual(1, transform.rename_count)
930
def test_reuse_after_cancel(self):
931
"""Don't avoid direct paths when it is safe to use them"""
932
transform, root = self.get_transform()
933
parent2 = transform.new_directory('parent2', root)
934
child1 = transform.new_directory('child1', parent2)
935
transform.cancel_creation(parent2)
936
transform.create_directory(parent2)
937
child2 = transform.new_directory('child1', parent2)
938
transform.adjust_path('child2', parent2, child1)
940
# limbo/new-1 => parent2, limbo/new-2 => parent2/child1
941
self.assertEqual(2, transform.rename_count)
943
def test_finalize_order(self):
944
"""Finalize must be done in child-to-parent order"""
945
transform, root = self.get_transform()
946
parent = transform.new_directory('parent', root)
947
child = transform.new_directory('child', parent)
951
self.fail('Tried to remove parent before child1')
953
def test_cancel_with_cancelled_child_should_succeed(self):
954
transform, root = self.get_transform()
955
parent = transform.new_directory('parent', root)
956
child = transform.new_directory('child', parent)
957
transform.cancel_creation(child)
958
transform.cancel_creation(parent)
961
def test_change_entry(self):
962
txt = 'bzrlib.transform.change_entry was deprecated in version 0.90.'
963
self.callDeprecated([txt], change_entry, None, None, None, None, None,
494
967
class TransformGroup(object):
495
def __init__(self, dirname):
968
def __init__(self, dirname, root_id):
496
969
self.name = dirname
497
970
os.mkdir(dirname)
498
971
self.wt = BzrDir.create_standalone_workingtree(dirname)
972
self.wt.set_root_id(root_id)
499
973
self.b = self.wt.branch
500
974
self.tt = TreeTransform(self.wt)
501
975
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
503
978
def conflict_text(tree, merge):
504
979
template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
505
980
return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
698
1177
a.add(['foo', 'foo/bar', 'foo/baz'])
699
1178
a.commit('initial commit')
700
1179
b = BzrDir.create_standalone_workingtree('b')
701
build_tree(a.basis_tree(), b)
1180
basis = a.basis_tree()
1182
self.addCleanup(basis.unlock)
1183
build_tree(basis, b)
702
1184
self.assertIs(os.path.isdir('b/foo'), True)
703
1185
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
704
1186
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1188
def test_build_with_references(self):
1189
tree = self.make_branch_and_tree('source',
1190
format='dirstate-with-subtree')
1191
subtree = self.make_branch_and_tree('source/subtree',
1192
format='dirstate-with-subtree')
1193
tree.add_reference(subtree)
1194
tree.commit('a revision')
1195
tree.branch.create_checkout('target')
1196
self.failUnlessExists('target')
1197
self.failUnlessExists('target/subtree')
1199
def test_file_conflict_handling(self):
1200
"""Ensure that when building trees, conflict handling is done"""
1201
source = self.make_branch_and_tree('source')
1202
target = self.make_branch_and_tree('target')
1203
self.build_tree(['source/file', 'target/file'])
1204
source.add('file', 'new-file')
1205
source.commit('added file')
1206
build_tree(source.basis_tree(), target)
1207
self.assertEqual([DuplicateEntry('Moved existing file to',
1208
'file.moved', 'file', None, 'new-file')],
1210
target2 = self.make_branch_and_tree('target2')
1211
target_file = file('target2/file', 'wb')
1213
source_file = file('source/file', 'rb')
1215
target_file.write(source_file.read())
1220
build_tree(source.basis_tree(), target2)
1221
self.assertEqual([], target2.conflicts())
1223
def test_symlink_conflict_handling(self):
1224
"""Ensure that when building trees, conflict handling is done"""
1225
self.requireFeature(SymlinkFeature)
1226
source = self.make_branch_and_tree('source')
1227
os.symlink('foo', 'source/symlink')
1228
source.add('symlink', 'new-symlink')
1229
source.commit('added file')
1230
target = self.make_branch_and_tree('target')
1231
os.symlink('bar', 'target/symlink')
1232
build_tree(source.basis_tree(), target)
1233
self.assertEqual([DuplicateEntry('Moved existing file to',
1234
'symlink.moved', 'symlink', None, 'new-symlink')],
1236
target = self.make_branch_and_tree('target2')
1237
os.symlink('foo', 'target2/symlink')
1238
build_tree(source.basis_tree(), target)
1239
self.assertEqual([], target.conflicts())
1241
def test_directory_conflict_handling(self):
1242
"""Ensure that when building trees, conflict handling is done"""
1243
source = self.make_branch_and_tree('source')
1244
target = self.make_branch_and_tree('target')
1245
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1246
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1247
source.commit('added file')
1248
build_tree(source.basis_tree(), target)
1249
self.assertEqual([], target.conflicts())
1250
self.failUnlessExists('target/dir1/file')
1252
# Ensure contents are merged
1253
target = self.make_branch_and_tree('target2')
1254
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1255
build_tree(source.basis_tree(), target)
1256
self.assertEqual([], target.conflicts())
1257
self.failUnlessExists('target2/dir1/file2')
1258
self.failUnlessExists('target2/dir1/file')
1260
# Ensure new contents are suppressed for existing branches
1261
target = self.make_branch_and_tree('target3')
1262
self.make_branch('target3/dir1')
1263
self.build_tree(['target3/dir1/file2'])
1264
build_tree(source.basis_tree(), target)
1265
self.failIfExists('target3/dir1/file')
1266
self.failUnlessExists('target3/dir1/file2')
1267
self.failUnlessExists('target3/dir1.diverted/file')
1268
self.assertEqual([DuplicateEntry('Diverted to',
1269
'dir1.diverted', 'dir1', 'new-dir1', None)],
1272
target = self.make_branch_and_tree('target4')
1273
self.build_tree(['target4/dir1/'])
1274
self.make_branch('target4/dir1/file')
1275
build_tree(source.basis_tree(), target)
1276
self.failUnlessExists('target4/dir1/file')
1277
self.assertEqual('directory', file_kind('target4/dir1/file'))
1278
self.failUnlessExists('target4/dir1/file.diverted')
1279
self.assertEqual([DuplicateEntry('Diverted to',
1280
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1283
def test_mixed_conflict_handling(self):
1284
"""Ensure that when building trees, conflict handling is done"""
1285
source = self.make_branch_and_tree('source')
1286
target = self.make_branch_and_tree('target')
1287
self.build_tree(['source/name', 'target/name/'])
1288
source.add('name', 'new-name')
1289
source.commit('added file')
1290
build_tree(source.basis_tree(), target)
1291
self.assertEqual([DuplicateEntry('Moved existing file to',
1292
'name.moved', 'name', None, 'new-name')], target.conflicts())
1294
def test_raises_in_populated(self):
1295
source = self.make_branch_and_tree('source')
1296
self.build_tree(['source/name'])
1298
source.commit('added name')
1299
target = self.make_branch_and_tree('target')
1300
self.build_tree(['target/name'])
1302
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1303
build_tree, source.basis_tree(), target)
1305
def test_build_tree_rename_count(self):
1306
source = self.make_branch_and_tree('source')
1307
self.build_tree(['source/file1', 'source/dir1/'])
1308
source.add(['file1', 'dir1'])
1309
source.commit('add1')
1310
target1 = self.make_branch_and_tree('target1')
1311
transform_result = build_tree(source.basis_tree(), target1)
1312
self.assertEqual(2, transform_result.rename_count)
1314
self.build_tree(['source/dir1/file2'])
1315
source.add(['dir1/file2'])
1316
source.commit('add3')
1317
target2 = self.make_branch_and_tree('target2')
1318
transform_result = build_tree(source.basis_tree(), target2)
1319
# children of non-root directories should not be renamed
1320
self.assertEqual(2, transform_result.rename_count)
706
1323
class MockTransform(object):
708
1325
def has_named_child(self, by_parent, parent_id, name):