463
764
rename.set_executability(True, myfile)
466
def test_find_interesting(self):
467
create, root = self.get_transform()
469
create.new_file('vfile', root, 'myfile-text', 'myfile-id')
470
create.new_file('uvfile', root, 'othertext')
472
self.assertEqual(find_interesting(wt, wt, ['vfile']),
474
self.assertRaises(NotVersionedError, find_interesting, wt, wt,
767
def test_set_executability_order(self):
768
"""Ensure that executability behaves the same, no matter what order.
770
- create file and set executability simultaneously
771
- create file and set executability afterward
772
- unsetting the executability of a file whose executability has not been
773
declared should throw an exception (this may happen when a
774
merge attempts to create a file with a duplicate ID)
776
transform, root = self.get_transform()
779
self.addCleanup(wt.unlock)
780
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
782
sac = transform.new_file('set_after_creation', root,
783
'Set after creation', 'sac')
784
transform.set_executability(True, sac)
785
uws = transform.new_file('unset_without_set', root, 'Unset badly',
787
self.assertRaises(KeyError, transform.set_executability, None, uws)
789
self.assertTrue(wt.is_executable('soc'))
790
self.assertTrue(wt.is_executable('sac'))
792
def test_preserve_mode(self):
793
"""File mode is preserved when replacing content"""
794
if sys.platform == 'win32':
795
raise TestSkipped('chmod has no effect on win32')
796
transform, root = self.get_transform()
797
transform.new_file('file1', root, 'contents', 'file1-id', True)
800
self.addCleanup(self.wt.unlock)
801
self.assertTrue(self.wt.is_executable('file1-id'))
802
transform, root = self.get_transform()
803
file1_id = transform.trans_id_tree_file_id('file1-id')
804
transform.delete_contents(file1_id)
805
transform.create_file('contents2', file1_id)
807
self.assertTrue(self.wt.is_executable('file1-id'))
809
def test__set_mode_stats_correctly(self):
810
"""_set_mode stats to determine file mode."""
811
if sys.platform == 'win32':
812
raise TestSkipped('chmod has no effect on win32')
816
def instrumented_stat(path):
817
stat_paths.append(path)
818
return real_stat(path)
820
transform, root = self.get_transform()
822
bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
823
file_id='bar-id-1', executable=False)
826
transform, root = self.get_transform()
827
bar1_id = transform.trans_id_tree_path('bar')
828
bar2_id = transform.trans_id_tree_path('bar2')
830
os.stat = instrumented_stat
831
transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
836
bar1_abspath = self.wt.abspath('bar')
837
self.assertEqual([bar1_abspath], stat_paths)
839
def test_iter_changes(self):
840
self.wt.set_root_id('eert_toor')
841
transform, root = self.get_transform()
842
transform.new_file('old', root, 'blah', 'id-1', True)
844
transform, root = self.get_transform()
846
self.assertEqual([], list(transform.iter_changes()))
847
old = transform.trans_id_tree_file_id('id-1')
848
transform.unversion_file(old)
849
self.assertEqual([('id-1', ('old', None), False, (True, False),
850
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
851
(True, True))], list(transform.iter_changes()))
852
transform.new_directory('new', root, 'id-1')
853
self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
854
('eert_toor', 'eert_toor'), ('old', 'new'),
855
('file', 'directory'),
856
(True, False))], list(transform.iter_changes()))
860
def test_iter_changes_new(self):
861
self.wt.set_root_id('eert_toor')
862
transform, root = self.get_transform()
863
transform.new_file('old', root, 'blah')
865
transform, root = self.get_transform()
867
old = transform.trans_id_tree_path('old')
868
transform.version_file('id-1', old)
869
self.assertEqual([('id-1', (None, 'old'), False, (False, True),
870
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
871
(False, False))], list(transform.iter_changes()))
875
def test_iter_changes_modifications(self):
876
self.wt.set_root_id('eert_toor')
877
transform, root = self.get_transform()
878
transform.new_file('old', root, 'blah', 'id-1')
879
transform.new_file('new', root, 'blah')
880
transform.new_directory('subdir', root, 'subdir-id')
882
transform, root = self.get_transform()
884
old = transform.trans_id_tree_path('old')
885
subdir = transform.trans_id_tree_file_id('subdir-id')
886
new = transform.trans_id_tree_path('new')
887
self.assertEqual([], list(transform.iter_changes()))
890
transform.delete_contents(old)
891
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
892
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
893
(False, False))], list(transform.iter_changes()))
896
transform.create_file('blah', old)
897
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
898
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
899
(False, False))], list(transform.iter_changes()))
900
transform.cancel_deletion(old)
901
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
902
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
903
(False, False))], list(transform.iter_changes()))
904
transform.cancel_creation(old)
906
# move file_id to a different file
907
self.assertEqual([], list(transform.iter_changes()))
908
transform.unversion_file(old)
909
transform.version_file('id-1', new)
910
transform.adjust_path('old', root, new)
911
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
912
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
913
(False, False))], list(transform.iter_changes()))
914
transform.cancel_versioning(new)
915
transform._removed_id = set()
918
self.assertEqual([], list(transform.iter_changes()))
919
transform.set_executability(True, old)
920
self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
921
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
922
(False, True))], list(transform.iter_changes()))
923
transform.set_executability(None, old)
926
self.assertEqual([], list(transform.iter_changes()))
927
transform.adjust_path('new', root, old)
928
transform._new_parent = {}
929
self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
930
('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
931
(False, False))], list(transform.iter_changes()))
932
transform._new_name = {}
935
self.assertEqual([], list(transform.iter_changes()))
936
transform.adjust_path('new', subdir, old)
937
transform._new_name = {}
938
self.assertEqual([('id-1', ('old', 'subdir/old'), False,
939
(True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
940
('file', 'file'), (False, False))],
941
list(transform.iter_changes()))
942
transform._new_path = {}
947
def test_iter_changes_modified_bleed(self):
948
self.wt.set_root_id('eert_toor')
949
"""Modified flag should not bleed from one change to another"""
950
# unfortunately, we have no guarantee that file1 (which is modified)
951
# will be applied before file2. And if it's applied after file2, it
952
# obviously can't bleed into file2's change output. But for now, it
954
transform, root = self.get_transform()
955
transform.new_file('file1', root, 'blah', 'id-1')
956
transform.new_file('file2', root, 'blah', 'id-2')
958
transform, root = self.get_transform()
960
transform.delete_contents(transform.trans_id_file_id('id-1'))
961
transform.set_executability(True,
962
transform.trans_id_file_id('id-2'))
963
self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
964
('eert_toor', 'eert_toor'), ('file1', u'file1'),
965
('file', None), (False, False)),
966
('id-2', (u'file2', u'file2'), False, (True, True),
967
('eert_toor', 'eert_toor'), ('file2', u'file2'),
968
('file', 'file'), (False, True))],
969
list(transform.iter_changes()))
973
def test_iter_changes_move_missing(self):
974
"""Test moving ids with no files around"""
975
self.wt.set_root_id('toor_eert')
976
# Need two steps because versioning a non-existant file is a conflict.
977
transform, root = self.get_transform()
978
transform.new_directory('floater', root, 'floater-id')
980
transform, root = self.get_transform()
981
transform.delete_contents(transform.trans_id_tree_path('floater'))
983
transform, root = self.get_transform()
984
floater = transform.trans_id_tree_path('floater')
986
transform.adjust_path('flitter', root, floater)
987
self.assertEqual([('floater-id', ('floater', 'flitter'), False,
988
(True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
989
(None, None), (False, False))], list(transform.iter_changes()))
993
def test_iter_changes_pointless(self):
994
"""Ensure that no-ops are not treated as modifications"""
995
self.wt.set_root_id('eert_toor')
996
transform, root = self.get_transform()
997
transform.new_file('old', root, 'blah', 'id-1')
998
transform.new_directory('subdir', root, 'subdir-id')
1000
transform, root = self.get_transform()
1002
old = transform.trans_id_tree_path('old')
1003
subdir = transform.trans_id_tree_file_id('subdir-id')
1004
self.assertEqual([], list(transform.iter_changes()))
1005
transform.delete_contents(subdir)
1006
transform.create_directory(subdir)
1007
transform.set_executability(False, old)
1008
transform.unversion_file(old)
1009
transform.version_file('id-1', old)
1010
transform.adjust_path('old', root, old)
1011
self.assertEqual([], list(transform.iter_changes()))
1013
transform.finalize()
1015
def test_rename_count(self):
1016
transform, root = self.get_transform()
1017
transform.new_file('name1', root, 'contents')
1018
self.assertEqual(transform.rename_count, 0)
1020
self.assertEqual(transform.rename_count, 1)
1021
transform2, root = self.get_transform()
1022
transform2.adjust_path('name2', root,
1023
transform2.trans_id_tree_path('name1'))
1024
self.assertEqual(transform2.rename_count, 0)
1026
self.assertEqual(transform2.rename_count, 2)
1028
def test_change_parent(self):
1029
"""Ensure that after we change a parent, the results are still right.
1031
Renames and parent changes on pending transforms can happen as part
1032
of conflict resolution, and are explicitly permitted by the
1035
This test ensures they work correctly with the rename-avoidance
1038
transform, root = self.get_transform()
1039
parent1 = transform.new_directory('parent1', root)
1040
child1 = transform.new_file('child1', parent1, 'contents')
1041
parent2 = transform.new_directory('parent2', root)
1042
transform.adjust_path('child1', parent2, child1)
1044
self.failIfExists(self.wt.abspath('parent1/child1'))
1045
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1046
# rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1047
# no rename for child1 (counting only renames during apply)
1048
self.failUnlessEqual(2, transform.rename_count)
1050
def test_cancel_parent(self):
1051
"""Cancelling a parent doesn't cause deletion of a non-empty directory
1053
This is like the test_change_parent, except that we cancel the parent
1054
before adjusting the path. The transform must detect that the
1055
directory is non-empty, and move children to safe locations.
1057
transform, root = self.get_transform()
1058
parent1 = transform.new_directory('parent1', root)
1059
child1 = transform.new_file('child1', parent1, 'contents')
1060
child2 = transform.new_file('child2', parent1, 'contents')
1062
transform.cancel_creation(parent1)
1064
self.fail('Failed to move child1 before deleting parent1')
1065
transform.cancel_creation(child2)
1066
transform.create_directory(parent1)
1068
transform.cancel_creation(parent1)
1069
# If the transform incorrectly believes that child2 is still in
1070
# parent1's limbo directory, it will try to rename it and fail
1071
# because was already moved by the first cancel_creation.
1073
self.fail('Transform still thinks child2 is a child of parent1')
1074
parent2 = transform.new_directory('parent2', root)
1075
transform.adjust_path('child1', parent2, child1)
1077
self.failIfExists(self.wt.abspath('parent1'))
1078
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1079
# rename limbo/new-3 => parent2, rename limbo/new-2 => child1
1080
self.failUnlessEqual(2, transform.rename_count)
1082
def test_adjust_and_cancel(self):
1083
"""Make sure adjust_path keeps track of limbo children properly"""
1084
transform, root = self.get_transform()
1085
parent1 = transform.new_directory('parent1', root)
1086
child1 = transform.new_file('child1', parent1, 'contents')
1087
parent2 = transform.new_directory('parent2', root)
1088
transform.adjust_path('child1', parent2, child1)
1089
transform.cancel_creation(child1)
1091
transform.cancel_creation(parent1)
1092
# if the transform thinks child1 is still in parent1's limbo
1093
# directory, it will attempt to move it and fail.
1095
self.fail('Transform still thinks child1 is a child of parent1')
1096
transform.finalize()
1098
def test_noname_contents(self):
1099
"""TreeTransform should permit deferring naming files."""
1100
transform, root = self.get_transform()
1101
parent = transform.trans_id_file_id('parent-id')
1103
transform.create_directory(parent)
1105
self.fail("Can't handle contents with no name")
1106
transform.finalize()
1108
def test_noname_contents_nested(self):
1109
"""TreeTransform should permit deferring naming files."""
1110
transform, root = self.get_transform()
1111
parent = transform.trans_id_file_id('parent-id')
1113
transform.create_directory(parent)
1115
self.fail("Can't handle contents with no name")
1116
child = transform.new_directory('child', parent)
1117
transform.adjust_path('parent', root, parent)
1119
self.failUnlessExists(self.wt.abspath('parent/child'))
1120
self.assertEqual(1, transform.rename_count)
1122
def test_reuse_name(self):
1123
"""Avoid reusing the same limbo name for different files"""
1124
transform, root = self.get_transform()
1125
parent = transform.new_directory('parent', root)
1126
child1 = transform.new_directory('child', parent)
1128
child2 = transform.new_directory('child', parent)
1130
self.fail('Tranform tried to use the same limbo name twice')
1131
transform.adjust_path('child2', parent, child2)
1133
# limbo/new-1 => parent, limbo/new-3 => parent/child2
1134
# child2 is put into top-level limbo because child1 has already
1135
# claimed the direct limbo path when child2 is created. There is no
1136
# advantage in renaming files once they're in top-level limbo, except
1138
self.assertEqual(2, transform.rename_count)
1140
def test_reuse_when_first_moved(self):
1141
"""Don't avoid direct paths when it is safe to use them"""
1142
transform, root = self.get_transform()
1143
parent = transform.new_directory('parent', root)
1144
child1 = transform.new_directory('child', parent)
1145
transform.adjust_path('child1', parent, child1)
1146
child2 = transform.new_directory('child', parent)
1148
# limbo/new-1 => parent
1149
self.assertEqual(1, transform.rename_count)
1151
def test_reuse_after_cancel(self):
1152
"""Don't avoid direct paths when it is safe to use them"""
1153
transform, root = self.get_transform()
1154
parent2 = transform.new_directory('parent2', root)
1155
child1 = transform.new_directory('child1', parent2)
1156
transform.cancel_creation(parent2)
1157
transform.create_directory(parent2)
1158
child2 = transform.new_directory('child1', parent2)
1159
transform.adjust_path('child2', parent2, child1)
1161
# limbo/new-1 => parent2, limbo/new-2 => parent2/child1
1162
self.assertEqual(2, transform.rename_count)
1164
def test_finalize_order(self):
1165
"""Finalize must be done in child-to-parent order"""
1166
transform, root = self.get_transform()
1167
parent = transform.new_directory('parent', root)
1168
child = transform.new_directory('child', parent)
1170
transform.finalize()
1172
self.fail('Tried to remove parent before child1')
1174
def test_cancel_with_cancelled_child_should_succeed(self):
1175
transform, root = self.get_transform()
1176
parent = transform.new_directory('parent', root)
1177
child = transform.new_directory('child', parent)
1178
transform.cancel_creation(child)
1179
transform.cancel_creation(parent)
1180
transform.finalize()
1182
def test_rollback_on_directory_clash(self):
1184
wt = self.make_branch_and_tree('.')
1185
tt = TreeTransform(wt) # TreeTransform obtains write lock
1187
foo = tt.new_directory('foo', tt.root)
1188
tt.new_file('bar', foo, 'foobar')
1189
baz = tt.new_directory('baz', tt.root)
1190
tt.new_file('qux', baz, 'quux')
1191
# Ask for a rename 'foo' -> 'baz'
1192
tt.adjust_path('baz', tt.root, foo)
1193
# Lie to tt that we've already resolved all conflicts.
1194
tt.apply(no_conflicts=True)
1198
# The rename will fail because the target directory is not empty (but
1199
# raises FileExists anyway).
1200
err = self.assertRaises(errors.FileExists, tt_helper)
1201
self.assertContainsRe(str(err),
1202
"^File exists: .+/baz")
1204
def test_two_directories_clash(self):
1206
wt = self.make_branch_and_tree('.')
1207
tt = TreeTransform(wt) # TreeTransform obtains write lock
1209
foo_1 = tt.new_directory('foo', tt.root)
1210
tt.new_directory('bar', foo_1)
1211
# Adding the same directory with a different content
1212
foo_2 = tt.new_directory('foo', tt.root)
1213
tt.new_directory('baz', foo_2)
1214
# Lie to tt that we've already resolved all conflicts.
1215
tt.apply(no_conflicts=True)
1219
err = self.assertRaises(errors.FileExists, tt_helper)
1220
self.assertContainsRe(str(err),
1221
"^File exists: .+/foo")
1223
def test_two_directories_clash_finalize(self):
1225
wt = self.make_branch_and_tree('.')
1226
tt = TreeTransform(wt) # TreeTransform obtains write lock
1228
foo_1 = tt.new_directory('foo', tt.root)
1229
tt.new_directory('bar', foo_1)
1230
# Adding the same directory with a different content
1231
foo_2 = tt.new_directory('foo', tt.root)
1232
tt.new_directory('baz', foo_2)
1233
# Lie to tt that we've already resolved all conflicts.
1234
tt.apply(no_conflicts=True)
1238
err = self.assertRaises(errors.FileExists, tt_helper)
1239
self.assertContainsRe(str(err),
1240
"^File exists: .+/foo")
1242
def test_file_to_directory(self):
1243
wt = self.make_branch_and_tree('.')
1244
self.build_tree(['foo'])
1247
tt = TreeTransform(wt)
1248
self.addCleanup(tt.finalize)
1249
foo_trans_id = tt.trans_id_tree_path("foo")
1250
tt.delete_contents(foo_trans_id)
1251
tt.create_directory(foo_trans_id)
1252
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1253
tt.create_file(["aa\n"], bar_trans_id)
1254
tt.version_file("bar-1", bar_trans_id)
1256
self.failUnlessExists("foo/bar")
1259
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1264
changes = wt.changes_from(wt.basis_tree())
1265
self.assertFalse(changes.has_changed(), changes)
1267
def test_file_to_symlink(self):
1268
self.requireFeature(SymlinkFeature)
1269
wt = self.make_branch_and_tree('.')
1270
self.build_tree(['foo'])
1273
tt = TreeTransform(wt)
1274
self.addCleanup(tt.finalize)
1275
foo_trans_id = tt.trans_id_tree_path("foo")
1276
tt.delete_contents(foo_trans_id)
1277
tt.create_symlink("bar", foo_trans_id)
1279
self.failUnlessExists("foo")
1281
self.addCleanup(wt.unlock)
1282
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1285
def test_dir_to_file(self):
1286
wt = self.make_branch_and_tree('.')
1287
self.build_tree(['foo/', 'foo/bar'])
1288
wt.add(['foo', 'foo/bar'])
1290
tt = TreeTransform(wt)
1291
self.addCleanup(tt.finalize)
1292
foo_trans_id = tt.trans_id_tree_path("foo")
1293
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1294
tt.delete_contents(foo_trans_id)
1295
tt.delete_versioned(bar_trans_id)
1296
tt.create_file(["aa\n"], foo_trans_id)
1298
self.failUnlessExists("foo")
1300
self.addCleanup(wt.unlock)
1301
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1304
def test_dir_to_hardlink(self):
1305
self.requireFeature(HardlinkFeature)
1306
wt = self.make_branch_and_tree('.')
1307
self.build_tree(['foo/', 'foo/bar'])
1308
wt.add(['foo', 'foo/bar'])
1310
tt = TreeTransform(wt)
1311
self.addCleanup(tt.finalize)
1312
foo_trans_id = tt.trans_id_tree_path("foo")
1313
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1314
tt.delete_contents(foo_trans_id)
1315
tt.delete_versioned(bar_trans_id)
1316
self.build_tree(['baz'])
1317
tt.create_hardlink("baz", foo_trans_id)
1319
self.failUnlessExists("foo")
1320
self.failUnlessExists("baz")
1322
self.addCleanup(wt.unlock)
1323
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1326
def test_no_final_path(self):
1327
transform, root = self.get_transform()
1328
trans_id = transform.trans_id_file_id('foo')
1329
transform.create_file('bar', trans_id)
1330
transform.cancel_creation(trans_id)
1333
def test_create_from_tree(self):
1334
tree1 = self.make_branch_and_tree('tree1')
1335
self.build_tree_contents([('tree1/foo/',), ('tree1/bar', 'baz')])
1336
tree1.add(['foo', 'bar'], ['foo-id', 'bar-id'])
1337
tree2 = self.make_branch_and_tree('tree2')
1338
tt = TreeTransform(tree2)
1339
foo_trans_id = tt.create_path('foo', tt.root)
1340
create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1341
bar_trans_id = tt.create_path('bar', tt.root)
1342
create_from_tree(tt, bar_trans_id, tree1, 'bar-id')
1344
self.assertEqual('directory', osutils.file_kind('tree2/foo'))
1345
self.assertFileEqual('baz', 'tree2/bar')
1347
def test_create_from_tree_bytes(self):
1348
"""Provided lines are used instead of tree content."""
1349
tree1 = self.make_branch_and_tree('tree1')
1350
self.build_tree_contents([('tree1/foo', 'bar'),])
1351
tree1.add('foo', 'foo-id')
1352
tree2 = self.make_branch_and_tree('tree2')
1353
tt = TreeTransform(tree2)
1354
foo_trans_id = tt.create_path('foo', tt.root)
1355
create_from_tree(tt, foo_trans_id, tree1, 'foo-id', bytes='qux')
1357
self.assertFileEqual('qux', 'tree2/foo')
1359
def test_create_from_tree_symlink(self):
1360
self.requireFeature(SymlinkFeature)
1361
tree1 = self.make_branch_and_tree('tree1')
1362
os.symlink('bar', 'tree1/foo')
1363
tree1.add('foo', 'foo-id')
1364
tt = TreeTransform(self.make_branch_and_tree('tree2'))
1365
foo_trans_id = tt.create_path('foo', tt.root)
1366
create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1368
self.assertEqual('bar', os.readlink('tree2/foo'))
478
1371
class TransformGroup(object):
479
def __init__(self, dirname):
1373
def __init__(self, dirname, root_id):
480
1374
self.name = dirname
481
1375
os.mkdir(dirname)
482
1376
self.wt = BzrDir.create_standalone_workingtree(dirname)
1377
self.wt.set_root_id(root_id)
483
1378
self.b = self.wt.branch
484
1379
self.tt = TreeTransform(self.wt)
485
1380
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
487
1383
def conflict_text(tree, merge):
488
1384
template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
489
1385
return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
492
1388
class TestTransformMerge(TestCaseInTempDir):
493
1390
def test_text_merge(self):
494
base = TransformGroup("base")
1391
root_id = generate_ids.gen_root_id()
1392
base = TransformGroup("base", root_id)
495
1393
base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
496
1394
base.tt.new_file('b', base.root, 'b1', 'b')
497
1395
base.tt.new_file('c', base.root, 'c', 'c')
671
1584
a.add(['foo', 'foo/bar', 'foo/baz'])
672
1585
a.commit('initial commit')
673
1586
b = BzrDir.create_standalone_workingtree('b')
674
build_tree(a.basis_tree(), b)
1587
basis = a.basis_tree()
1589
self.addCleanup(basis.unlock)
1590
build_tree(basis, b)
675
1591
self.assertIs(os.path.isdir('b/foo'), True)
676
1592
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
677
1593
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1595
def test_build_with_references(self):
1596
tree = self.make_branch_and_tree('source',
1597
format='dirstate-with-subtree')
1598
subtree = self.make_branch_and_tree('source/subtree',
1599
format='dirstate-with-subtree')
1600
tree.add_reference(subtree)
1601
tree.commit('a revision')
1602
tree.branch.create_checkout('target')
1603
self.failUnlessExists('target')
1604
self.failUnlessExists('target/subtree')
1606
def test_file_conflict_handling(self):
1607
"""Ensure that when building trees, conflict handling is done"""
1608
source = self.make_branch_and_tree('source')
1609
target = self.make_branch_and_tree('target')
1610
self.build_tree(['source/file', 'target/file'])
1611
source.add('file', 'new-file')
1612
source.commit('added file')
1613
build_tree(source.basis_tree(), target)
1614
self.assertEqual([DuplicateEntry('Moved existing file to',
1615
'file.moved', 'file', None, 'new-file')],
1617
target2 = self.make_branch_and_tree('target2')
1618
target_file = file('target2/file', 'wb')
1620
source_file = file('source/file', 'rb')
1622
target_file.write(source_file.read())
1627
build_tree(source.basis_tree(), target2)
1628
self.assertEqual([], target2.conflicts())
1630
def test_symlink_conflict_handling(self):
1631
"""Ensure that when building trees, conflict handling is done"""
1632
self.requireFeature(SymlinkFeature)
1633
source = self.make_branch_and_tree('source')
1634
os.symlink('foo', 'source/symlink')
1635
source.add('symlink', 'new-symlink')
1636
source.commit('added file')
1637
target = self.make_branch_and_tree('target')
1638
os.symlink('bar', 'target/symlink')
1639
build_tree(source.basis_tree(), target)
1640
self.assertEqual([DuplicateEntry('Moved existing file to',
1641
'symlink.moved', 'symlink', None, 'new-symlink')],
1643
target = self.make_branch_and_tree('target2')
1644
os.symlink('foo', 'target2/symlink')
1645
build_tree(source.basis_tree(), target)
1646
self.assertEqual([], target.conflicts())
1648
def test_directory_conflict_handling(self):
1649
"""Ensure that when building trees, conflict handling is done"""
1650
source = self.make_branch_and_tree('source')
1651
target = self.make_branch_and_tree('target')
1652
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1653
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1654
source.commit('added file')
1655
build_tree(source.basis_tree(), target)
1656
self.assertEqual([], target.conflicts())
1657
self.failUnlessExists('target/dir1/file')
1659
# Ensure contents are merged
1660
target = self.make_branch_and_tree('target2')
1661
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1662
build_tree(source.basis_tree(), target)
1663
self.assertEqual([], target.conflicts())
1664
self.failUnlessExists('target2/dir1/file2')
1665
self.failUnlessExists('target2/dir1/file')
1667
# Ensure new contents are suppressed for existing branches
1668
target = self.make_branch_and_tree('target3')
1669
self.make_branch('target3/dir1')
1670
self.build_tree(['target3/dir1/file2'])
1671
build_tree(source.basis_tree(), target)
1672
self.failIfExists('target3/dir1/file')
1673
self.failUnlessExists('target3/dir1/file2')
1674
self.failUnlessExists('target3/dir1.diverted/file')
1675
self.assertEqual([DuplicateEntry('Diverted to',
1676
'dir1.diverted', 'dir1', 'new-dir1', None)],
1679
target = self.make_branch_and_tree('target4')
1680
self.build_tree(['target4/dir1/'])
1681
self.make_branch('target4/dir1/file')
1682
build_tree(source.basis_tree(), target)
1683
self.failUnlessExists('target4/dir1/file')
1684
self.assertEqual('directory', file_kind('target4/dir1/file'))
1685
self.failUnlessExists('target4/dir1/file.diverted')
1686
self.assertEqual([DuplicateEntry('Diverted to',
1687
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1690
def test_mixed_conflict_handling(self):
1691
"""Ensure that when building trees, conflict handling is done"""
1692
source = self.make_branch_and_tree('source')
1693
target = self.make_branch_and_tree('target')
1694
self.build_tree(['source/name', 'target/name/'])
1695
source.add('name', 'new-name')
1696
source.commit('added file')
1697
build_tree(source.basis_tree(), target)
1698
self.assertEqual([DuplicateEntry('Moved existing file to',
1699
'name.moved', 'name', None, 'new-name')], target.conflicts())
1701
def test_raises_in_populated(self):
1702
source = self.make_branch_and_tree('source')
1703
self.build_tree(['source/name'])
1705
source.commit('added name')
1706
target = self.make_branch_and_tree('target')
1707
self.build_tree(['target/name'])
1709
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1710
build_tree, source.basis_tree(), target)
1712
def test_build_tree_rename_count(self):
1713
source = self.make_branch_and_tree('source')
1714
self.build_tree(['source/file1', 'source/dir1/'])
1715
source.add(['file1', 'dir1'])
1716
source.commit('add1')
1717
target1 = self.make_branch_and_tree('target1')
1718
transform_result = build_tree(source.basis_tree(), target1)
1719
self.assertEqual(2, transform_result.rename_count)
1721
self.build_tree(['source/dir1/file2'])
1722
source.add(['dir1/file2'])
1723
source.commit('add3')
1724
target2 = self.make_branch_and_tree('target2')
1725
transform_result = build_tree(source.basis_tree(), target2)
1726
# children of non-root directories should not be renamed
1727
self.assertEqual(2, transform_result.rename_count)
1729
def create_ab_tree(self):
1730
"""Create a committed test tree with two files"""
1731
source = self.make_branch_and_tree('source')
1732
self.build_tree_contents([('source/file1', 'A')])
1733
self.build_tree_contents([('source/file2', 'B')])
1734
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1735
source.commit('commit files')
1737
self.addCleanup(source.unlock)
1740
def test_build_tree_accelerator_tree(self):
1741
source = self.create_ab_tree()
1742
self.build_tree_contents([('source/file2', 'C')])
1744
real_source_get_file = source.get_file
1745
def get_file(file_id, path=None):
1746
calls.append(file_id)
1747
return real_source_get_file(file_id, path)
1748
source.get_file = get_file
1749
target = self.make_branch_and_tree('target')
1750
revision_tree = source.basis_tree()
1751
revision_tree.lock_read()
1752
self.addCleanup(revision_tree.unlock)
1753
build_tree(revision_tree, target, source)
1754
self.assertEqual(['file1-id'], calls)
1756
self.addCleanup(target.unlock)
1757
self.assertEqual([], list(target.iter_changes(revision_tree)))
1759
def test_build_tree_accelerator_tree_missing_file(self):
1760
source = self.create_ab_tree()
1761
os.unlink('source/file1')
1762
source.remove(['file2'])
1763
target = self.make_branch_and_tree('target')
1764
revision_tree = source.basis_tree()
1765
revision_tree.lock_read()
1766
self.addCleanup(revision_tree.unlock)
1767
build_tree(revision_tree, target, source)
1769
self.addCleanup(target.unlock)
1770
self.assertEqual([], list(target.iter_changes(revision_tree)))
1772
def test_build_tree_accelerator_wrong_kind(self):
1773
self.requireFeature(SymlinkFeature)
1774
source = self.make_branch_and_tree('source')
1775
self.build_tree_contents([('source/file1', '')])
1776
self.build_tree_contents([('source/file2', '')])
1777
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1778
source.commit('commit files')
1779
os.unlink('source/file2')
1780
self.build_tree_contents([('source/file2/', 'C')])
1781
os.unlink('source/file1')
1782
os.symlink('file2', 'source/file1')
1784
real_source_get_file = source.get_file
1785
def get_file(file_id, path=None):
1786
calls.append(file_id)
1787
return real_source_get_file(file_id, path)
1788
source.get_file = get_file
1789
target = self.make_branch_and_tree('target')
1790
revision_tree = source.basis_tree()
1791
revision_tree.lock_read()
1792
self.addCleanup(revision_tree.unlock)
1793
build_tree(revision_tree, target, source)
1794
self.assertEqual([], calls)
1796
self.addCleanup(target.unlock)
1797
self.assertEqual([], list(target.iter_changes(revision_tree)))
1799
def test_build_tree_hardlink(self):
1800
self.requireFeature(HardlinkFeature)
1801
source = self.create_ab_tree()
1802
target = self.make_branch_and_tree('target')
1803
revision_tree = source.basis_tree()
1804
revision_tree.lock_read()
1805
self.addCleanup(revision_tree.unlock)
1806
build_tree(revision_tree, target, source, hardlink=True)
1808
self.addCleanup(target.unlock)
1809
self.assertEqual([], list(target.iter_changes(revision_tree)))
1810
source_stat = os.stat('source/file1')
1811
target_stat = os.stat('target/file1')
1812
self.assertEqual(source_stat, target_stat)
1814
# Explicitly disallowing hardlinks should prevent them.
1815
target2 = self.make_branch_and_tree('target2')
1816
build_tree(revision_tree, target2, source, hardlink=False)
1818
self.addCleanup(target2.unlock)
1819
self.assertEqual([], list(target2.iter_changes(revision_tree)))
1820
source_stat = os.stat('source/file1')
1821
target2_stat = os.stat('target2/file1')
1822
self.assertNotEqual(source_stat, target2_stat)
1824
def test_build_tree_accelerator_tree_moved(self):
1825
source = self.make_branch_and_tree('source')
1826
self.build_tree_contents([('source/file1', 'A')])
1827
source.add(['file1'], ['file1-id'])
1828
source.commit('commit files')
1829
source.rename_one('file1', 'file2')
1831
self.addCleanup(source.unlock)
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)
1838
self.addCleanup(target.unlock)
1839
self.assertEqual([], list(target.iter_changes(revision_tree)))
1841
def test_build_tree_hardlinks_preserve_execute(self):
1842
self.requireFeature(HardlinkFeature)
1843
source = self.create_ab_tree()
1844
tt = TreeTransform(source)
1845
trans_id = tt.trans_id_tree_file_id('file1-id')
1846
tt.set_executability(True, trans_id)
1848
self.assertTrue(source.is_executable('file1-id'))
1849
target = self.make_branch_and_tree('target')
1850
revision_tree = source.basis_tree()
1851
revision_tree.lock_read()
1852
self.addCleanup(revision_tree.unlock)
1853
build_tree(revision_tree, target, source, hardlink=True)
1855
self.addCleanup(target.unlock)
1856
self.assertEqual([], list(target.iter_changes(revision_tree)))
1857
self.assertTrue(source.is_executable('file1-id'))
1859
def test_case_insensitive_build_tree_inventory(self):
1860
if (tests.CaseInsensitiveFilesystemFeature.available()
1861
or tests.CaseInsCasePresFilenameFeature.available()):
1862
raise tests.UnavailableFeature('Fully case sensitive filesystem')
1863
source = self.make_branch_and_tree('source')
1864
self.build_tree(['source/file', 'source/FILE'])
1865
source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1866
source.commit('added files')
1867
# Don't try this at home, kids!
1868
# Force the tree to report that it is case insensitive
1869
target = self.make_branch_and_tree('target')
1870
target.case_sensitive = False
1871
build_tree(source.basis_tree(), target, source, delta_from_tree=True)
1872
self.assertEqual('file.moved', target.id2path('lower-id'))
1873
self.assertEqual('FILE', target.id2path('upper-id'))
1876
class TestCommitTransform(tests.TestCaseWithTransport):
1878
def get_branch(self):
1879
tree = self.make_branch_and_tree('tree')
1881
self.addCleanup(tree.unlock)
1882
tree.commit('empty commit')
1885
def get_branch_and_transform(self):
1886
branch = self.get_branch()
1887
tt = TransformPreview(branch.basis_tree())
1888
self.addCleanup(tt.finalize)
1891
def test_commit_wrong_basis(self):
1892
branch = self.get_branch()
1893
basis = branch.repository.revision_tree(
1894
_mod_revision.NULL_REVISION)
1895
tt = TransformPreview(basis)
1896
self.addCleanup(tt.finalize)
1897
e = self.assertRaises(ValueError, tt.commit, branch, '')
1898
self.assertEqual('TreeTransform not based on branch basis: null:',
1901
def test_empy_commit(self):
1902
branch, tt = self.get_branch_and_transform()
1903
rev = tt.commit(branch, 'my message')
1904
self.assertEqual(2, branch.revno())
1905
repo = branch.repository
1906
self.assertEqual('my message', repo.get_revision(rev).message)
1908
def test_merge_parents(self):
1909
branch, tt = self.get_branch_and_transform()
1910
rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
1911
self.assertEqual(['rev1b', 'rev1c'],
1912
branch.basis_tree().get_parent_ids()[1:])
1914
def test_first_commit(self):
1915
branch = self.make_branch('branch')
1917
self.addCleanup(branch.unlock)
1918
tt = TransformPreview(branch.basis_tree())
1919
self.addCleanup(tt.finalize)
1920
tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
1921
rev = tt.commit(branch, 'my message')
1922
self.assertEqual([], branch.basis_tree().get_parent_ids())
1923
self.assertNotEqual(_mod_revision.NULL_REVISION,
1924
branch.last_revision())
1926
def test_first_commit_with_merge_parents(self):
1927
branch = self.make_branch('branch')
1929
self.addCleanup(branch.unlock)
1930
tt = TransformPreview(branch.basis_tree())
1931
self.addCleanup(tt.finalize)
1932
e = self.assertRaises(ValueError, tt.commit, branch,
1933
'my message', ['rev1b-id'])
1934
self.assertEqual('Cannot supply merge parents for first commit.',
1936
self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
1938
def test_add_files(self):
1939
branch, tt = self.get_branch_and_transform()
1940
tt.new_file('file', tt.root, 'contents', 'file-id')
1941
trans_id = tt.new_directory('dir', tt.root, 'dir-id')
1942
tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
1943
rev = tt.commit(branch, 'message')
1944
tree = branch.basis_tree()
1945
self.assertEqual('file', tree.id2path('file-id'))
1946
self.assertEqual('contents', tree.get_file_text('file-id'))
1947
self.assertEqual('dir', tree.id2path('dir-id'))
1948
self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
1949
self.assertEqual('target', tree.get_symlink_target('symlink-id'))
1951
def test_add_unversioned(self):
1952
branch, tt = self.get_branch_and_transform()
1953
tt.new_file('file', tt.root, 'contents')
1954
self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
1955
'message', strict=True)
1957
def test_modify_strict(self):
1958
branch, tt = self.get_branch_and_transform()
1959
tt.new_file('file', tt.root, 'contents', 'file-id')
1960
tt.commit(branch, 'message', strict=True)
1961
tt = TransformPreview(branch.basis_tree())
1962
self.addCleanup(tt.finalize)
1963
trans_id = tt.trans_id_file_id('file-id')
1964
tt.delete_contents(trans_id)
1965
tt.create_file('contents', trans_id)
1966
tt.commit(branch, 'message', strict=True)
1968
def test_commit_malformed(self):
1969
"""Committing a malformed transform should raise an exception.
1971
In this case, we are adding a file without adding its parent.
1973
branch, tt = self.get_branch_and_transform()
1974
parent_id = tt.trans_id_file_id('parent-id')
1975
tt.new_file('file', parent_id, 'contents', 'file-id')
1976
self.assertRaises(errors.MalformedTransform, tt.commit, branch,
1980
class MockTransform(object):
1982
def has_named_child(self, by_parent, parent_id, name):
1983
for child_id in by_parent[parent_id]:
1987
elif name == "name.~%s~" % child_id:
1992
class MockEntry(object):
1994
object.__init__(self)
1998
class TestGetBackupName(TestCase):
1999
def test_get_backup_name(self):
2000
tt = MockTransform()
2001
name = get_backup_name(MockEntry(), {'a':[]}, 'a', tt)
2002
self.assertEqual(name, 'name.~1~')
2003
name = get_backup_name(MockEntry(), {'a':['1']}, 'a', tt)
2004
self.assertEqual(name, 'name.~2~')
2005
name = get_backup_name(MockEntry(), {'a':['2']}, 'a', tt)
2006
self.assertEqual(name, 'name.~1~')
2007
name = get_backup_name(MockEntry(), {'a':['2'], 'b':[]}, 'b', tt)
2008
self.assertEqual(name, 'name.~1~')
2009
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
2010
self.assertEqual(name, 'name.~4~')
2013
class TestFileMover(tests.TestCaseWithTransport):
2015
def test_file_mover(self):
2016
self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
2017
mover = _FileMover()
2018
mover.rename('a', 'q')
2019
self.failUnlessExists('q')
2020
self.failIfExists('a')
2021
self.failUnlessExists('q/b')
2022
self.failUnlessExists('c')
2023
self.failUnlessExists('c/d')
2025
def test_pre_delete_rollback(self):
2026
self.build_tree(['a/'])
2027
mover = _FileMover()
2028
mover.pre_delete('a', 'q')
2029
self.failUnlessExists('q')
2030
self.failIfExists('a')
2032
self.failIfExists('q')
2033
self.failUnlessExists('a')
2035
def test_apply_deletions(self):
2036
self.build_tree(['a/', 'b/'])
2037
mover = _FileMover()
2038
mover.pre_delete('a', 'q')
2039
mover.pre_delete('b', 'r')
2040
self.failUnlessExists('q')
2041
self.failUnlessExists('r')
2042
self.failIfExists('a')
2043
self.failIfExists('b')
2044
mover.apply_deletions()
2045
self.failIfExists('q')
2046
self.failIfExists('r')
2047
self.failIfExists('a')
2048
self.failIfExists('b')
2050
def test_file_mover_rollback(self):
2051
self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
2052
mover = _FileMover()
2053
mover.rename('c/d', 'c/f')
2054
mover.rename('c/e', 'c/d')
2056
mover.rename('a', 'c')
2057
except errors.FileExists, e:
2059
self.failUnlessExists('a')
2060
self.failUnlessExists('c/d')
2063
class Bogus(Exception):
2067
class TestTransformRollback(tests.TestCaseWithTransport):
2069
class ExceptionFileMover(_FileMover):
2071
def __init__(self, bad_source=None, bad_target=None):
2072
_FileMover.__init__(self)
2073
self.bad_source = bad_source
2074
self.bad_target = bad_target
2076
def rename(self, source, target):
2077
if (self.bad_source is not None and
2078
source.endswith(self.bad_source)):
2080
elif (self.bad_target is not None and
2081
target.endswith(self.bad_target)):
2084
_FileMover.rename(self, source, target)
2086
def test_rollback_rename(self):
2087
tree = self.make_branch_and_tree('.')
2088
self.build_tree(['a/', 'a/b'])
2089
tt = TreeTransform(tree)
2090
self.addCleanup(tt.finalize)
2091
a_id = tt.trans_id_tree_path('a')
2092
tt.adjust_path('c', tt.root, a_id)
2093
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2094
self.assertRaises(Bogus, tt.apply,
2095
_mover=self.ExceptionFileMover(bad_source='a'))
2096
self.failUnlessExists('a')
2097
self.failUnlessExists('a/b')
2099
self.failUnlessExists('c')
2100
self.failUnlessExists('c/d')
2102
def test_rollback_rename_into_place(self):
2103
tree = self.make_branch_and_tree('.')
2104
self.build_tree(['a/', 'a/b'])
2105
tt = TreeTransform(tree)
2106
self.addCleanup(tt.finalize)
2107
a_id = tt.trans_id_tree_path('a')
2108
tt.adjust_path('c', tt.root, a_id)
2109
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2110
self.assertRaises(Bogus, tt.apply,
2111
_mover=self.ExceptionFileMover(bad_target='c/d'))
2112
self.failUnlessExists('a')
2113
self.failUnlessExists('a/b')
2115
self.failUnlessExists('c')
2116
self.failUnlessExists('c/d')
2118
def test_rollback_deletion(self):
2119
tree = self.make_branch_and_tree('.')
2120
self.build_tree(['a/', 'a/b'])
2121
tt = TreeTransform(tree)
2122
self.addCleanup(tt.finalize)
2123
a_id = tt.trans_id_tree_path('a')
2124
tt.delete_contents(a_id)
2125
tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
2126
self.assertRaises(Bogus, tt.apply,
2127
_mover=self.ExceptionFileMover(bad_target='d'))
2128
self.failUnlessExists('a')
2129
self.failUnlessExists('a/b')
2131
def test_resolve_no_parent(self):
2132
wt = self.make_branch_and_tree('.')
2133
tt = TreeTransform(wt)
2134
self.addCleanup(tt.finalize)
2135
parent = tt.trans_id_file_id('parent-id')
2136
tt.new_file('file', parent, 'Contents')
2137
resolve_conflicts(tt)
2140
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
2141
('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
2143
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
2144
('', ''), ('directory', 'directory'), (False, None))
2147
class TestTransformPreview(tests.TestCaseWithTransport):
2149
def create_tree(self):
2150
tree = self.make_branch_and_tree('.')
2151
self.build_tree_contents([('a', 'content 1')])
2152
tree.set_root_id('TREE_ROOT')
2153
tree.add('a', 'a-id')
2154
tree.commit('rev1', rev_id='rev1')
2155
return tree.branch.repository.revision_tree('rev1')
2157
def get_empty_preview(self):
2158
repository = self.make_repository('repo')
2159
tree = repository.revision_tree(_mod_revision.NULL_REVISION)
2160
preview = TransformPreview(tree)
2161
self.addCleanup(preview.finalize)
2164
def test_transform_preview(self):
2165
revision_tree = self.create_tree()
2166
preview = TransformPreview(revision_tree)
2167
self.addCleanup(preview.finalize)
2169
def test_transform_preview_tree(self):
2170
revision_tree = self.create_tree()
2171
preview = TransformPreview(revision_tree)
2172
self.addCleanup(preview.finalize)
2173
preview.get_preview_tree()
2175
def test_transform_new_file(self):
2176
revision_tree = self.create_tree()
2177
preview = TransformPreview(revision_tree)
2178
self.addCleanup(preview.finalize)
2179
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2180
preview_tree = preview.get_preview_tree()
2181
self.assertEqual(preview_tree.kind('file2-id'), 'file')
2183
preview_tree.get_file('file2-id').read(), 'content B\n')
2185
def test_diff_preview_tree(self):
2186
revision_tree = self.create_tree()
2187
preview = TransformPreview(revision_tree)
2188
self.addCleanup(preview.finalize)
2189
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2190
preview_tree = preview.get_preview_tree()
2192
show_diff_trees(revision_tree, preview_tree, out)
2193
lines = out.getvalue().splitlines()
2194
self.assertEqual(lines[0], "=== added file 'file2'")
2195
# 3 lines of diff administrivia
2196
self.assertEqual(lines[4], "+content B")
2198
def test_transform_conflicts(self):
2199
revision_tree = self.create_tree()
2200
preview = TransformPreview(revision_tree)
2201
self.addCleanup(preview.finalize)
2202
preview.new_file('a', preview.root, 'content 2')
2203
resolve_conflicts(preview)
2204
trans_id = preview.trans_id_file_id('a-id')
2205
self.assertEqual('a.moved', preview.final_name(trans_id))
2207
def get_tree_and_preview_tree(self):
2208
revision_tree = self.create_tree()
2209
preview = TransformPreview(revision_tree)
2210
self.addCleanup(preview.finalize)
2211
a_trans_id = preview.trans_id_file_id('a-id')
2212
preview.delete_contents(a_trans_id)
2213
preview.create_file('b content', a_trans_id)
2214
preview_tree = preview.get_preview_tree()
2215
return revision_tree, preview_tree
2217
def test_iter_changes(self):
2218
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2219
root = revision_tree.inventory.root.file_id
2220
self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
2221
(root, root), ('a', 'a'), ('file', 'file'),
2223
list(preview_tree.iter_changes(revision_tree)))
2225
def test_include_unchanged_succeeds(self):
2226
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2227
changes = preview_tree.iter_changes(revision_tree,
2228
include_unchanged=True)
2229
root = revision_tree.inventory.root.file_id
2231
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2233
def test_specific_files(self):
2234
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2235
changes = preview_tree.iter_changes(revision_tree,
2236
specific_files=[''])
2237
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2239
def test_want_unversioned(self):
2240
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2241
changes = preview_tree.iter_changes(revision_tree,
2242
want_unversioned=True)
2243
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2245
def test_ignore_extra_trees_no_specific_files(self):
2246
# extra_trees is harmless without specific_files, so we'll silently
2247
# accept it, even though we won't use it.
2248
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2249
preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
2251
def test_ignore_require_versioned_no_specific_files(self):
2252
# require_versioned is meaningless without specific_files.
2253
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2254
preview_tree.iter_changes(revision_tree, require_versioned=False)
2256
def test_ignore_pb(self):
2257
# pb could be supported, but TT.iter_changes doesn't support it.
2258
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2259
preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
2261
def test_kind(self):
2262
revision_tree = self.create_tree()
2263
preview = TransformPreview(revision_tree)
2264
self.addCleanup(preview.finalize)
2265
preview.new_file('file', preview.root, 'contents', 'file-id')
2266
preview.new_directory('directory', preview.root, 'dir-id')
2267
preview_tree = preview.get_preview_tree()
2268
self.assertEqual('file', preview_tree.kind('file-id'))
2269
self.assertEqual('directory', preview_tree.kind('dir-id'))
2271
def test_get_file_mtime(self):
2272
preview = self.get_empty_preview()
2273
file_trans_id = preview.new_file('file', preview.root, 'contents',
2275
limbo_path = preview._limbo_name(file_trans_id)
2276
preview_tree = preview.get_preview_tree()
2277
self.assertEqual(os.stat(limbo_path).st_mtime,
2278
preview_tree.get_file_mtime('file-id'))
2280
def test_get_file(self):
2281
preview = self.get_empty_preview()
2282
preview.new_file('file', preview.root, 'contents', 'file-id')
2283
preview_tree = preview.get_preview_tree()
2284
tree_file = preview_tree.get_file('file-id')
2286
self.assertEqual('contents', tree_file.read())
2290
def test_get_symlink_target(self):
2291
self.requireFeature(SymlinkFeature)
2292
preview = self.get_empty_preview()
2293
preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
2294
preview_tree = preview.get_preview_tree()
2295
self.assertEqual('target',
2296
preview_tree.get_symlink_target('symlink-id'))
2298
def test_all_file_ids(self):
2299
tree = self.make_branch_and_tree('tree')
2300
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
2301
tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
2302
preview = TransformPreview(tree)
2303
self.addCleanup(preview.finalize)
2304
preview.unversion_file(preview.trans_id_file_id('b-id'))
2305
c_trans_id = preview.trans_id_file_id('c-id')
2306
preview.unversion_file(c_trans_id)
2307
preview.version_file('c-id', c_trans_id)
2308
preview_tree = preview.get_preview_tree()
2309
self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
2310
preview_tree.all_file_ids())
2312
def test_path2id_deleted_unchanged(self):
2313
tree = self.make_branch_and_tree('tree')
2314
self.build_tree(['tree/unchanged', 'tree/deleted'])
2315
tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
2316
preview = TransformPreview(tree)
2317
self.addCleanup(preview.finalize)
2318
preview.unversion_file(preview.trans_id_file_id('deleted-id'))
2319
preview_tree = preview.get_preview_tree()
2320
self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
2321
self.assertIs(None, preview_tree.path2id('deleted'))
2323
def test_path2id_created(self):
2324
tree = self.make_branch_and_tree('tree')
2325
self.build_tree(['tree/unchanged'])
2326
tree.add(['unchanged'], ['unchanged-id'])
2327
preview = TransformPreview(tree)
2328
self.addCleanup(preview.finalize)
2329
preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
2330
'contents', 'new-id')
2331
preview_tree = preview.get_preview_tree()
2332
self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
2334
def test_path2id_moved(self):
2335
tree = self.make_branch_and_tree('tree')
2336
self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
2337
tree.add(['old_parent', 'old_parent/child'],
2338
['old_parent-id', 'child-id'])
2339
preview = TransformPreview(tree)
2340
self.addCleanup(preview.finalize)
2341
new_parent = preview.new_directory('new_parent', preview.root,
2343
preview.adjust_path('child', new_parent,
2344
preview.trans_id_file_id('child-id'))
2345
preview_tree = preview.get_preview_tree()
2346
self.assertIs(None, preview_tree.path2id('old_parent/child'))
2347
self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
2349
def test_path2id_renamed_parent(self):
2350
tree = self.make_branch_and_tree('tree')
2351
self.build_tree(['tree/old_name/', 'tree/old_name/child'])
2352
tree.add(['old_name', 'old_name/child'],
2353
['parent-id', 'child-id'])
2354
preview = TransformPreview(tree)
2355
self.addCleanup(preview.finalize)
2356
preview.adjust_path('new_name', preview.root,
2357
preview.trans_id_file_id('parent-id'))
2358
preview_tree = preview.get_preview_tree()
2359
self.assertIs(None, preview_tree.path2id('old_name/child'))
2360
self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
2362
def assertMatchingIterEntries(self, tt, specific_file_ids=None):
2363
preview_tree = tt.get_preview_tree()
2364
preview_result = list(preview_tree.iter_entries_by_dir(
2368
actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
2369
self.assertEqual(actual_result, preview_result)
2371
def test_iter_entries_by_dir_new(self):
2372
tree = self.make_branch_and_tree('tree')
2373
tt = TreeTransform(tree)
2374
tt.new_file('new', tt.root, 'contents', 'new-id')
2375
self.assertMatchingIterEntries(tt)
2377
def test_iter_entries_by_dir_deleted(self):
2378
tree = self.make_branch_and_tree('tree')
2379
self.build_tree(['tree/deleted'])
2380
tree.add('deleted', 'deleted-id')
2381
tt = TreeTransform(tree)
2382
tt.delete_contents(tt.trans_id_file_id('deleted-id'))
2383
self.assertMatchingIterEntries(tt)
2385
def test_iter_entries_by_dir_unversioned(self):
2386
tree = self.make_branch_and_tree('tree')
2387
self.build_tree(['tree/removed'])
2388
tree.add('removed', 'removed-id')
2389
tt = TreeTransform(tree)
2390
tt.unversion_file(tt.trans_id_file_id('removed-id'))
2391
self.assertMatchingIterEntries(tt)
2393
def test_iter_entries_by_dir_moved(self):
2394
tree = self.make_branch_and_tree('tree')
2395
self.build_tree(['tree/moved', 'tree/new_parent/'])
2396
tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
2397
tt = TreeTransform(tree)
2398
tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
2399
tt.trans_id_file_id('moved-id'))
2400
self.assertMatchingIterEntries(tt)
2402
def test_iter_entries_by_dir_specific_file_ids(self):
2403
tree = self.make_branch_and_tree('tree')
2404
tree.set_root_id('tree-root-id')
2405
self.build_tree(['tree/parent/', 'tree/parent/child'])
2406
tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
2407
tt = TreeTransform(tree)
2408
self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
2410
def test_symlink_content_summary(self):
2411
self.requireFeature(SymlinkFeature)
2412
preview = self.get_empty_preview()
2413
preview.new_symlink('path', preview.root, 'target', 'path-id')
2414
summary = preview.get_preview_tree().path_content_summary('path')
2415
self.assertEqual(('symlink', None, None, 'target'), summary)
2417
def test_missing_content_summary(self):
2418
preview = self.get_empty_preview()
2419
summary = preview.get_preview_tree().path_content_summary('path')
2420
self.assertEqual(('missing', None, None, None), summary)
2422
def test_deleted_content_summary(self):
2423
tree = self.make_branch_and_tree('tree')
2424
self.build_tree(['tree/path/'])
2426
preview = TransformPreview(tree)
2427
self.addCleanup(preview.finalize)
2428
preview.delete_contents(preview.trans_id_tree_path('path'))
2429
summary = preview.get_preview_tree().path_content_summary('path')
2430
self.assertEqual(('missing', None, None, None), summary)
2432
def test_file_content_summary_executable(self):
2433
if not osutils.supports_executable():
2434
raise TestNotApplicable()
2435
preview = self.get_empty_preview()
2436
path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2437
preview.set_executability(True, path_id)
2438
summary = preview.get_preview_tree().path_content_summary('path')
2439
self.assertEqual(4, len(summary))
2440
self.assertEqual('file', summary[0])
2441
# size must be known
2442
self.assertEqual(len('contents'), summary[1])
2444
self.assertEqual(True, summary[2])
2445
# will not have hash (not cheap to determine)
2446
self.assertIs(None, summary[3])
2448
def test_change_executability(self):
2449
if not osutils.supports_executable():
2450
raise TestNotApplicable()
2451
tree = self.make_branch_and_tree('tree')
2452
self.build_tree(['tree/path'])
2454
preview = TransformPreview(tree)
2455
self.addCleanup(preview.finalize)
2456
path_id = preview.trans_id_tree_path('path')
2457
preview.set_executability(True, path_id)
2458
summary = preview.get_preview_tree().path_content_summary('path')
2459
self.assertEqual(True, summary[2])
2461
def test_file_content_summary_non_exec(self):
2462
preview = self.get_empty_preview()
2463
preview.new_file('path', preview.root, 'contents', 'path-id')
2464
summary = preview.get_preview_tree().path_content_summary('path')
2465
self.assertEqual(4, len(summary))
2466
self.assertEqual('file', summary[0])
2467
# size must be known
2468
self.assertEqual(len('contents'), summary[1])
2470
if osutils.supports_executable():
2471
self.assertEqual(False, summary[2])
2473
self.assertEqual(None, summary[2])
2474
# will not have hash (not cheap to determine)
2475
self.assertIs(None, summary[3])
2477
def test_dir_content_summary(self):
2478
preview = self.get_empty_preview()
2479
preview.new_directory('path', preview.root, 'path-id')
2480
summary = preview.get_preview_tree().path_content_summary('path')
2481
self.assertEqual(('directory', None, None, None), summary)
2483
def test_tree_content_summary(self):
2484
preview = self.get_empty_preview()
2485
path = preview.new_directory('path', preview.root, 'path-id')
2486
preview.set_tree_reference('rev-1', path)
2487
summary = preview.get_preview_tree().path_content_summary('path')
2488
self.assertEqual(4, len(summary))
2489
self.assertEqual('tree-reference', summary[0])
2491
def test_annotate(self):
2492
tree = self.make_branch_and_tree('tree')
2493
self.build_tree_contents([('tree/file', 'a\n')])
2494
tree.add('file', 'file-id')
2495
tree.commit('a', rev_id='one')
2496
self.build_tree_contents([('tree/file', 'a\nb\n')])
2497
preview = TransformPreview(tree)
2498
self.addCleanup(preview.finalize)
2499
file_trans_id = preview.trans_id_file_id('file-id')
2500
preview.delete_contents(file_trans_id)
2501
preview.create_file('a\nb\nc\n', file_trans_id)
2502
preview_tree = preview.get_preview_tree()
2508
annotation = preview_tree.annotate_iter('file-id', 'me:')
2509
self.assertEqual(expected, annotation)
2511
def test_annotate_missing(self):
2512
preview = self.get_empty_preview()
2513
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2514
preview_tree = preview.get_preview_tree()
2520
annotation = preview_tree.annotate_iter('file-id', 'me:')
2521
self.assertEqual(expected, annotation)
2523
def test_annotate_rename(self):
2524
tree = self.make_branch_and_tree('tree')
2525
self.build_tree_contents([('tree/file', 'a\n')])
2526
tree.add('file', 'file-id')
2527
tree.commit('a', rev_id='one')
2528
preview = TransformPreview(tree)
2529
self.addCleanup(preview.finalize)
2530
file_trans_id = preview.trans_id_file_id('file-id')
2531
preview.adjust_path('newname', preview.root, file_trans_id)
2532
preview_tree = preview.get_preview_tree()
2536
annotation = preview_tree.annotate_iter('file-id', 'me:')
2537
self.assertEqual(expected, annotation)
2539
def test_annotate_deleted(self):
2540
tree = self.make_branch_and_tree('tree')
2541
self.build_tree_contents([('tree/file', 'a\n')])
2542
tree.add('file', 'file-id')
2543
tree.commit('a', rev_id='one')
2544
self.build_tree_contents([('tree/file', 'a\nb\n')])
2545
preview = TransformPreview(tree)
2546
self.addCleanup(preview.finalize)
2547
file_trans_id = preview.trans_id_file_id('file-id')
2548
preview.delete_contents(file_trans_id)
2549
preview_tree = preview.get_preview_tree()
2550
annotation = preview_tree.annotate_iter('file-id', 'me:')
2551
self.assertIs(None, annotation)
2553
def test_stored_kind(self):
2554
preview = self.get_empty_preview()
2555
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2556
preview_tree = preview.get_preview_tree()
2557
self.assertEqual('file', preview_tree.stored_kind('file-id'))
2559
def test_is_executable(self):
2560
preview = self.get_empty_preview()
2561
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2562
preview.set_executability(True, preview.trans_id_file_id('file-id'))
2563
preview_tree = preview.get_preview_tree()
2564
self.assertEqual(True, preview_tree.is_executable('file-id'))
2566
def test_get_set_parent_ids(self):
2567
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2568
self.assertEqual([], preview_tree.get_parent_ids())
2569
preview_tree.set_parent_ids(['rev-1'])
2570
self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
2572
def test_plan_file_merge(self):
2573
work_a = self.make_branch_and_tree('wta')
2574
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2575
work_a.add('file', 'file-id')
2576
base_id = work_a.commit('base version')
2577
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2578
preview = TransformPreview(work_a)
2579
self.addCleanup(preview.finalize)
2580
trans_id = preview.trans_id_file_id('file-id')
2581
preview.delete_contents(trans_id)
2582
preview.create_file('b\nc\nd\ne\n', trans_id)
2583
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2584
tree_a = preview.get_preview_tree()
2585
tree_a.set_parent_ids([base_id])
2587
('killed-a', 'a\n'),
2588
('killed-b', 'b\n'),
2589
('unchanged', 'c\n'),
2590
('unchanged', 'd\n'),
2593
], list(tree_a.plan_file_merge('file-id', tree_b)))
2595
def test_plan_file_merge_revision_tree(self):
2596
work_a = self.make_branch_and_tree('wta')
2597
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2598
work_a.add('file', 'file-id')
2599
base_id = work_a.commit('base version')
2600
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2601
preview = TransformPreview(work_a.basis_tree())
2602
self.addCleanup(preview.finalize)
2603
trans_id = preview.trans_id_file_id('file-id')
2604
preview.delete_contents(trans_id)
2605
preview.create_file('b\nc\nd\ne\n', trans_id)
2606
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2607
tree_a = preview.get_preview_tree()
2608
tree_a.set_parent_ids([base_id])
2610
('killed-a', 'a\n'),
2611
('killed-b', 'b\n'),
2612
('unchanged', 'c\n'),
2613
('unchanged', 'd\n'),
2616
], list(tree_a.plan_file_merge('file-id', tree_b)))
2618
def test_walkdirs(self):
2619
preview = self.get_empty_preview()
2620
preview.version_file('tree-root', preview.root)
2621
preview_tree = preview.get_preview_tree()
2622
file_trans_id = preview.new_file('a', preview.root, 'contents',
2624
expected = [(('', 'tree-root'),
2625
[('a', 'a', 'file', None, 'a-id', 'file')])]
2626
self.assertEqual(expected, list(preview_tree.walkdirs()))
2628
def test_extras(self):
2629
work_tree = self.make_branch_and_tree('tree')
2630
self.build_tree(['tree/removed-file', 'tree/existing-file',
2631
'tree/not-removed-file'])
2632
work_tree.add(['removed-file', 'not-removed-file'])
2633
preview = TransformPreview(work_tree)
2634
self.addCleanup(preview.finalize)
2635
preview.new_file('new-file', preview.root, 'contents')
2636
preview.new_file('new-versioned-file', preview.root, 'contents',
2638
tree = preview.get_preview_tree()
2639
preview.unversion_file(preview.trans_id_tree_path('removed-file'))
2640
self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
2643
def test_merge_into_preview(self):
2644
work_tree = self.make_branch_and_tree('tree')
2645
self.build_tree_contents([('tree/file','b\n')])
2646
work_tree.add('file', 'file-id')
2647
work_tree.commit('first commit')
2648
child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
2649
self.build_tree_contents([('child/file','b\nc\n')])
2650
child_tree.commit('child commit')
2651
child_tree.lock_write()
2652
self.addCleanup(child_tree.unlock)
2653
work_tree.lock_write()
2654
self.addCleanup(work_tree.unlock)
2655
preview = TransformPreview(work_tree)
2656
self.addCleanup(preview.finalize)
2657
preview_tree = preview.get_preview_tree()
2658
file_trans_id = preview.trans_id_file_id('file-id')
2659
preview.delete_contents(file_trans_id)
2660
preview.create_file('a\nb\n', file_trans_id)
2661
pb = progress.DummyProgress()
2662
merger = Merger.from_revision_ids(pb, preview_tree,
2663
child_tree.branch.last_revision(),
2664
other_branch=child_tree.branch,
2665
tree_branch=work_tree.branch)
2666
merger.merge_type = Merge3Merger
2667
tt = merger.make_merger().make_preview_transform()
2668
self.addCleanup(tt.finalize)
2669
final_tree = tt.get_preview_tree()
2670
self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
2672
def test_merge_preview_into_workingtree(self):
2673
tree = self.make_branch_and_tree('tree')
2674
tree.set_root_id('TREE_ROOT')
2675
tt = TransformPreview(tree)
2676
self.addCleanup(tt.finalize)
2677
tt.new_file('name', tt.root, 'content', 'file-id')
2678
tree2 = self.make_branch_and_tree('tree2')
2679
tree2.set_root_id('TREE_ROOT')
2680
pb = progress.DummyProgress()
2681
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2682
pb, tree.basis_tree())
2683
merger.merge_type = Merge3Merger
2686
def test_merge_preview_into_workingtree_handles_conflicts(self):
2687
tree = self.make_branch_and_tree('tree')
2688
self.build_tree_contents([('tree/foo', 'bar')])
2689
tree.add('foo', 'foo-id')
2691
tt = TransformPreview(tree)
2692
self.addCleanup(tt.finalize)
2693
trans_id = tt.trans_id_file_id('foo-id')
2694
tt.delete_contents(trans_id)
2695
tt.create_file('baz', trans_id)
2696
tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2697
self.build_tree_contents([('tree2/foo', 'qux')])
2698
pb = progress.DummyProgress()
2699
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2700
pb, tree.basis_tree())
2701
merger.merge_type = Merge3Merger
2704
def test_is_executable(self):
2705
tree = self.make_branch_and_tree('tree')
2706
preview = TransformPreview(tree)
2707
self.addCleanup(preview.finalize)
2708
preview.new_file('foo', preview.root, 'bar', 'baz-id')
2709
preview_tree = preview.get_preview_tree()
2710
self.assertEqual(False, preview_tree.is_executable('baz-id',
2712
self.assertEqual(False, preview_tree.is_executable('baz-id'))
2714
def test_commit_preview_tree(self):
2715
tree = self.make_branch_and_tree('tree')
2716
rev_id = tree.commit('rev1')
2717
tree.branch.lock_write()
2718
self.addCleanup(tree.branch.unlock)
2719
tt = TransformPreview(tree)
2720
tt.new_file('file', tt.root, 'contents', 'file_id')
2721
self.addCleanup(tt.finalize)
2722
preview = tt.get_preview_tree()
2723
preview.set_parent_ids([rev_id])
2724
builder = tree.branch.get_commit_builder([rev_id])
2725
list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
2726
builder.finish_inventory()
2727
rev2_id = builder.commit('rev2')
2728
rev2_tree = tree.branch.repository.revision_tree(rev2_id)
2729
self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
2732
class FakeSerializer(object):
2733
"""Serializer implementation that simply returns the input.
2735
The input is returned in the order used by pack.ContainerPushParser.
2738
def bytes_record(bytes, names):
2742
class TestSerializeTransform(tests.TestCaseWithTransport):
2744
_test_needs_features = [tests.UnicodeFilenameFeature]
2746
def get_preview(self, tree=None):
2748
tree = self.make_branch_and_tree('tree')
2749
tt = TransformPreview(tree)
2750
self.addCleanup(tt.finalize)
2753
def assertSerializesTo(self, expected, tt):
2754
records = list(tt.serialize(FakeSerializer()))
2755
self.assertEqual(expected, records)
2758
def default_attribs():
2763
'_new_executability': {},
2765
'_tree_path_ids': {'': 'new-0'},
2767
'_removed_contents': [],
2768
'_non_present_ids': {},
2771
def make_records(self, attribs, contents):
2773
(((('attribs'),),), bencode.bencode(attribs))]
2774
records.extend([(((n, k),), c) for n, k, c in contents])
2777
def creation_records(self):
2778
attribs = self.default_attribs()
2779
attribs['_id_number'] = 3
2780
attribs['_new_name'] = {
2781
'new-1': u'foo\u1234'.encode('utf-8'), 'new-2': 'qux'}
2782
attribs['_new_id'] = {'new-1': 'baz', 'new-2': 'quxx'}
2783
attribs['_new_parent'] = {'new-1': 'new-0', 'new-2': 'new-0'}
2784
attribs['_new_executability'] = {'new-1': 1}
2786
('new-1', 'file', 'i 1\nbar\n'),
2787
('new-2', 'directory', ''),
2789
return self.make_records(attribs, contents)
2791
def test_serialize_creation(self):
2792
tt = self.get_preview()
2793
tt.new_file(u'foo\u1234', tt.root, 'bar', 'baz', True)
2794
tt.new_directory('qux', tt.root, 'quxx')
2795
self.assertSerializesTo(self.creation_records(), tt)
2797
def test_deserialize_creation(self):
2798
tt = self.get_preview()
2799
tt.deserialize(iter(self.creation_records()))
2800
self.assertEqual(3, tt._id_number)
2801
self.assertEqual({'new-1': u'foo\u1234',
2802
'new-2': 'qux'}, tt._new_name)
2803
self.assertEqual({'new-1': 'baz', 'new-2': 'quxx'}, tt._new_id)
2804
self.assertEqual({'new-1': tt.root, 'new-2': tt.root}, tt._new_parent)
2805
self.assertEqual({'baz': 'new-1', 'quxx': 'new-2'}, tt._r_new_id)
2806
self.assertEqual({'new-1': True}, tt._new_executability)
2807
self.assertEqual({'new-1': 'file',
2808
'new-2': 'directory'}, tt._new_contents)
2809
foo_limbo = open(tt._limbo_name('new-1'), 'rb')
2811
foo_content = foo_limbo.read()
2814
self.assertEqual('bar', foo_content)
2816
def symlink_creation_records(self):
2817
attribs = self.default_attribs()
2818
attribs['_id_number'] = 2
2819
attribs['_new_name'] = {'new-1': u'foo\u1234'.encode('utf-8')}
2820
attribs['_new_parent'] = {'new-1': 'new-0'}
2821
contents = [('new-1', 'symlink', u'bar\u1234'.encode('utf-8'))]
2822
return self.make_records(attribs, contents)
2824
def test_serialize_symlink_creation(self):
2825
self.requireFeature(tests.SymlinkFeature)
2826
tt = self.get_preview()
2827
tt.new_symlink(u'foo\u1234', tt.root, u'bar\u1234')
2828
self.assertSerializesTo(self.symlink_creation_records(), tt)
2830
def test_deserialize_symlink_creation(self):
2831
self.requireFeature(tests.SymlinkFeature)
2832
tt = self.get_preview()
2833
tt.deserialize(iter(self.symlink_creation_records()))
2834
abspath = tt._limbo_name('new-1')
2835
foo_content = osutils.readlink(abspath)
2836
self.assertEqual(u'bar\u1234', foo_content)
2838
def make_destruction_preview(self):
2839
tree = self.make_branch_and_tree('.')
2840
self.build_tree([u'foo\u1234', 'bar'])
2841
tree.add([u'foo\u1234', 'bar'], ['foo-id', 'bar-id'])
2842
return self.get_preview(tree)
2844
def destruction_records(self):
2845
attribs = self.default_attribs()
2846
attribs['_id_number'] = 3
2847
attribs['_removed_id'] = ['new-1']
2848
attribs['_removed_contents'] = ['new-2']
2849
attribs['_tree_path_ids'] = {
2851
u'foo\u1234'.encode('utf-8'): 'new-1',
2854
return self.make_records(attribs, [])
2856
def test_serialize_destruction(self):
2857
tt = self.make_destruction_preview()
2858
foo_trans_id = tt.trans_id_tree_file_id('foo-id')
2859
tt.unversion_file(foo_trans_id)
2860
bar_trans_id = tt.trans_id_tree_file_id('bar-id')
2861
tt.delete_contents(bar_trans_id)
2862
self.assertSerializesTo(self.destruction_records(), tt)
2864
def test_deserialize_destruction(self):
2865
tt = self.make_destruction_preview()
2866
tt.deserialize(iter(self.destruction_records()))
2867
self.assertEqual({u'foo\u1234': 'new-1',
2869
'': tt.root}, tt._tree_path_ids)
2870
self.assertEqual({'new-1': u'foo\u1234',
2872
tt.root: ''}, tt._tree_id_paths)
2873
self.assertEqual(set(['new-1']), tt._removed_id)
2874
self.assertEqual(set(['new-2']), tt._removed_contents)
2876
def missing_records(self):
2877
attribs = self.default_attribs()
2878
attribs['_id_number'] = 2
2879
attribs['_non_present_ids'] = {
2881
return self.make_records(attribs, [])
2883
def test_serialize_missing(self):
2884
tt = self.get_preview()
2885
boo_trans_id = tt.trans_id_file_id('boo')
2886
self.assertSerializesTo(self.missing_records(), tt)
2888
def test_deserialize_missing(self):
2889
tt = self.get_preview()
2890
tt.deserialize(iter(self.missing_records()))
2891
self.assertEqual({'boo': 'new-1'}, tt._non_present_ids)
2893
def make_modification_preview(self):
2894
LINES_ONE = 'aa\nbb\ncc\ndd\n'
2895
LINES_TWO = 'z\nbb\nx\ndd\n'
2896
tree = self.make_branch_and_tree('tree')
2897
self.build_tree_contents([('tree/file', LINES_ONE)])
2898
tree.add('file', 'file-id')
2899
return self.get_preview(tree), LINES_TWO
2901
def modification_records(self):
2902
attribs = self.default_attribs()
2903
attribs['_id_number'] = 2
2904
attribs['_tree_path_ids'] = {
2907
attribs['_removed_contents'] = ['new-1']
2908
contents = [('new-1', 'file',
2909
'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
2910
return self.make_records(attribs, contents)
2912
def test_serialize_modification(self):
2913
tt, LINES = self.make_modification_preview()
2914
trans_id = tt.trans_id_file_id('file-id')
2915
tt.delete_contents(trans_id)
2916
tt.create_file(LINES, trans_id)
2917
self.assertSerializesTo(self.modification_records(), tt)
2919
def test_deserialize_modification(self):
2920
tt, LINES = self.make_modification_preview()
2921
tt.deserialize(iter(self.modification_records()))
2922
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2924
def make_kind_change_preview(self):
2925
LINES = 'a\nb\nc\nd\n'
2926
tree = self.make_branch_and_tree('tree')
2927
self.build_tree(['tree/foo/'])
2928
tree.add('foo', 'foo-id')
2929
return self.get_preview(tree), LINES
2931
def kind_change_records(self):
2932
attribs = self.default_attribs()
2933
attribs['_id_number'] = 2
2934
attribs['_tree_path_ids'] = {
2937
attribs['_removed_contents'] = ['new-1']
2938
contents = [('new-1', 'file',
2939
'i 4\na\nb\nc\nd\n\n')]
2940
return self.make_records(attribs, contents)
2942
def test_serialize_kind_change(self):
2943
tt, LINES = self.make_kind_change_preview()
2944
trans_id = tt.trans_id_file_id('foo-id')
2945
tt.delete_contents(trans_id)
2946
tt.create_file(LINES, trans_id)
2947
self.assertSerializesTo(self.kind_change_records(), tt)
2949
def test_deserialize_kind_change(self):
2950
tt, LINES = self.make_kind_change_preview()
2951
tt.deserialize(iter(self.kind_change_records()))
2952
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2954
def make_add_contents_preview(self):
2955
LINES = 'a\nb\nc\nd\n'
2956
tree = self.make_branch_and_tree('tree')
2957
self.build_tree(['tree/foo'])
2959
os.unlink('tree/foo')
2960
return self.get_preview(tree), LINES
2962
def add_contents_records(self):
2963
attribs = self.default_attribs()
2964
attribs['_id_number'] = 2
2965
attribs['_tree_path_ids'] = {
2968
contents = [('new-1', 'file',
2969
'i 4\na\nb\nc\nd\n\n')]
2970
return self.make_records(attribs, contents)
2972
def test_serialize_add_contents(self):
2973
tt, LINES = self.make_add_contents_preview()
2974
trans_id = tt.trans_id_tree_path('foo')
2975
tt.create_file(LINES, trans_id)
2976
self.assertSerializesTo(self.add_contents_records(), tt)
2978
def test_deserialize_add_contents(self):
2979
tt, LINES = self.make_add_contents_preview()
2980
tt.deserialize(iter(self.add_contents_records()))
2981
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2983
def test_get_parents_lines(self):
2984
LINES_ONE = 'aa\nbb\ncc\ndd\n'
2985
LINES_TWO = 'z\nbb\nx\ndd\n'
2986
tree = self.make_branch_and_tree('tree')
2987
self.build_tree_contents([('tree/file', LINES_ONE)])
2988
tree.add('file', 'file-id')
2989
tt = self.get_preview(tree)
2990
trans_id = tt.trans_id_tree_path('file')
2991
self.assertEqual((['aa\n', 'bb\n', 'cc\n', 'dd\n'],),
2992
tt._get_parents_lines(trans_id))
2994
def test_get_parents_texts(self):
2995
LINES_ONE = 'aa\nbb\ncc\ndd\n'
2996
LINES_TWO = 'z\nbb\nx\ndd\n'
2997
tree = self.make_branch_and_tree('tree')
2998
self.build_tree_contents([('tree/file', LINES_ONE)])
2999
tree.add('file', 'file-id')
3000
tt = self.get_preview(tree)
3001
trans_id = tt.trans_id_tree_path('file')
3002
self.assertEqual((LINES_ONE,),
3003
tt._get_parents_texts(trans_id))