747
481
rename.set_executability(True, myfile)
750
def test_set_executability_order(self):
751
"""Ensure that executability behaves the same, no matter what order.
753
- create file and set executability simultaneously
754
- create file and set executability afterward
755
- unsetting the executability of a file whose executability has not been
756
declared should throw an exception (this may happen when a
757
merge attempts to create a file with a duplicate ID)
759
transform, root = self.get_transform()
762
self.addCleanup(wt.unlock)
763
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
765
sac = transform.new_file('set_after_creation', root,
766
'Set after creation', 'sac')
767
transform.set_executability(True, sac)
768
uws = transform.new_file('unset_without_set', root, 'Unset badly',
770
self.assertRaises(KeyError, transform.set_executability, None, uws)
772
self.assertTrue(wt.is_executable('soc'))
773
self.assertTrue(wt.is_executable('sac'))
775
def test_preserve_mode(self):
776
"""File mode is preserved when replacing content"""
777
if sys.platform == 'win32':
778
raise TestSkipped('chmod has no effect on win32')
779
transform, root = self.get_transform()
780
transform.new_file('file1', root, 'contents', 'file1-id', True)
783
self.addCleanup(self.wt.unlock)
784
self.assertTrue(self.wt.is_executable('file1-id'))
785
transform, root = self.get_transform()
786
file1_id = transform.trans_id_tree_file_id('file1-id')
787
transform.delete_contents(file1_id)
788
transform.create_file('contents2', file1_id)
790
self.assertTrue(self.wt.is_executable('file1-id'))
792
def test__set_mode_stats_correctly(self):
793
"""_set_mode stats to determine file mode."""
794
if sys.platform == 'win32':
795
raise TestSkipped('chmod has no effect on win32')
799
def instrumented_stat(path):
800
stat_paths.append(path)
801
return real_stat(path)
803
transform, root = self.get_transform()
805
bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
806
file_id='bar-id-1', executable=False)
809
transform, root = self.get_transform()
810
bar1_id = transform.trans_id_tree_path('bar')
811
bar2_id = transform.trans_id_tree_path('bar2')
813
os.stat = instrumented_stat
814
transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
819
bar1_abspath = self.wt.abspath('bar')
820
self.assertEqual([bar1_abspath], stat_paths)
822
def test_iter_changes(self):
823
self.wt.set_root_id('eert_toor')
824
transform, root = self.get_transform()
825
transform.new_file('old', root, 'blah', 'id-1', True)
827
transform, root = self.get_transform()
829
self.assertEqual([], list(transform.iter_changes()))
830
old = transform.trans_id_tree_file_id('id-1')
831
transform.unversion_file(old)
832
self.assertEqual([('id-1', ('old', None), False, (True, False),
833
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
834
(True, True))], list(transform.iter_changes()))
835
transform.new_directory('new', root, 'id-1')
836
self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
837
('eert_toor', 'eert_toor'), ('old', 'new'),
838
('file', 'directory'),
839
(True, False))], list(transform.iter_changes()))
843
def test_iter_changes_new(self):
844
self.wt.set_root_id('eert_toor')
845
transform, root = self.get_transform()
846
transform.new_file('old', root, 'blah')
848
transform, root = self.get_transform()
850
old = transform.trans_id_tree_path('old')
851
transform.version_file('id-1', old)
852
self.assertEqual([('id-1', (None, 'old'), False, (False, True),
853
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
854
(False, False))], list(transform.iter_changes()))
858
def test_iter_changes_modifications(self):
859
self.wt.set_root_id('eert_toor')
860
transform, root = self.get_transform()
861
transform.new_file('old', root, 'blah', 'id-1')
862
transform.new_file('new', root, 'blah')
863
transform.new_directory('subdir', root, 'subdir-id')
865
transform, root = self.get_transform()
867
old = transform.trans_id_tree_path('old')
868
subdir = transform.trans_id_tree_file_id('subdir-id')
869
new = transform.trans_id_tree_path('new')
870
self.assertEqual([], list(transform.iter_changes()))
873
transform.delete_contents(old)
874
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
875
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
876
(False, False))], list(transform.iter_changes()))
879
transform.create_file('blah', old)
880
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
881
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
882
(False, False))], list(transform.iter_changes()))
883
transform.cancel_deletion(old)
884
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
885
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
886
(False, False))], list(transform.iter_changes()))
887
transform.cancel_creation(old)
889
# move file_id to a different file
890
self.assertEqual([], list(transform.iter_changes()))
891
transform.unversion_file(old)
892
transform.version_file('id-1', new)
893
transform.adjust_path('old', root, new)
894
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
895
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
896
(False, False))], list(transform.iter_changes()))
897
transform.cancel_versioning(new)
898
transform._removed_id = set()
901
self.assertEqual([], list(transform.iter_changes()))
902
transform.set_executability(True, old)
903
self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
904
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
905
(False, True))], list(transform.iter_changes()))
906
transform.set_executability(None, old)
909
self.assertEqual([], list(transform.iter_changes()))
910
transform.adjust_path('new', root, old)
911
transform._new_parent = {}
912
self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
913
('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
914
(False, False))], list(transform.iter_changes()))
915
transform._new_name = {}
918
self.assertEqual([], list(transform.iter_changes()))
919
transform.adjust_path('new', subdir, old)
920
transform._new_name = {}
921
self.assertEqual([('id-1', ('old', 'subdir/old'), False,
922
(True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
923
('file', 'file'), (False, False))],
924
list(transform.iter_changes()))
925
transform._new_path = {}
930
def test_iter_changes_modified_bleed(self):
931
self.wt.set_root_id('eert_toor')
932
"""Modified flag should not bleed from one change to another"""
933
# unfortunately, we have no guarantee that file1 (which is modified)
934
# will be applied before file2. And if it's applied after file2, it
935
# obviously can't bleed into file2's change output. But for now, it
937
transform, root = self.get_transform()
938
transform.new_file('file1', root, 'blah', 'id-1')
939
transform.new_file('file2', root, 'blah', 'id-2')
941
transform, root = self.get_transform()
943
transform.delete_contents(transform.trans_id_file_id('id-1'))
944
transform.set_executability(True,
945
transform.trans_id_file_id('id-2'))
946
self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
947
('eert_toor', 'eert_toor'), ('file1', u'file1'),
948
('file', None), (False, False)),
949
('id-2', (u'file2', u'file2'), False, (True, True),
950
('eert_toor', 'eert_toor'), ('file2', u'file2'),
951
('file', 'file'), (False, True))],
952
list(transform.iter_changes()))
956
def test_iter_changes_move_missing(self):
957
"""Test moving ids with no files around"""
958
self.wt.set_root_id('toor_eert')
959
# Need two steps because versioning a non-existant file is a conflict.
960
transform, root = self.get_transform()
961
transform.new_directory('floater', root, 'floater-id')
963
transform, root = self.get_transform()
964
transform.delete_contents(transform.trans_id_tree_path('floater'))
966
transform, root = self.get_transform()
967
floater = transform.trans_id_tree_path('floater')
969
transform.adjust_path('flitter', root, floater)
970
self.assertEqual([('floater-id', ('floater', 'flitter'), False,
971
(True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
972
(None, None), (False, False))], list(transform.iter_changes()))
976
def test_iter_changes_pointless(self):
977
"""Ensure that no-ops are not treated as modifications"""
978
self.wt.set_root_id('eert_toor')
979
transform, root = self.get_transform()
980
transform.new_file('old', root, 'blah', 'id-1')
981
transform.new_directory('subdir', root, 'subdir-id')
983
transform, root = self.get_transform()
985
old = transform.trans_id_tree_path('old')
986
subdir = transform.trans_id_tree_file_id('subdir-id')
987
self.assertEqual([], list(transform.iter_changes()))
988
transform.delete_contents(subdir)
989
transform.create_directory(subdir)
990
transform.set_executability(False, old)
991
transform.unversion_file(old)
992
transform.version_file('id-1', old)
993
transform.adjust_path('old', root, old)
994
self.assertEqual([], list(transform.iter_changes()))
998
def test_rename_count(self):
999
transform, root = self.get_transform()
1000
transform.new_file('name1', root, 'contents')
1001
self.assertEqual(transform.rename_count, 0)
1003
self.assertEqual(transform.rename_count, 1)
1004
transform2, root = self.get_transform()
1005
transform2.adjust_path('name2', root,
1006
transform2.trans_id_tree_path('name1'))
1007
self.assertEqual(transform2.rename_count, 0)
1009
self.assertEqual(transform2.rename_count, 2)
1011
def test_change_parent(self):
1012
"""Ensure that after we change a parent, the results are still right.
1014
Renames and parent changes on pending transforms can happen as part
1015
of conflict resolution, and are explicitly permitted by the
1018
This test ensures they work correctly with the rename-avoidance
1021
transform, root = self.get_transform()
1022
parent1 = transform.new_directory('parent1', root)
1023
child1 = transform.new_file('child1', parent1, 'contents')
1024
parent2 = transform.new_directory('parent2', root)
1025
transform.adjust_path('child1', parent2, child1)
1027
self.failIfExists(self.wt.abspath('parent1/child1'))
1028
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1029
# rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1030
# no rename for child1 (counting only renames during apply)
1031
self.failUnlessEqual(2, transform.rename_count)
1033
def test_cancel_parent(self):
1034
"""Cancelling a parent doesn't cause deletion of a non-empty directory
1036
This is like the test_change_parent, except that we cancel the parent
1037
before adjusting the path. The transform must detect that the
1038
directory is non-empty, and move children to safe locations.
1040
transform, root = self.get_transform()
1041
parent1 = transform.new_directory('parent1', root)
1042
child1 = transform.new_file('child1', parent1, 'contents')
1043
child2 = transform.new_file('child2', parent1, 'contents')
1045
transform.cancel_creation(parent1)
1047
self.fail('Failed to move child1 before deleting parent1')
1048
transform.cancel_creation(child2)
1049
transform.create_directory(parent1)
1051
transform.cancel_creation(parent1)
1052
# If the transform incorrectly believes that child2 is still in
1053
# parent1's limbo directory, it will try to rename it and fail
1054
# because was already moved by the first cancel_creation.
1056
self.fail('Transform still thinks child2 is a child of parent1')
1057
parent2 = transform.new_directory('parent2', root)
1058
transform.adjust_path('child1', parent2, child1)
1060
self.failIfExists(self.wt.abspath('parent1'))
1061
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1062
# rename limbo/new-3 => parent2, rename limbo/new-2 => child1
1063
self.failUnlessEqual(2, transform.rename_count)
1065
def test_adjust_and_cancel(self):
1066
"""Make sure adjust_path keeps track of limbo children properly"""
1067
transform, root = self.get_transform()
1068
parent1 = transform.new_directory('parent1', root)
1069
child1 = transform.new_file('child1', parent1, 'contents')
1070
parent2 = transform.new_directory('parent2', root)
1071
transform.adjust_path('child1', parent2, child1)
1072
transform.cancel_creation(child1)
1074
transform.cancel_creation(parent1)
1075
# if the transform thinks child1 is still in parent1's limbo
1076
# directory, it will attempt to move it and fail.
1078
self.fail('Transform still thinks child1 is a child of parent1')
1079
transform.finalize()
1081
def test_noname_contents(self):
1082
"""TreeTransform should permit deferring naming files."""
1083
transform, root = self.get_transform()
1084
parent = transform.trans_id_file_id('parent-id')
1086
transform.create_directory(parent)
1088
self.fail("Can't handle contents with no name")
1089
transform.finalize()
1091
def test_noname_contents_nested(self):
1092
"""TreeTransform should permit deferring naming files."""
1093
transform, root = self.get_transform()
1094
parent = transform.trans_id_file_id('parent-id')
1096
transform.create_directory(parent)
1098
self.fail("Can't handle contents with no name")
1099
child = transform.new_directory('child', parent)
1100
transform.adjust_path('parent', root, parent)
1102
self.failUnlessExists(self.wt.abspath('parent/child'))
1103
self.assertEqual(1, transform.rename_count)
1105
def test_reuse_name(self):
1106
"""Avoid reusing the same limbo name for different files"""
1107
transform, root = self.get_transform()
1108
parent = transform.new_directory('parent', root)
1109
child1 = transform.new_directory('child', parent)
1111
child2 = transform.new_directory('child', parent)
1113
self.fail('Tranform tried to use the same limbo name twice')
1114
transform.adjust_path('child2', parent, child2)
1116
# limbo/new-1 => parent, limbo/new-3 => parent/child2
1117
# child2 is put into top-level limbo because child1 has already
1118
# claimed the direct limbo path when child2 is created. There is no
1119
# advantage in renaming files once they're in top-level limbo, except
1121
self.assertEqual(2, transform.rename_count)
1123
def test_reuse_when_first_moved(self):
1124
"""Don't avoid direct paths when it is safe to use them"""
1125
transform, root = self.get_transform()
1126
parent = transform.new_directory('parent', root)
1127
child1 = transform.new_directory('child', parent)
1128
transform.adjust_path('child1', parent, child1)
1129
child2 = transform.new_directory('child', parent)
1131
# limbo/new-1 => parent
1132
self.assertEqual(1, transform.rename_count)
1134
def test_reuse_after_cancel(self):
1135
"""Don't avoid direct paths when it is safe to use them"""
1136
transform, root = self.get_transform()
1137
parent2 = transform.new_directory('parent2', root)
1138
child1 = transform.new_directory('child1', parent2)
1139
transform.cancel_creation(parent2)
1140
transform.create_directory(parent2)
1141
child2 = transform.new_directory('child1', parent2)
1142
transform.adjust_path('child2', parent2, child1)
1144
# limbo/new-1 => parent2, limbo/new-2 => parent2/child1
1145
self.assertEqual(2, transform.rename_count)
1147
def test_finalize_order(self):
1148
"""Finalize must be done in child-to-parent order"""
1149
transform, root = self.get_transform()
1150
parent = transform.new_directory('parent', root)
1151
child = transform.new_directory('child', parent)
1153
transform.finalize()
1155
self.fail('Tried to remove parent before child1')
1157
def test_cancel_with_cancelled_child_should_succeed(self):
1158
transform, root = self.get_transform()
1159
parent = transform.new_directory('parent', root)
1160
child = transform.new_directory('child', parent)
1161
transform.cancel_creation(child)
1162
transform.cancel_creation(parent)
1163
transform.finalize()
1165
def test_rollback_on_directory_clash(self):
1167
wt = self.make_branch_and_tree('.')
1168
tt = TreeTransform(wt) # TreeTransform obtains write lock
1170
foo = tt.new_directory('foo', tt.root)
1171
tt.new_file('bar', foo, 'foobar')
1172
baz = tt.new_directory('baz', tt.root)
1173
tt.new_file('qux', baz, 'quux')
1174
# Ask for a rename 'foo' -> 'baz'
1175
tt.adjust_path('baz', tt.root, foo)
1176
# Lie to tt that we've already resolved all conflicts.
1177
tt.apply(no_conflicts=True)
1181
# The rename will fail because the target directory is not empty (but
1182
# raises FileExists anyway).
1183
err = self.assertRaises(errors.FileExists, tt_helper)
1184
self.assertContainsRe(str(err),
1185
"^File exists: .+/baz")
1187
def test_two_directories_clash(self):
1189
wt = self.make_branch_and_tree('.')
1190
tt = TreeTransform(wt) # TreeTransform obtains write lock
1192
foo_1 = tt.new_directory('foo', tt.root)
1193
tt.new_directory('bar', foo_1)
1194
# Adding the same directory with a different content
1195
foo_2 = tt.new_directory('foo', tt.root)
1196
tt.new_directory('baz', foo_2)
1197
# Lie to tt that we've already resolved all conflicts.
1198
tt.apply(no_conflicts=True)
1202
err = self.assertRaises(errors.FileExists, tt_helper)
1203
self.assertContainsRe(str(err),
1204
"^File exists: .+/foo")
1206
def test_two_directories_clash_finalize(self):
1208
wt = self.make_branch_and_tree('.')
1209
tt = TreeTransform(wt) # TreeTransform obtains write lock
1211
foo_1 = tt.new_directory('foo', tt.root)
1212
tt.new_directory('bar', foo_1)
1213
# Adding the same directory with a different content
1214
foo_2 = tt.new_directory('foo', tt.root)
1215
tt.new_directory('baz', foo_2)
1216
# Lie to tt that we've already resolved all conflicts.
1217
tt.apply(no_conflicts=True)
1221
err = self.assertRaises(errors.FileExists, tt_helper)
1222
self.assertContainsRe(str(err),
1223
"^File exists: .+/foo")
1225
def test_file_to_directory(self):
1226
wt = self.make_branch_and_tree('.')
1227
self.build_tree(['foo'])
1230
tt = TreeTransform(wt)
1231
self.addCleanup(tt.finalize)
1232
foo_trans_id = tt.trans_id_tree_path("foo")
1233
tt.delete_contents(foo_trans_id)
1234
tt.create_directory(foo_trans_id)
1235
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1236
tt.create_file(["aa\n"], bar_trans_id)
1237
tt.version_file("bar-1", bar_trans_id)
1239
self.failUnlessExists("foo/bar")
1242
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1247
changes = wt.changes_from(wt.basis_tree())
1248
self.assertFalse(changes.has_changed(), changes)
1250
def test_file_to_symlink(self):
1251
self.requireFeature(SymlinkFeature)
1252
wt = self.make_branch_and_tree('.')
1253
self.build_tree(['foo'])
1256
tt = TreeTransform(wt)
1257
self.addCleanup(tt.finalize)
1258
foo_trans_id = tt.trans_id_tree_path("foo")
1259
tt.delete_contents(foo_trans_id)
1260
tt.create_symlink("bar", foo_trans_id)
1262
self.failUnlessExists("foo")
1264
self.addCleanup(wt.unlock)
1265
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1268
def test_dir_to_file(self):
1269
wt = self.make_branch_and_tree('.')
1270
self.build_tree(['foo/', 'foo/bar'])
1271
wt.add(['foo', 'foo/bar'])
1273
tt = TreeTransform(wt)
1274
self.addCleanup(tt.finalize)
1275
foo_trans_id = tt.trans_id_tree_path("foo")
1276
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1277
tt.delete_contents(foo_trans_id)
1278
tt.delete_versioned(bar_trans_id)
1279
tt.create_file(["aa\n"], foo_trans_id)
1281
self.failUnlessExists("foo")
1283
self.addCleanup(wt.unlock)
1284
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1287
def test_dir_to_hardlink(self):
1288
self.requireFeature(HardlinkFeature)
1289
wt = self.make_branch_and_tree('.')
1290
self.build_tree(['foo/', 'foo/bar'])
1291
wt.add(['foo', 'foo/bar'])
1293
tt = TreeTransform(wt)
1294
self.addCleanup(tt.finalize)
1295
foo_trans_id = tt.trans_id_tree_path("foo")
1296
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1297
tt.delete_contents(foo_trans_id)
1298
tt.delete_versioned(bar_trans_id)
1299
self.build_tree(['baz'])
1300
tt.create_hardlink("baz", foo_trans_id)
1302
self.failUnlessExists("foo")
1303
self.failUnlessExists("baz")
1305
self.addCleanup(wt.unlock)
1306
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1309
def test_no_final_path(self):
1310
transform, root = self.get_transform()
1311
trans_id = transform.trans_id_file_id('foo')
1312
transform.create_file('bar', trans_id)
1313
transform.cancel_creation(trans_id)
1316
def test_create_from_tree(self):
1317
tree1 = self.make_branch_and_tree('tree1')
1318
self.build_tree_contents([('tree1/foo/',), ('tree1/bar', 'baz')])
1319
tree1.add(['foo', 'bar'], ['foo-id', 'bar-id'])
1320
tree2 = self.make_branch_and_tree('tree2')
1321
tt = TreeTransform(tree2)
1322
foo_trans_id = tt.create_path('foo', tt.root)
1323
create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1324
bar_trans_id = tt.create_path('bar', tt.root)
1325
create_from_tree(tt, bar_trans_id, tree1, 'bar-id')
1327
self.assertEqual('directory', osutils.file_kind('tree2/foo'))
1328
self.assertFileEqual('baz', 'tree2/bar')
1330
def test_create_from_tree_bytes(self):
1331
"""Provided lines are used instead of tree content."""
1332
tree1 = self.make_branch_and_tree('tree1')
1333
self.build_tree_contents([('tree1/foo', 'bar'),])
1334
tree1.add('foo', 'foo-id')
1335
tree2 = self.make_branch_and_tree('tree2')
1336
tt = TreeTransform(tree2)
1337
foo_trans_id = tt.create_path('foo', tt.root)
1338
create_from_tree(tt, foo_trans_id, tree1, 'foo-id', bytes='qux')
1340
self.assertFileEqual('qux', 'tree2/foo')
1342
def test_create_from_tree_symlink(self):
1343
self.requireFeature(SymlinkFeature)
1344
tree1 = self.make_branch_and_tree('tree1')
1345
os.symlink('bar', 'tree1/foo')
1346
tree1.add('foo', 'foo-id')
1347
tt = TreeTransform(self.make_branch_and_tree('tree2'))
1348
foo_trans_id = tt.create_path('foo', tt.root)
1349
create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1351
self.assertEqual('bar', os.readlink('tree2/foo'))
484
def test_find_interesting(self):
485
create, root = self.get_transform()
487
create.new_file('vfile', root, 'myfile-text', 'myfile-id')
488
create.new_file('uvfile', root, 'othertext')
490
self.assertEqual(find_interesting(wt, wt, ['vfile']),
492
self.assertRaises(NotVersionedError, find_interesting, wt, wt,
1354
496
class TransformGroup(object):
1356
def __init__(self, dirname, root_id):
497
def __init__(self, dirname):
1357
498
self.name = dirname
1358
499
os.mkdir(dirname)
1359
500
self.wt = BzrDir.create_standalone_workingtree(dirname)
1360
self.wt.set_root_id(root_id)
1361
501
self.b = self.wt.branch
1362
502
self.tt = TreeTransform(self.wt)
1363
503
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
1366
505
def conflict_text(tree, merge):
1367
506
template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
1368
507
return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
1371
510
class TestTransformMerge(TestCaseInTempDir):
1373
511
def test_text_merge(self):
1374
root_id = generate_ids.gen_root_id()
1375
base = TransformGroup("base", root_id)
512
base = TransformGroup("base")
1376
513
base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
1377
514
base.tt.new_file('b', base.root, 'b1', 'b')
1378
515
base.tt.new_file('c', base.root, 'c', 'c')
1567
700
a.add(['foo', 'foo/bar', 'foo/baz'])
1568
701
a.commit('initial commit')
1569
702
b = BzrDir.create_standalone_workingtree('b')
1570
basis = a.basis_tree()
1572
self.addCleanup(basis.unlock)
1573
build_tree(basis, b)
703
build_tree(a.basis_tree(), b)
1574
704
self.assertIs(os.path.isdir('b/foo'), True)
1575
705
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
1576
706
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1578
def test_build_with_references(self):
1579
tree = self.make_branch_and_tree('source',
1580
format='dirstate-with-subtree')
1581
subtree = self.make_branch_and_tree('source/subtree',
1582
format='dirstate-with-subtree')
1583
tree.add_reference(subtree)
1584
tree.commit('a revision')
1585
tree.branch.create_checkout('target')
1586
self.failUnlessExists('target')
1587
self.failUnlessExists('target/subtree')
1589
def test_file_conflict_handling(self):
1590
"""Ensure that when building trees, conflict handling is done"""
1591
source = self.make_branch_and_tree('source')
1592
target = self.make_branch_and_tree('target')
1593
self.build_tree(['source/file', 'target/file'])
1594
source.add('file', 'new-file')
1595
source.commit('added file')
1596
build_tree(source.basis_tree(), target)
1597
self.assertEqual([DuplicateEntry('Moved existing file to',
1598
'file.moved', 'file', None, 'new-file')],
1600
target2 = self.make_branch_and_tree('target2')
1601
target_file = file('target2/file', 'wb')
1603
source_file = file('source/file', 'rb')
1605
target_file.write(source_file.read())
1610
build_tree(source.basis_tree(), target2)
1611
self.assertEqual([], target2.conflicts())
1613
def test_symlink_conflict_handling(self):
1614
"""Ensure that when building trees, conflict handling is done"""
1615
self.requireFeature(SymlinkFeature)
1616
source = self.make_branch_and_tree('source')
1617
os.symlink('foo', 'source/symlink')
1618
source.add('symlink', 'new-symlink')
1619
source.commit('added file')
1620
target = self.make_branch_and_tree('target')
1621
os.symlink('bar', 'target/symlink')
1622
build_tree(source.basis_tree(), target)
1623
self.assertEqual([DuplicateEntry('Moved existing file to',
1624
'symlink.moved', 'symlink', None, 'new-symlink')],
1626
target = self.make_branch_and_tree('target2')
1627
os.symlink('foo', 'target2/symlink')
1628
build_tree(source.basis_tree(), target)
1629
self.assertEqual([], target.conflicts())
1631
def test_directory_conflict_handling(self):
1632
"""Ensure that when building trees, conflict handling is done"""
1633
source = self.make_branch_and_tree('source')
1634
target = self.make_branch_and_tree('target')
1635
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1636
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1637
source.commit('added file')
1638
build_tree(source.basis_tree(), target)
1639
self.assertEqual([], target.conflicts())
1640
self.failUnlessExists('target/dir1/file')
1642
# Ensure contents are merged
1643
target = self.make_branch_and_tree('target2')
1644
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1645
build_tree(source.basis_tree(), target)
1646
self.assertEqual([], target.conflicts())
1647
self.failUnlessExists('target2/dir1/file2')
1648
self.failUnlessExists('target2/dir1/file')
1650
# Ensure new contents are suppressed for existing branches
1651
target = self.make_branch_and_tree('target3')
1652
self.make_branch('target3/dir1')
1653
self.build_tree(['target3/dir1/file2'])
1654
build_tree(source.basis_tree(), target)
1655
self.failIfExists('target3/dir1/file')
1656
self.failUnlessExists('target3/dir1/file2')
1657
self.failUnlessExists('target3/dir1.diverted/file')
1658
self.assertEqual([DuplicateEntry('Diverted to',
1659
'dir1.diverted', 'dir1', 'new-dir1', None)],
1662
target = self.make_branch_and_tree('target4')
1663
self.build_tree(['target4/dir1/'])
1664
self.make_branch('target4/dir1/file')
1665
build_tree(source.basis_tree(), target)
1666
self.failUnlessExists('target4/dir1/file')
1667
self.assertEqual('directory', file_kind('target4/dir1/file'))
1668
self.failUnlessExists('target4/dir1/file.diverted')
1669
self.assertEqual([DuplicateEntry('Diverted to',
1670
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1673
def test_mixed_conflict_handling(self):
1674
"""Ensure that when building trees, conflict handling is done"""
1675
source = self.make_branch_and_tree('source')
1676
target = self.make_branch_and_tree('target')
1677
self.build_tree(['source/name', 'target/name/'])
1678
source.add('name', 'new-name')
1679
source.commit('added file')
1680
build_tree(source.basis_tree(), target)
1681
self.assertEqual([DuplicateEntry('Moved existing file to',
1682
'name.moved', 'name', None, 'new-name')], target.conflicts())
1684
def test_raises_in_populated(self):
1685
source = self.make_branch_and_tree('source')
1686
self.build_tree(['source/name'])
1688
source.commit('added name')
1689
target = self.make_branch_and_tree('target')
1690
self.build_tree(['target/name'])
1692
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1693
build_tree, source.basis_tree(), target)
1695
def test_build_tree_rename_count(self):
1696
source = self.make_branch_and_tree('source')
1697
self.build_tree(['source/file1', 'source/dir1/'])
1698
source.add(['file1', 'dir1'])
1699
source.commit('add1')
1700
target1 = self.make_branch_and_tree('target1')
1701
transform_result = build_tree(source.basis_tree(), target1)
1702
self.assertEqual(2, transform_result.rename_count)
1704
self.build_tree(['source/dir1/file2'])
1705
source.add(['dir1/file2'])
1706
source.commit('add3')
1707
target2 = self.make_branch_and_tree('target2')
1708
transform_result = build_tree(source.basis_tree(), target2)
1709
# children of non-root directories should not be renamed
1710
self.assertEqual(2, transform_result.rename_count)
1712
def create_ab_tree(self):
1713
"""Create a committed test tree with two files"""
1714
source = self.make_branch_and_tree('source')
1715
self.build_tree_contents([('source/file1', 'A')])
1716
self.build_tree_contents([('source/file2', 'B')])
1717
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1718
source.commit('commit files')
1720
self.addCleanup(source.unlock)
1723
def test_build_tree_accelerator_tree(self):
1724
source = self.create_ab_tree()
1725
self.build_tree_contents([('source/file2', 'C')])
1727
real_source_get_file = source.get_file
1728
def get_file(file_id, path=None):
1729
calls.append(file_id)
1730
return real_source_get_file(file_id, path)
1731
source.get_file = get_file
1732
target = self.make_branch_and_tree('target')
1733
revision_tree = source.basis_tree()
1734
revision_tree.lock_read()
1735
self.addCleanup(revision_tree.unlock)
1736
build_tree(revision_tree, target, source)
1737
self.assertEqual(['file1-id'], calls)
1739
self.addCleanup(target.unlock)
1740
self.assertEqual([], list(target.iter_changes(revision_tree)))
1742
def test_build_tree_accelerator_tree_missing_file(self):
1743
source = self.create_ab_tree()
1744
os.unlink('source/file1')
1745
source.remove(['file2'])
1746
target = self.make_branch_and_tree('target')
1747
revision_tree = source.basis_tree()
1748
revision_tree.lock_read()
1749
self.addCleanup(revision_tree.unlock)
1750
build_tree(revision_tree, target, source)
1752
self.addCleanup(target.unlock)
1753
self.assertEqual([], list(target.iter_changes(revision_tree)))
1755
def test_build_tree_accelerator_wrong_kind(self):
1756
self.requireFeature(SymlinkFeature)
1757
source = self.make_branch_and_tree('source')
1758
self.build_tree_contents([('source/file1', '')])
1759
self.build_tree_contents([('source/file2', '')])
1760
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1761
source.commit('commit files')
1762
os.unlink('source/file2')
1763
self.build_tree_contents([('source/file2/', 'C')])
1764
os.unlink('source/file1')
1765
os.symlink('file2', 'source/file1')
1767
real_source_get_file = source.get_file
1768
def get_file(file_id, path=None):
1769
calls.append(file_id)
1770
return real_source_get_file(file_id, path)
1771
source.get_file = get_file
1772
target = self.make_branch_and_tree('target')
1773
revision_tree = source.basis_tree()
1774
revision_tree.lock_read()
1775
self.addCleanup(revision_tree.unlock)
1776
build_tree(revision_tree, target, source)
1777
self.assertEqual([], calls)
1779
self.addCleanup(target.unlock)
1780
self.assertEqual([], list(target.iter_changes(revision_tree)))
1782
def test_build_tree_hardlink(self):
1783
self.requireFeature(HardlinkFeature)
1784
source = self.create_ab_tree()
1785
target = self.make_branch_and_tree('target')
1786
revision_tree = source.basis_tree()
1787
revision_tree.lock_read()
1788
self.addCleanup(revision_tree.unlock)
1789
build_tree(revision_tree, target, source, hardlink=True)
1791
self.addCleanup(target.unlock)
1792
self.assertEqual([], list(target.iter_changes(revision_tree)))
1793
source_stat = os.stat('source/file1')
1794
target_stat = os.stat('target/file1')
1795
self.assertEqual(source_stat, target_stat)
1797
# Explicitly disallowing hardlinks should prevent them.
1798
target2 = self.make_branch_and_tree('target2')
1799
build_tree(revision_tree, target2, source, hardlink=False)
1801
self.addCleanup(target2.unlock)
1802
self.assertEqual([], list(target2.iter_changes(revision_tree)))
1803
source_stat = os.stat('source/file1')
1804
target2_stat = os.stat('target2/file1')
1805
self.assertNotEqual(source_stat, target2_stat)
1807
def test_build_tree_accelerator_tree_moved(self):
1808
source = self.make_branch_and_tree('source')
1809
self.build_tree_contents([('source/file1', 'A')])
1810
source.add(['file1'], ['file1-id'])
1811
source.commit('commit files')
1812
source.rename_one('file1', 'file2')
1814
self.addCleanup(source.unlock)
1815
target = self.make_branch_and_tree('target')
1816
revision_tree = source.basis_tree()
1817
revision_tree.lock_read()
1818
self.addCleanup(revision_tree.unlock)
1819
build_tree(revision_tree, target, source)
1821
self.addCleanup(target.unlock)
1822
self.assertEqual([], list(target.iter_changes(revision_tree)))
1824
def test_build_tree_hardlinks_preserve_execute(self):
1825
self.requireFeature(HardlinkFeature)
1826
source = self.create_ab_tree()
1827
tt = TreeTransform(source)
1828
trans_id = tt.trans_id_tree_file_id('file1-id')
1829
tt.set_executability(True, trans_id)
1831
self.assertTrue(source.is_executable('file1-id'))
1832
target = self.make_branch_and_tree('target')
1833
revision_tree = source.basis_tree()
1834
revision_tree.lock_read()
1835
self.addCleanup(revision_tree.unlock)
1836
build_tree(revision_tree, target, source, hardlink=True)
1838
self.addCleanup(target.unlock)
1839
self.assertEqual([], list(target.iter_changes(revision_tree)))
1840
self.assertTrue(source.is_executable('file1-id'))
1842
def test_case_insensitive_build_tree_inventory(self):
1843
source = self.make_branch_and_tree('source')
1844
self.build_tree(['source/file', 'source/FILE'])
1845
source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1846
source.commit('added files')
1847
# Don't try this at home, kids!
1848
# Force the tree to report that it is case insensitive
1849
target = self.make_branch_and_tree('target')
1850
target.case_sensitive = False
1851
build_tree(source.basis_tree(), target, source, delta_from_tree=True)
1852
self.assertEqual('file.moved', target.id2path('lower-id'))
1853
self.assertEqual('FILE', target.id2path('upper-id'))
1856
708
class MockTransform(object):
1858
710
def has_named_child(self, by_parent, parent_id, name):
1884
734
self.assertEqual(name, 'name.~1~')
1885
735
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
1886
736
self.assertEqual(name, 'name.~4~')
1889
class TestFileMover(tests.TestCaseWithTransport):
1891
def test_file_mover(self):
1892
self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
1893
mover = _FileMover()
1894
mover.rename('a', 'q')
1895
self.failUnlessExists('q')
1896
self.failIfExists('a')
1897
self.failUnlessExists('q/b')
1898
self.failUnlessExists('c')
1899
self.failUnlessExists('c/d')
1901
def test_pre_delete_rollback(self):
1902
self.build_tree(['a/'])
1903
mover = _FileMover()
1904
mover.pre_delete('a', 'q')
1905
self.failUnlessExists('q')
1906
self.failIfExists('a')
1908
self.failIfExists('q')
1909
self.failUnlessExists('a')
1911
def test_apply_deletions(self):
1912
self.build_tree(['a/', 'b/'])
1913
mover = _FileMover()
1914
mover.pre_delete('a', 'q')
1915
mover.pre_delete('b', 'r')
1916
self.failUnlessExists('q')
1917
self.failUnlessExists('r')
1918
self.failIfExists('a')
1919
self.failIfExists('b')
1920
mover.apply_deletions()
1921
self.failIfExists('q')
1922
self.failIfExists('r')
1923
self.failIfExists('a')
1924
self.failIfExists('b')
1926
def test_file_mover_rollback(self):
1927
self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
1928
mover = _FileMover()
1929
mover.rename('c/d', 'c/f')
1930
mover.rename('c/e', 'c/d')
1932
mover.rename('a', 'c')
1933
except errors.FileExists, e:
1935
self.failUnlessExists('a')
1936
self.failUnlessExists('c/d')
1939
class Bogus(Exception):
1943
class TestTransformRollback(tests.TestCaseWithTransport):
1945
class ExceptionFileMover(_FileMover):
1947
def __init__(self, bad_source=None, bad_target=None):
1948
_FileMover.__init__(self)
1949
self.bad_source = bad_source
1950
self.bad_target = bad_target
1952
def rename(self, source, target):
1953
if (self.bad_source is not None and
1954
source.endswith(self.bad_source)):
1956
elif (self.bad_target is not None and
1957
target.endswith(self.bad_target)):
1960
_FileMover.rename(self, source, target)
1962
def test_rollback_rename(self):
1963
tree = self.make_branch_and_tree('.')
1964
self.build_tree(['a/', 'a/b'])
1965
tt = TreeTransform(tree)
1966
self.addCleanup(tt.finalize)
1967
a_id = tt.trans_id_tree_path('a')
1968
tt.adjust_path('c', tt.root, a_id)
1969
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1970
self.assertRaises(Bogus, tt.apply,
1971
_mover=self.ExceptionFileMover(bad_source='a'))
1972
self.failUnlessExists('a')
1973
self.failUnlessExists('a/b')
1975
self.failUnlessExists('c')
1976
self.failUnlessExists('c/d')
1978
def test_rollback_rename_into_place(self):
1979
tree = self.make_branch_and_tree('.')
1980
self.build_tree(['a/', 'a/b'])
1981
tt = TreeTransform(tree)
1982
self.addCleanup(tt.finalize)
1983
a_id = tt.trans_id_tree_path('a')
1984
tt.adjust_path('c', tt.root, a_id)
1985
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1986
self.assertRaises(Bogus, tt.apply,
1987
_mover=self.ExceptionFileMover(bad_target='c/d'))
1988
self.failUnlessExists('a')
1989
self.failUnlessExists('a/b')
1991
self.failUnlessExists('c')
1992
self.failUnlessExists('c/d')
1994
def test_rollback_deletion(self):
1995
tree = self.make_branch_and_tree('.')
1996
self.build_tree(['a/', 'a/b'])
1997
tt = TreeTransform(tree)
1998
self.addCleanup(tt.finalize)
1999
a_id = tt.trans_id_tree_path('a')
2000
tt.delete_contents(a_id)
2001
tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
2002
self.assertRaises(Bogus, tt.apply,
2003
_mover=self.ExceptionFileMover(bad_target='d'))
2004
self.failUnlessExists('a')
2005
self.failUnlessExists('a/b')
2007
def test_resolve_no_parent(self):
2008
wt = self.make_branch_and_tree('.')
2009
tt = TreeTransform(wt)
2010
self.addCleanup(tt.finalize)
2011
parent = tt.trans_id_file_id('parent-id')
2012
tt.new_file('file', parent, 'Contents')
2013
resolve_conflicts(tt)
2016
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
2017
('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
2019
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
2020
('', ''), ('directory', 'directory'), (False, None))
2023
class TestTransformPreview(tests.TestCaseWithTransport):
2025
def create_tree(self):
2026
tree = self.make_branch_and_tree('.')
2027
self.build_tree_contents([('a', 'content 1')])
2028
tree.add('a', 'a-id')
2029
tree.commit('rev1', rev_id='rev1')
2030
return tree.branch.repository.revision_tree('rev1')
2032
def get_empty_preview(self):
2033
repository = self.make_repository('repo')
2034
tree = repository.revision_tree(_mod_revision.NULL_REVISION)
2035
preview = TransformPreview(tree)
2036
self.addCleanup(preview.finalize)
2039
def test_transform_preview(self):
2040
revision_tree = self.create_tree()
2041
preview = TransformPreview(revision_tree)
2042
self.addCleanup(preview.finalize)
2044
def test_transform_preview_tree(self):
2045
revision_tree = self.create_tree()
2046
preview = TransformPreview(revision_tree)
2047
self.addCleanup(preview.finalize)
2048
preview.get_preview_tree()
2050
def test_transform_new_file(self):
2051
revision_tree = self.create_tree()
2052
preview = TransformPreview(revision_tree)
2053
self.addCleanup(preview.finalize)
2054
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2055
preview_tree = preview.get_preview_tree()
2056
self.assertEqual(preview_tree.kind('file2-id'), 'file')
2058
preview_tree.get_file('file2-id').read(), 'content B\n')
2060
def test_diff_preview_tree(self):
2061
revision_tree = self.create_tree()
2062
preview = TransformPreview(revision_tree)
2063
self.addCleanup(preview.finalize)
2064
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2065
preview_tree = preview.get_preview_tree()
2067
show_diff_trees(revision_tree, preview_tree, out)
2068
lines = out.getvalue().splitlines()
2069
self.assertEqual(lines[0], "=== added file 'file2'")
2070
# 3 lines of diff administrivia
2071
self.assertEqual(lines[4], "+content B")
2073
def test_transform_conflicts(self):
2074
revision_tree = self.create_tree()
2075
preview = TransformPreview(revision_tree)
2076
self.addCleanup(preview.finalize)
2077
preview.new_file('a', preview.root, 'content 2')
2078
resolve_conflicts(preview)
2079
trans_id = preview.trans_id_file_id('a-id')
2080
self.assertEqual('a.moved', preview.final_name(trans_id))
2082
def get_tree_and_preview_tree(self):
2083
revision_tree = self.create_tree()
2084
preview = TransformPreview(revision_tree)
2085
self.addCleanup(preview.finalize)
2086
a_trans_id = preview.trans_id_file_id('a-id')
2087
preview.delete_contents(a_trans_id)
2088
preview.create_file('b content', a_trans_id)
2089
preview_tree = preview.get_preview_tree()
2090
return revision_tree, preview_tree
2092
def test_iter_changes(self):
2093
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2094
root = revision_tree.inventory.root.file_id
2095
self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
2096
(root, root), ('a', 'a'), ('file', 'file'),
2098
list(preview_tree.iter_changes(revision_tree)))
2100
def test_include_unchanged_succeeds(self):
2101
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2102
changes = preview_tree.iter_changes(revision_tree,
2103
include_unchanged=True)
2104
root = revision_tree.inventory.root.file_id
2106
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2108
def test_specific_files(self):
2109
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2110
changes = preview_tree.iter_changes(revision_tree,
2111
specific_files=[''])
2112
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2114
def test_want_unversioned(self):
2115
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2116
changes = preview_tree.iter_changes(revision_tree,
2117
want_unversioned=True)
2118
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2120
def test_ignore_extra_trees_no_specific_files(self):
2121
# extra_trees is harmless without specific_files, so we'll silently
2122
# accept it, even though we won't use it.
2123
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2124
preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
2126
def test_ignore_require_versioned_no_specific_files(self):
2127
# require_versioned is meaningless without specific_files.
2128
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2129
preview_tree.iter_changes(revision_tree, require_versioned=False)
2131
def test_ignore_pb(self):
2132
# pb could be supported, but TT.iter_changes doesn't support it.
2133
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2134
preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
2136
def test_kind(self):
2137
revision_tree = self.create_tree()
2138
preview = TransformPreview(revision_tree)
2139
self.addCleanup(preview.finalize)
2140
preview.new_file('file', preview.root, 'contents', 'file-id')
2141
preview.new_directory('directory', preview.root, 'dir-id')
2142
preview_tree = preview.get_preview_tree()
2143
self.assertEqual('file', preview_tree.kind('file-id'))
2144
self.assertEqual('directory', preview_tree.kind('dir-id'))
2146
def test_get_file_mtime(self):
2147
preview = self.get_empty_preview()
2148
file_trans_id = preview.new_file('file', preview.root, 'contents',
2150
limbo_path = preview._limbo_name(file_trans_id)
2151
preview_tree = preview.get_preview_tree()
2152
self.assertEqual(os.stat(limbo_path).st_mtime,
2153
preview_tree.get_file_mtime('file-id'))
2155
def test_get_file(self):
2156
preview = self.get_empty_preview()
2157
preview.new_file('file', preview.root, 'contents', 'file-id')
2158
preview_tree = preview.get_preview_tree()
2159
tree_file = preview_tree.get_file('file-id')
2161
self.assertEqual('contents', tree_file.read())
2165
def test_get_symlink_target(self):
2166
self.requireFeature(SymlinkFeature)
2167
preview = self.get_empty_preview()
2168
preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
2169
preview_tree = preview.get_preview_tree()
2170
self.assertEqual('target',
2171
preview_tree.get_symlink_target('symlink-id'))
2173
def test_all_file_ids(self):
2174
tree = self.make_branch_and_tree('tree')
2175
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
2176
tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
2177
preview = TransformPreview(tree)
2178
self.addCleanup(preview.finalize)
2179
preview.unversion_file(preview.trans_id_file_id('b-id'))
2180
c_trans_id = preview.trans_id_file_id('c-id')
2181
preview.unversion_file(c_trans_id)
2182
preview.version_file('c-id', c_trans_id)
2183
preview_tree = preview.get_preview_tree()
2184
self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
2185
preview_tree.all_file_ids())
2187
def test_path2id_deleted_unchanged(self):
2188
tree = self.make_branch_and_tree('tree')
2189
self.build_tree(['tree/unchanged', 'tree/deleted'])
2190
tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
2191
preview = TransformPreview(tree)
2192
self.addCleanup(preview.finalize)
2193
preview.unversion_file(preview.trans_id_file_id('deleted-id'))
2194
preview_tree = preview.get_preview_tree()
2195
self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
2196
self.assertIs(None, preview_tree.path2id('deleted'))
2198
def test_path2id_created(self):
2199
tree = self.make_branch_and_tree('tree')
2200
self.build_tree(['tree/unchanged'])
2201
tree.add(['unchanged'], ['unchanged-id'])
2202
preview = TransformPreview(tree)
2203
self.addCleanup(preview.finalize)
2204
preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
2205
'contents', 'new-id')
2206
preview_tree = preview.get_preview_tree()
2207
self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
2209
def test_path2id_moved(self):
2210
tree = self.make_branch_and_tree('tree')
2211
self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
2212
tree.add(['old_parent', 'old_parent/child'],
2213
['old_parent-id', 'child-id'])
2214
preview = TransformPreview(tree)
2215
self.addCleanup(preview.finalize)
2216
new_parent = preview.new_directory('new_parent', preview.root,
2218
preview.adjust_path('child', new_parent,
2219
preview.trans_id_file_id('child-id'))
2220
preview_tree = preview.get_preview_tree()
2221
self.assertIs(None, preview_tree.path2id('old_parent/child'))
2222
self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
2224
def test_path2id_renamed_parent(self):
2225
tree = self.make_branch_and_tree('tree')
2226
self.build_tree(['tree/old_name/', 'tree/old_name/child'])
2227
tree.add(['old_name', 'old_name/child'],
2228
['parent-id', 'child-id'])
2229
preview = TransformPreview(tree)
2230
self.addCleanup(preview.finalize)
2231
preview.adjust_path('new_name', preview.root,
2232
preview.trans_id_file_id('parent-id'))
2233
preview_tree = preview.get_preview_tree()
2234
self.assertIs(None, preview_tree.path2id('old_name/child'))
2235
self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
2237
def assertMatchingIterEntries(self, tt, specific_file_ids=None):
2238
preview_tree = tt.get_preview_tree()
2239
preview_result = list(preview_tree.iter_entries_by_dir(
2243
actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
2244
self.assertEqual(actual_result, preview_result)
2246
def test_iter_entries_by_dir_new(self):
2247
tree = self.make_branch_and_tree('tree')
2248
tt = TreeTransform(tree)
2249
tt.new_file('new', tt.root, 'contents', 'new-id')
2250
self.assertMatchingIterEntries(tt)
2252
def test_iter_entries_by_dir_deleted(self):
2253
tree = self.make_branch_and_tree('tree')
2254
self.build_tree(['tree/deleted'])
2255
tree.add('deleted', 'deleted-id')
2256
tt = TreeTransform(tree)
2257
tt.delete_contents(tt.trans_id_file_id('deleted-id'))
2258
self.assertMatchingIterEntries(tt)
2260
def test_iter_entries_by_dir_unversioned(self):
2261
tree = self.make_branch_and_tree('tree')
2262
self.build_tree(['tree/removed'])
2263
tree.add('removed', 'removed-id')
2264
tt = TreeTransform(tree)
2265
tt.unversion_file(tt.trans_id_file_id('removed-id'))
2266
self.assertMatchingIterEntries(tt)
2268
def test_iter_entries_by_dir_moved(self):
2269
tree = self.make_branch_and_tree('tree')
2270
self.build_tree(['tree/moved', 'tree/new_parent/'])
2271
tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
2272
tt = TreeTransform(tree)
2273
tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
2274
tt.trans_id_file_id('moved-id'))
2275
self.assertMatchingIterEntries(tt)
2277
def test_iter_entries_by_dir_specific_file_ids(self):
2278
tree = self.make_branch_and_tree('tree')
2279
tree.set_root_id('tree-root-id')
2280
self.build_tree(['tree/parent/', 'tree/parent/child'])
2281
tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
2282
tt = TreeTransform(tree)
2283
self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
2285
def test_symlink_content_summary(self):
2286
self.requireFeature(SymlinkFeature)
2287
preview = self.get_empty_preview()
2288
preview.new_symlink('path', preview.root, 'target', 'path-id')
2289
summary = preview.get_preview_tree().path_content_summary('path')
2290
self.assertEqual(('symlink', None, None, 'target'), summary)
2292
def test_missing_content_summary(self):
2293
preview = self.get_empty_preview()
2294
summary = preview.get_preview_tree().path_content_summary('path')
2295
self.assertEqual(('missing', None, None, None), summary)
2297
def test_deleted_content_summary(self):
2298
tree = self.make_branch_and_tree('tree')
2299
self.build_tree(['tree/path/'])
2301
preview = TransformPreview(tree)
2302
self.addCleanup(preview.finalize)
2303
preview.delete_contents(preview.trans_id_tree_path('path'))
2304
summary = preview.get_preview_tree().path_content_summary('path')
2305
self.assertEqual(('missing', None, None, None), summary)
2307
def test_file_content_summary_executable(self):
2308
if not osutils.supports_executable():
2309
raise TestNotApplicable()
2310
preview = self.get_empty_preview()
2311
path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2312
preview.set_executability(True, path_id)
2313
summary = preview.get_preview_tree().path_content_summary('path')
2314
self.assertEqual(4, len(summary))
2315
self.assertEqual('file', summary[0])
2316
# size must be known
2317
self.assertEqual(len('contents'), summary[1])
2319
self.assertEqual(True, summary[2])
2320
# will not have hash (not cheap to determine)
2321
self.assertIs(None, summary[3])
2323
def test_change_executability(self):
2324
if not osutils.supports_executable():
2325
raise TestNotApplicable()
2326
tree = self.make_branch_and_tree('tree')
2327
self.build_tree(['tree/path'])
2329
preview = TransformPreview(tree)
2330
self.addCleanup(preview.finalize)
2331
path_id = preview.trans_id_tree_path('path')
2332
preview.set_executability(True, path_id)
2333
summary = preview.get_preview_tree().path_content_summary('path')
2334
self.assertEqual(True, summary[2])
2336
def test_file_content_summary_non_exec(self):
2337
preview = self.get_empty_preview()
2338
preview.new_file('path', preview.root, 'contents', 'path-id')
2339
summary = preview.get_preview_tree().path_content_summary('path')
2340
self.assertEqual(4, len(summary))
2341
self.assertEqual('file', summary[0])
2342
# size must be known
2343
self.assertEqual(len('contents'), summary[1])
2345
if osutils.supports_executable():
2346
self.assertEqual(False, summary[2])
2348
self.assertEqual(None, summary[2])
2349
# will not have hash (not cheap to determine)
2350
self.assertIs(None, summary[3])
2352
def test_dir_content_summary(self):
2353
preview = self.get_empty_preview()
2354
preview.new_directory('path', preview.root, 'path-id')
2355
summary = preview.get_preview_tree().path_content_summary('path')
2356
self.assertEqual(('directory', None, None, None), summary)
2358
def test_tree_content_summary(self):
2359
preview = self.get_empty_preview()
2360
path = preview.new_directory('path', preview.root, 'path-id')
2361
preview.set_tree_reference('rev-1', path)
2362
summary = preview.get_preview_tree().path_content_summary('path')
2363
self.assertEqual(4, len(summary))
2364
self.assertEqual('tree-reference', summary[0])
2366
def test_annotate(self):
2367
tree = self.make_branch_and_tree('tree')
2368
self.build_tree_contents([('tree/file', 'a\n')])
2369
tree.add('file', 'file-id')
2370
tree.commit('a', rev_id='one')
2371
self.build_tree_contents([('tree/file', 'a\nb\n')])
2372
preview = TransformPreview(tree)
2373
self.addCleanup(preview.finalize)
2374
file_trans_id = preview.trans_id_file_id('file-id')
2375
preview.delete_contents(file_trans_id)
2376
preview.create_file('a\nb\nc\n', file_trans_id)
2377
preview_tree = preview.get_preview_tree()
2383
annotation = preview_tree.annotate_iter('file-id', 'me:')
2384
self.assertEqual(expected, annotation)
2386
def test_annotate_missing(self):
2387
preview = self.get_empty_preview()
2388
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2389
preview_tree = preview.get_preview_tree()
2395
annotation = preview_tree.annotate_iter('file-id', 'me:')
2396
self.assertEqual(expected, annotation)
2398
def test_annotate_rename(self):
2399
tree = self.make_branch_and_tree('tree')
2400
self.build_tree_contents([('tree/file', 'a\n')])
2401
tree.add('file', 'file-id')
2402
tree.commit('a', rev_id='one')
2403
preview = TransformPreview(tree)
2404
self.addCleanup(preview.finalize)
2405
file_trans_id = preview.trans_id_file_id('file-id')
2406
preview.adjust_path('newname', preview.root, file_trans_id)
2407
preview_tree = preview.get_preview_tree()
2411
annotation = preview_tree.annotate_iter('file-id', 'me:')
2412
self.assertEqual(expected, annotation)
2414
def test_annotate_deleted(self):
2415
tree = self.make_branch_and_tree('tree')
2416
self.build_tree_contents([('tree/file', 'a\n')])
2417
tree.add('file', 'file-id')
2418
tree.commit('a', rev_id='one')
2419
self.build_tree_contents([('tree/file', 'a\nb\n')])
2420
preview = TransformPreview(tree)
2421
self.addCleanup(preview.finalize)
2422
file_trans_id = preview.trans_id_file_id('file-id')
2423
preview.delete_contents(file_trans_id)
2424
preview_tree = preview.get_preview_tree()
2425
annotation = preview_tree.annotate_iter('file-id', 'me:')
2426
self.assertIs(None, annotation)
2428
def test_stored_kind(self):
2429
preview = self.get_empty_preview()
2430
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2431
preview_tree = preview.get_preview_tree()
2432
self.assertEqual('file', preview_tree.stored_kind('file-id'))
2434
def test_is_executable(self):
2435
preview = self.get_empty_preview()
2436
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2437
preview.set_executability(True, preview.trans_id_file_id('file-id'))
2438
preview_tree = preview.get_preview_tree()
2439
self.assertEqual(True, preview_tree.is_executable('file-id'))
2441
def test_get_set_parent_ids(self):
2442
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2443
self.assertEqual([], preview_tree.get_parent_ids())
2444
preview_tree.set_parent_ids(['rev-1'])
2445
self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
2447
def test_plan_file_merge(self):
2448
work_a = self.make_branch_and_tree('wta')
2449
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2450
work_a.add('file', 'file-id')
2451
base_id = work_a.commit('base version')
2452
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2453
preview = TransformPreview(work_a)
2454
self.addCleanup(preview.finalize)
2455
trans_id = preview.trans_id_file_id('file-id')
2456
preview.delete_contents(trans_id)
2457
preview.create_file('b\nc\nd\ne\n', trans_id)
2458
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2459
tree_a = preview.get_preview_tree()
2460
tree_a.set_parent_ids([base_id])
2462
('killed-a', 'a\n'),
2463
('killed-b', 'b\n'),
2464
('unchanged', 'c\n'),
2465
('unchanged', 'd\n'),
2468
], list(tree_a.plan_file_merge('file-id', tree_b)))
2470
def test_plan_file_merge_revision_tree(self):
2471
work_a = self.make_branch_and_tree('wta')
2472
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2473
work_a.add('file', 'file-id')
2474
base_id = work_a.commit('base version')
2475
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2476
preview = TransformPreview(work_a.basis_tree())
2477
self.addCleanup(preview.finalize)
2478
trans_id = preview.trans_id_file_id('file-id')
2479
preview.delete_contents(trans_id)
2480
preview.create_file('b\nc\nd\ne\n', trans_id)
2481
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2482
tree_a = preview.get_preview_tree()
2483
tree_a.set_parent_ids([base_id])
2485
('killed-a', 'a\n'),
2486
('killed-b', 'b\n'),
2487
('unchanged', 'c\n'),
2488
('unchanged', 'd\n'),
2491
], list(tree_a.plan_file_merge('file-id', tree_b)))
2493
def test_walkdirs(self):
2494
preview = self.get_empty_preview()
2495
preview.version_file('tree-root', preview.root)
2496
preview_tree = preview.get_preview_tree()
2497
file_trans_id = preview.new_file('a', preview.root, 'contents',
2499
expected = [(('', 'tree-root'),
2500
[('a', 'a', 'file', None, 'a-id', 'file')])]
2501
self.assertEqual(expected, list(preview_tree.walkdirs()))
2503
def test_extras(self):
2504
work_tree = self.make_branch_and_tree('tree')
2505
self.build_tree(['tree/removed-file', 'tree/existing-file',
2506
'tree/not-removed-file'])
2507
work_tree.add(['removed-file', 'not-removed-file'])
2508
preview = TransformPreview(work_tree)
2509
self.addCleanup(preview.finalize)
2510
preview.new_file('new-file', preview.root, 'contents')
2511
preview.new_file('new-versioned-file', preview.root, 'contents',
2513
tree = preview.get_preview_tree()
2514
preview.unversion_file(preview.trans_id_tree_path('removed-file'))
2515
self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
2518
def test_merge_into_preview(self):
2519
work_tree = self.make_branch_and_tree('tree')
2520
self.build_tree_contents([('tree/file','b\n')])
2521
work_tree.add('file', 'file-id')
2522
work_tree.commit('first commit')
2523
child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
2524
self.build_tree_contents([('child/file','b\nc\n')])
2525
child_tree.commit('child commit')
2526
child_tree.lock_write()
2527
self.addCleanup(child_tree.unlock)
2528
work_tree.lock_write()
2529
self.addCleanup(work_tree.unlock)
2530
preview = TransformPreview(work_tree)
2531
self.addCleanup(preview.finalize)
2532
preview_tree = preview.get_preview_tree()
2533
file_trans_id = preview.trans_id_file_id('file-id')
2534
preview.delete_contents(file_trans_id)
2535
preview.create_file('a\nb\n', file_trans_id)
2536
pb = progress.DummyProgress()
2537
merger = Merger.from_revision_ids(pb, preview_tree,
2538
child_tree.branch.last_revision(),
2539
other_branch=child_tree.branch,
2540
tree_branch=work_tree.branch)
2541
merger.merge_type = Merge3Merger
2542
tt = merger.make_merger().make_preview_transform()
2543
self.addCleanup(tt.finalize)
2544
final_tree = tt.get_preview_tree()
2545
self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
2547
def test_merge_preview_into_workingtree(self):
2548
tree = self.make_branch_and_tree('tree')
2549
tt = TransformPreview(tree)
2550
self.addCleanup(tt.finalize)
2551
tt.new_file('name', tt.root, 'content', 'file-id')
2552
tree2 = self.make_branch_and_tree('tree2')
2553
pb = progress.DummyProgress()
2554
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2555
pb, tree.basis_tree())
2556
merger.merge_type = Merge3Merger
2559
def test_merge_preview_into_workingtree_handles_conflicts(self):
2560
tree = self.make_branch_and_tree('tree')
2561
self.build_tree_contents([('tree/foo', 'bar')])
2562
tree.add('foo', 'foo-id')
2564
tt = TransformPreview(tree)
2565
self.addCleanup(tt.finalize)
2566
trans_id = tt.trans_id_file_id('foo-id')
2567
tt.delete_contents(trans_id)
2568
tt.create_file('baz', trans_id)
2569
tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2570
self.build_tree_contents([('tree2/foo', 'qux')])
2571
pb = progress.DummyProgress()
2572
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2573
pb, tree.basis_tree())
2574
merger.merge_type = Merge3Merger
2577
def test_is_executable(self):
2578
tree = self.make_branch_and_tree('tree')
2579
preview = TransformPreview(tree)
2580
self.addCleanup(preview.finalize)
2581
preview.new_file('foo', preview.root, 'bar', 'baz-id')
2582
preview_tree = preview.get_preview_tree()
2583
self.assertEqual(False, preview_tree.is_executable('baz-id',
2585
self.assertEqual(False, preview_tree.is_executable('baz-id'))