485
539
create.new_file('vfile', root, 'myfile-text', 'myfile-id')
486
540
create.new_file('uvfile', root, 'othertext')
488
self.assertEqual(find_interesting(wt, wt, ['vfile']),
490
self.assertRaises(NotVersionedError, find_interesting, wt, wt,
542
result = self.applyDeprecated(symbol_versioning.zero_fifteen,
543
find_interesting, wt, wt, ['vfile'])
544
self.assertEqual(result, set(['myfile-id']))
546
def test_set_executability_order(self):
547
"""Ensure that executability behaves the same, no matter what order.
549
- create file and set executability simultaneously
550
- create file and set executability afterward
551
- unsetting the executability of a file whose executability has not been
552
declared should throw an exception (this may happen when a
553
merge attempts to create a file with a duplicate ID)
555
transform, root = self.get_transform()
557
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
559
sac = transform.new_file('set_after_creation', root,
560
'Set after creation', 'sac')
561
transform.set_executability(True, sac)
562
uws = transform.new_file('unset_without_set', root, 'Unset badly',
564
self.assertRaises(KeyError, transform.set_executability, None, uws)
566
self.assertTrue(wt.is_executable('soc'))
567
self.assertTrue(wt.is_executable('sac'))
569
def test_preserve_mode(self):
570
"""File mode is preserved when replacing content"""
571
if sys.platform == 'win32':
572
raise TestSkipped('chmod has no effect on win32')
573
transform, root = self.get_transform()
574
transform.new_file('file1', root, 'contents', 'file1-id', True)
576
self.assertTrue(self.wt.is_executable('file1-id'))
577
transform, root = self.get_transform()
578
file1_id = transform.trans_id_tree_file_id('file1-id')
579
transform.delete_contents(file1_id)
580
transform.create_file('contents2', file1_id)
582
self.assertTrue(self.wt.is_executable('file1-id'))
584
def test__set_mode_stats_correctly(self):
585
"""_set_mode stats to determine file mode."""
586
if sys.platform == 'win32':
587
raise TestSkipped('chmod has no effect on win32')
591
def instrumented_stat(path):
592
stat_paths.append(path)
593
return real_stat(path)
595
transform, root = self.get_transform()
597
bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
598
file_id='bar-id-1', executable=False)
601
transform, root = self.get_transform()
602
bar1_id = transform.trans_id_tree_path('bar')
603
bar2_id = transform.trans_id_tree_path('bar2')
605
os.stat = instrumented_stat
606
transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
611
bar1_abspath = self.wt.abspath('bar')
612
self.assertEqual([bar1_abspath], stat_paths)
614
def test_iter_changes(self):
615
self.wt.set_root_id('eert_toor')
616
transform, root = self.get_transform()
617
transform.new_file('old', root, 'blah', 'id-1', True)
619
transform, root = self.get_transform()
621
self.assertEqual([], list(transform._iter_changes()))
622
old = transform.trans_id_tree_file_id('id-1')
623
transform.unversion_file(old)
624
self.assertEqual([('id-1', ('old', None), False, (True, False),
625
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
626
(True, True))], list(transform._iter_changes()))
627
transform.new_directory('new', root, 'id-1')
628
self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
629
('eert_toor', 'eert_toor'), ('old', 'new'),
630
('file', 'directory'),
631
(True, False))], list(transform._iter_changes()))
635
def test_iter_changes_new(self):
636
self.wt.set_root_id('eert_toor')
637
transform, root = self.get_transform()
638
transform.new_file('old', root, 'blah')
640
transform, root = self.get_transform()
642
old = transform.trans_id_tree_path('old')
643
transform.version_file('id-1', old)
644
self.assertEqual([('id-1', (None, 'old'), False, (False, True),
645
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
646
(False, False))], list(transform._iter_changes()))
650
def test_iter_changes_modifications(self):
651
self.wt.set_root_id('eert_toor')
652
transform, root = self.get_transform()
653
transform.new_file('old', root, 'blah', 'id-1')
654
transform.new_file('new', root, 'blah')
655
transform.new_directory('subdir', root, 'subdir-id')
657
transform, root = self.get_transform()
659
old = transform.trans_id_tree_path('old')
660
subdir = transform.trans_id_tree_file_id('subdir-id')
661
new = transform.trans_id_tree_path('new')
662
self.assertEqual([], list(transform._iter_changes()))
665
transform.delete_contents(old)
666
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
667
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
668
(False, False))], list(transform._iter_changes()))
671
transform.create_file('blah', old)
672
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
673
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
674
(False, False))], list(transform._iter_changes()))
675
transform.cancel_deletion(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_creation(old)
681
# move file_id to a different file
682
self.assertEqual([], list(transform._iter_changes()))
683
transform.unversion_file(old)
684
transform.version_file('id-1', new)
685
transform.adjust_path('old', root, new)
686
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
687
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
688
(False, False))], list(transform._iter_changes()))
689
transform.cancel_versioning(new)
690
transform._removed_id = set()
693
self.assertEqual([], list(transform._iter_changes()))
694
transform.set_executability(True, old)
695
self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
696
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
697
(False, True))], list(transform._iter_changes()))
698
transform.set_executability(None, old)
701
self.assertEqual([], list(transform._iter_changes()))
702
transform.adjust_path('new', root, old)
703
transform._new_parent = {}
704
self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
705
('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
706
(False, False))], list(transform._iter_changes()))
707
transform._new_name = {}
710
self.assertEqual([], list(transform._iter_changes()))
711
transform.adjust_path('new', subdir, old)
712
transform._new_name = {}
713
self.assertEqual([('id-1', ('old', 'subdir/old'), False,
714
(True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
715
('file', 'file'), (False, False))],
716
list(transform._iter_changes()))
717
transform._new_path = {}
722
def test_iter_changes_modified_bleed(self):
723
self.wt.set_root_id('eert_toor')
724
"""Modified flag should not bleed from one change to another"""
725
# unfortunately, we have no guarantee that file1 (which is modified)
726
# will be applied before file2. And if it's applied after file2, it
727
# obviously can't bleed into file2's change output. But for now, it
729
transform, root = self.get_transform()
730
transform.new_file('file1', root, 'blah', 'id-1')
731
transform.new_file('file2', root, 'blah', 'id-2')
733
transform, root = self.get_transform()
735
transform.delete_contents(transform.trans_id_file_id('id-1'))
736
transform.set_executability(True,
737
transform.trans_id_file_id('id-2'))
738
self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
739
('eert_toor', 'eert_toor'), ('file1', u'file1'),
740
('file', None), (False, False)),
741
('id-2', (u'file2', u'file2'), False, (True, True),
742
('eert_toor', 'eert_toor'), ('file2', u'file2'),
743
('file', 'file'), (False, True))],
744
list(transform._iter_changes()))
748
def test_iter_changes_move_missing(self):
749
"""Test moving ids with no files around"""
750
self.wt.set_root_id('toor_eert')
751
# Need two steps because versioning a non-existant file is a conflict.
752
transform, root = self.get_transform()
753
transform.new_directory('floater', root, 'floater-id')
755
transform, root = self.get_transform()
756
transform.delete_contents(transform.trans_id_tree_path('floater'))
758
transform, root = self.get_transform()
759
floater = transform.trans_id_tree_path('floater')
761
transform.adjust_path('flitter', root, floater)
762
self.assertEqual([('floater-id', ('floater', 'flitter'), False,
763
(True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
764
(None, None), (False, False))], list(transform._iter_changes()))
768
def test_iter_changes_pointless(self):
769
"""Ensure that no-ops are not treated as modifications"""
770
self.wt.set_root_id('eert_toor')
771
transform, root = self.get_transform()
772
transform.new_file('old', root, 'blah', 'id-1')
773
transform.new_directory('subdir', root, 'subdir-id')
775
transform, root = self.get_transform()
777
old = transform.trans_id_tree_path('old')
778
subdir = transform.trans_id_tree_file_id('subdir-id')
779
self.assertEqual([], list(transform._iter_changes()))
780
transform.delete_contents(subdir)
781
transform.create_directory(subdir)
782
transform.set_executability(False, old)
783
transform.unversion_file(old)
784
transform.version_file('id-1', old)
785
transform.adjust_path('old', root, old)
786
self.assertEqual([], list(transform._iter_changes()))
790
def test_rename_count(self):
791
transform, root = self.get_transform()
792
transform.new_file('name1', root, 'contents')
793
self.assertEqual(transform.rename_count, 0)
795
self.assertEqual(transform.rename_count, 1)
796
transform2, root = self.get_transform()
797
transform2.adjust_path('name2', root,
798
transform2.trans_id_tree_path('name1'))
799
self.assertEqual(transform2.rename_count, 0)
801
self.assertEqual(transform2.rename_count, 2)
803
def test_change_parent(self):
804
"""Ensure that after we change a parent, the results are still right.
806
Renames and parent changes on pending transforms can happen as part
807
of conflict resolution, and are explicitly permitted by the
810
This test ensures they work correctly with the rename-avoidance
813
transform, root = self.get_transform()
814
parent1 = transform.new_directory('parent1', root)
815
child1 = transform.new_file('child1', parent1, 'contents')
816
parent2 = transform.new_directory('parent2', root)
817
transform.adjust_path('child1', parent2, child1)
819
self.failIfExists(self.wt.abspath('parent1/child1'))
820
self.failUnlessExists(self.wt.abspath('parent2/child1'))
821
# rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
822
# no rename for child1 (counting only renames during apply)
823
self.failUnlessEqual(2, transform.rename_count)
825
def test_cancel_parent(self):
826
"""Cancelling a parent doesn't cause deletion of a non-empty directory
828
This is like the test_change_parent, except that we cancel the parent
829
before adjusting the path. The transform must detect that the
830
directory is non-empty, and move children to safe locations.
832
transform, root = self.get_transform()
833
parent1 = transform.new_directory('parent1', root)
834
child1 = transform.new_file('child1', parent1, 'contents')
835
child2 = transform.new_file('child2', parent1, 'contents')
837
transform.cancel_creation(parent1)
839
self.fail('Failed to move child1 before deleting parent1')
840
transform.cancel_creation(child2)
841
transform.create_directory(parent1)
843
transform.cancel_creation(parent1)
844
# If the transform incorrectly believes that child2 is still in
845
# parent1's limbo directory, it will try to rename it and fail
846
# because was already moved by the first cancel_creation.
848
self.fail('Transform still thinks child2 is a child of parent1')
849
parent2 = transform.new_directory('parent2', root)
850
transform.adjust_path('child1', parent2, child1)
852
self.failIfExists(self.wt.abspath('parent1'))
853
self.failUnlessExists(self.wt.abspath('parent2/child1'))
854
# rename limbo/new-3 => parent2, rename limbo/new-2 => child1
855
self.failUnlessEqual(2, transform.rename_count)
857
def test_adjust_and_cancel(self):
858
"""Make sure adjust_path keeps track of limbo children properly"""
859
transform, root = self.get_transform()
860
parent1 = transform.new_directory('parent1', root)
861
child1 = transform.new_file('child1', parent1, 'contents')
862
parent2 = transform.new_directory('parent2', root)
863
transform.adjust_path('child1', parent2, child1)
864
transform.cancel_creation(child1)
866
transform.cancel_creation(parent1)
867
# if the transform thinks child1 is still in parent1's limbo
868
# directory, it will attempt to move it and fail.
870
self.fail('Transform still thinks child1 is a child of parent1')
873
def test_noname_contents(self):
874
"""TreeTransform should permit deferring naming files."""
875
transform, root = self.get_transform()
876
parent = transform.trans_id_file_id('parent-id')
878
transform.create_directory(parent)
880
self.fail("Can't handle contents with no name")
883
def test_noname_contents_nested(self):
884
"""TreeTransform should permit deferring naming files."""
885
transform, root = self.get_transform()
886
parent = transform.trans_id_file_id('parent-id')
888
transform.create_directory(parent)
890
self.fail("Can't handle contents with no name")
891
child = transform.new_directory('child', parent)
892
transform.adjust_path('parent', root, parent)
894
self.failUnlessExists(self.wt.abspath('parent/child'))
895
self.assertEqual(1, transform.rename_count)
897
def test_reuse_name(self):
898
"""Avoid reusing the same limbo name for different files"""
899
transform, root = self.get_transform()
900
parent = transform.new_directory('parent', root)
901
child1 = transform.new_directory('child', parent)
903
child2 = transform.new_directory('child', parent)
905
self.fail('Tranform tried to use the same limbo name twice')
906
transform.adjust_path('child2', parent, child2)
908
# limbo/new-1 => parent, limbo/new-3 => parent/child2
909
# child2 is put into top-level limbo because child1 has already
910
# claimed the direct limbo path when child2 is created. There is no
911
# advantage in renaming files once they're in top-level limbo, except
913
self.assertEqual(2, transform.rename_count)
915
def test_reuse_when_first_moved(self):
916
"""Don't avoid direct paths when it is safe to use them"""
917
transform, root = self.get_transform()
918
parent = transform.new_directory('parent', root)
919
child1 = transform.new_directory('child', parent)
920
transform.adjust_path('child1', parent, child1)
921
child2 = transform.new_directory('child', parent)
923
# limbo/new-1 => parent
924
self.assertEqual(1, transform.rename_count)
926
def test_reuse_after_cancel(self):
927
"""Don't avoid direct paths when it is safe to use them"""
928
transform, root = self.get_transform()
929
parent2 = transform.new_directory('parent2', root)
930
child1 = transform.new_directory('child1', parent2)
931
transform.cancel_creation(parent2)
932
transform.create_directory(parent2)
933
child2 = transform.new_directory('child1', parent2)
934
transform.adjust_path('child2', parent2, child1)
936
# limbo/new-1 => parent2, limbo/new-2 => parent2/child1
937
self.assertEqual(2, transform.rename_count)
939
def test_finalize_order(self):
940
"""Finalize must be done in child-to-parent order"""
941
transform, root = self.get_transform()
942
parent = transform.new_directory('parent', root)
943
child = transform.new_directory('child', parent)
947
self.fail('Tried to remove parent before child1')
949
def test_cancel_with_cancelled_child_should_succeed(self):
950
transform, root = self.get_transform()
951
parent = transform.new_directory('parent', root)
952
child = transform.new_directory('child', parent)
953
transform.cancel_creation(child)
954
transform.cancel_creation(parent)
957
def test_change_entry(self):
958
txt = 'bzrlib.transform.change_entry was deprecated in version 0.90.'
959
self.callDeprecated([txt], change_entry, None, None, None, None, None,
494
963
class TransformGroup(object):
495
def __init__(self, dirname):
964
def __init__(self, dirname, root_id):
496
965
self.name = dirname
497
966
os.mkdir(dirname)
498
967
self.wt = BzrDir.create_standalone_workingtree(dirname)
968
self.wt.set_root_id(root_id)
499
969
self.b = self.wt.branch
500
970
self.tt = TreeTransform(self.wt)
501
971
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
503
974
def conflict_text(tree, merge):
504
975
template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
505
976
return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
698
1175
a.add(['foo', 'foo/bar', 'foo/baz'])
699
1176
a.commit('initial commit')
700
1177
b = BzrDir.create_standalone_workingtree('b')
701
build_tree(a.basis_tree(), b)
1178
basis = a.basis_tree()
1180
self.addCleanup(basis.unlock)
1181
build_tree(basis, b)
702
1182
self.assertIs(os.path.isdir('b/foo'), True)
703
1183
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
704
1184
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1186
def test_build_with_references(self):
1187
tree = self.make_branch_and_tree('source',
1188
format='dirstate-with-subtree')
1189
subtree = self.make_branch_and_tree('source/subtree',
1190
format='dirstate-with-subtree')
1191
tree.add_reference(subtree)
1192
tree.commit('a revision')
1193
tree.branch.create_checkout('target')
1194
self.failUnlessExists('target')
1195
self.failUnlessExists('target/subtree')
1197
def test_file_conflict_handling(self):
1198
"""Ensure that when building trees, conflict handling is done"""
1199
source = self.make_branch_and_tree('source')
1200
target = self.make_branch_and_tree('target')
1201
self.build_tree(['source/file', 'target/file'])
1202
source.add('file', 'new-file')
1203
source.commit('added file')
1204
build_tree(source.basis_tree(), target)
1205
self.assertEqual([DuplicateEntry('Moved existing file to',
1206
'file.moved', 'file', None, 'new-file')],
1208
target2 = self.make_branch_and_tree('target2')
1209
target_file = file('target2/file', 'wb')
1211
source_file = file('source/file', 'rb')
1213
target_file.write(source_file.read())
1218
build_tree(source.basis_tree(), target2)
1219
self.assertEqual([], target2.conflicts())
1221
def test_symlink_conflict_handling(self):
1222
"""Ensure that when building trees, conflict handling is done"""
1223
if not has_symlinks():
1224
raise TestSkipped('Test requires symlink support')
1225
source = self.make_branch_and_tree('source')
1226
os.symlink('foo', 'source/symlink')
1227
source.add('symlink', 'new-symlink')
1228
source.commit('added file')
1229
target = self.make_branch_and_tree('target')
1230
os.symlink('bar', 'target/symlink')
1231
build_tree(source.basis_tree(), target)
1232
self.assertEqual([DuplicateEntry('Moved existing file to',
1233
'symlink.moved', 'symlink', None, 'new-symlink')],
1235
target = self.make_branch_and_tree('target2')
1236
os.symlink('foo', 'target2/symlink')
1237
build_tree(source.basis_tree(), target)
1238
self.assertEqual([], target.conflicts())
1240
def test_directory_conflict_handling(self):
1241
"""Ensure that when building trees, conflict handling is done"""
1242
source = self.make_branch_and_tree('source')
1243
target = self.make_branch_and_tree('target')
1244
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1245
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1246
source.commit('added file')
1247
build_tree(source.basis_tree(), target)
1248
self.assertEqual([], target.conflicts())
1249
self.failUnlessExists('target/dir1/file')
1251
# Ensure contents are merged
1252
target = self.make_branch_and_tree('target2')
1253
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1254
build_tree(source.basis_tree(), target)
1255
self.assertEqual([], target.conflicts())
1256
self.failUnlessExists('target2/dir1/file2')
1257
self.failUnlessExists('target2/dir1/file')
1259
# Ensure new contents are suppressed for existing branches
1260
target = self.make_branch_and_tree('target3')
1261
self.make_branch('target3/dir1')
1262
self.build_tree(['target3/dir1/file2'])
1263
build_tree(source.basis_tree(), target)
1264
self.failIfExists('target3/dir1/file')
1265
self.failUnlessExists('target3/dir1/file2')
1266
self.failUnlessExists('target3/dir1.diverted/file')
1267
self.assertEqual([DuplicateEntry('Diverted to',
1268
'dir1.diverted', 'dir1', 'new-dir1', None)],
1271
target = self.make_branch_and_tree('target4')
1272
self.build_tree(['target4/dir1/'])
1273
self.make_branch('target4/dir1/file')
1274
build_tree(source.basis_tree(), target)
1275
self.failUnlessExists('target4/dir1/file')
1276
self.assertEqual('directory', file_kind('target4/dir1/file'))
1277
self.failUnlessExists('target4/dir1/file.diverted')
1278
self.assertEqual([DuplicateEntry('Diverted to',
1279
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1282
def test_mixed_conflict_handling(self):
1283
"""Ensure that when building trees, conflict handling is done"""
1284
source = self.make_branch_and_tree('source')
1285
target = self.make_branch_and_tree('target')
1286
self.build_tree(['source/name', 'target/name/'])
1287
source.add('name', 'new-name')
1288
source.commit('added file')
1289
build_tree(source.basis_tree(), target)
1290
self.assertEqual([DuplicateEntry('Moved existing file to',
1291
'name.moved', 'name', None, 'new-name')], target.conflicts())
1293
def test_raises_in_populated(self):
1294
source = self.make_branch_and_tree('source')
1295
self.build_tree(['source/name'])
1297
source.commit('added name')
1298
target = self.make_branch_and_tree('target')
1299
self.build_tree(['target/name'])
1301
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1302
build_tree, source.basis_tree(), target)
1304
def test_build_tree_rename_count(self):
1305
source = self.make_branch_and_tree('source')
1306
self.build_tree(['source/file1', 'source/dir1/'])
1307
source.add(['file1', 'dir1'])
1308
source.commit('add1')
1309
target1 = self.make_branch_and_tree('target1')
1310
transform_result = build_tree(source.basis_tree(), target1)
1311
self.assertEqual(2, transform_result.rename_count)
1313
self.build_tree(['source/dir1/file2'])
1314
source.add(['dir1/file2'])
1315
source.commit('add3')
1316
target2 = self.make_branch_and_tree('target2')
1317
transform_result = build_tree(source.basis_tree(), target2)
1318
# children of non-root directories should not be renamed
1319
self.assertEqual(2, transform_result.rename_count)
706
1322
class MockTransform(object):
708
1324
def has_named_child(self, by_parent, parent_id, name):