479
776
rename.set_executability(True, myfile)
482
def test_find_interesting(self):
483
create, root = self.get_transform()
485
create.new_file('vfile', root, 'myfile-text', 'myfile-id')
486
create.new_file('uvfile', root, 'othertext')
488
self.assertEqual(find_interesting(wt, wt, ['vfile']),
490
self.assertRaises(NotVersionedError, find_interesting, wt, wt,
779
def test_set_executability_order(self):
780
"""Ensure that executability behaves the same, no matter what order.
782
- create file and set executability simultaneously
783
- create file and set executability afterward
784
- unsetting the executability of a file whose executability has not been
785
declared should throw an exception (this may happen when a
786
merge attempts to create a file with a duplicate ID)
788
transform, root = self.get_transform()
791
self.addCleanup(wt.unlock)
792
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
794
sac = transform.new_file('set_after_creation', root,
795
'Set after creation', 'sac')
796
transform.set_executability(True, sac)
797
uws = transform.new_file('unset_without_set', root, 'Unset badly',
799
self.assertRaises(KeyError, transform.set_executability, None, uws)
801
self.assertTrue(wt.is_executable('soc'))
802
self.assertTrue(wt.is_executable('sac'))
804
def test_preserve_mode(self):
805
"""File mode is preserved when replacing content"""
806
if sys.platform == 'win32':
807
raise TestSkipped('chmod has no effect on win32')
808
transform, root = self.get_transform()
809
transform.new_file('file1', root, 'contents', 'file1-id', True)
812
self.addCleanup(self.wt.unlock)
813
self.assertTrue(self.wt.is_executable('file1-id'))
814
transform, root = self.get_transform()
815
file1_id = transform.trans_id_tree_file_id('file1-id')
816
transform.delete_contents(file1_id)
817
transform.create_file('contents2', file1_id)
819
self.assertTrue(self.wt.is_executable('file1-id'))
821
def test__set_mode_stats_correctly(self):
822
"""_set_mode stats to determine file mode."""
823
if sys.platform == 'win32':
824
raise TestSkipped('chmod has no effect on win32')
828
def instrumented_stat(path):
829
stat_paths.append(path)
830
return real_stat(path)
832
transform, root = self.get_transform()
834
bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
835
file_id='bar-id-1', executable=False)
838
transform, root = self.get_transform()
839
bar1_id = transform.trans_id_tree_path('bar')
840
bar2_id = transform.trans_id_tree_path('bar2')
842
os.stat = instrumented_stat
843
transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
848
bar1_abspath = self.wt.abspath('bar')
849
self.assertEqual([bar1_abspath], stat_paths)
851
def test_iter_changes(self):
852
self.wt.set_root_id('eert_toor')
853
transform, root = self.get_transform()
854
transform.new_file('old', root, 'blah', 'id-1', True)
856
transform, root = self.get_transform()
858
self.assertEqual([], list(transform.iter_changes()))
859
old = transform.trans_id_tree_file_id('id-1')
860
transform.unversion_file(old)
861
self.assertEqual([('id-1', ('old', None), False, (True, False),
862
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
863
(True, True))], list(transform.iter_changes()))
864
transform.new_directory('new', root, 'id-1')
865
self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
866
('eert_toor', 'eert_toor'), ('old', 'new'),
867
('file', 'directory'),
868
(True, False))], list(transform.iter_changes()))
872
def test_iter_changes_new(self):
873
self.wt.set_root_id('eert_toor')
874
transform, root = self.get_transform()
875
transform.new_file('old', root, 'blah')
877
transform, root = self.get_transform()
879
old = transform.trans_id_tree_path('old')
880
transform.version_file('id-1', old)
881
self.assertEqual([('id-1', (None, 'old'), False, (False, True),
882
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
883
(False, False))], list(transform.iter_changes()))
887
def test_iter_changes_modifications(self):
888
self.wt.set_root_id('eert_toor')
889
transform, root = self.get_transform()
890
transform.new_file('old', root, 'blah', 'id-1')
891
transform.new_file('new', root, 'blah')
892
transform.new_directory('subdir', root, 'subdir-id')
894
transform, root = self.get_transform()
896
old = transform.trans_id_tree_path('old')
897
subdir = transform.trans_id_tree_file_id('subdir-id')
898
new = transform.trans_id_tree_path('new')
899
self.assertEqual([], list(transform.iter_changes()))
902
transform.delete_contents(old)
903
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
904
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
905
(False, False))], list(transform.iter_changes()))
908
transform.create_file('blah', old)
909
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
910
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
911
(False, False))], list(transform.iter_changes()))
912
transform.cancel_deletion(old)
913
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
914
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
915
(False, False))], list(transform.iter_changes()))
916
transform.cancel_creation(old)
918
# move file_id to a different file
919
self.assertEqual([], list(transform.iter_changes()))
920
transform.unversion_file(old)
921
transform.version_file('id-1', new)
922
transform.adjust_path('old', root, new)
923
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
924
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
925
(False, False))], list(transform.iter_changes()))
926
transform.cancel_versioning(new)
927
transform._removed_id = set()
930
self.assertEqual([], list(transform.iter_changes()))
931
transform.set_executability(True, old)
932
self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
933
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
934
(False, True))], list(transform.iter_changes()))
935
transform.set_executability(None, old)
938
self.assertEqual([], list(transform.iter_changes()))
939
transform.adjust_path('new', root, old)
940
transform._new_parent = {}
941
self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
942
('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
943
(False, False))], list(transform.iter_changes()))
944
transform._new_name = {}
947
self.assertEqual([], list(transform.iter_changes()))
948
transform.adjust_path('new', subdir, old)
949
transform._new_name = {}
950
self.assertEqual([('id-1', ('old', 'subdir/old'), False,
951
(True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
952
('file', 'file'), (False, False))],
953
list(transform.iter_changes()))
954
transform._new_path = {}
959
def test_iter_changes_modified_bleed(self):
960
self.wt.set_root_id('eert_toor')
961
"""Modified flag should not bleed from one change to another"""
962
# unfortunately, we have no guarantee that file1 (which is modified)
963
# will be applied before file2. And if it's applied after file2, it
964
# obviously can't bleed into file2's change output. But for now, it
966
transform, root = self.get_transform()
967
transform.new_file('file1', root, 'blah', 'id-1')
968
transform.new_file('file2', root, 'blah', 'id-2')
970
transform, root = self.get_transform()
972
transform.delete_contents(transform.trans_id_file_id('id-1'))
973
transform.set_executability(True,
974
transform.trans_id_file_id('id-2'))
975
self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
976
('eert_toor', 'eert_toor'), ('file1', u'file1'),
977
('file', None), (False, False)),
978
('id-2', (u'file2', u'file2'), False, (True, True),
979
('eert_toor', 'eert_toor'), ('file2', u'file2'),
980
('file', 'file'), (False, True))],
981
list(transform.iter_changes()))
985
def test_iter_changes_move_missing(self):
986
"""Test moving ids with no files around"""
987
self.wt.set_root_id('toor_eert')
988
# Need two steps because versioning a non-existant file is a conflict.
989
transform, root = self.get_transform()
990
transform.new_directory('floater', root, 'floater-id')
992
transform, root = self.get_transform()
993
transform.delete_contents(transform.trans_id_tree_path('floater'))
995
transform, root = self.get_transform()
996
floater = transform.trans_id_tree_path('floater')
998
transform.adjust_path('flitter', root, floater)
999
self.assertEqual([('floater-id', ('floater', 'flitter'), False,
1000
(True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
1001
(None, None), (False, False))], list(transform.iter_changes()))
1003
transform.finalize()
1005
def test_iter_changes_pointless(self):
1006
"""Ensure that no-ops are not treated as modifications"""
1007
self.wt.set_root_id('eert_toor')
1008
transform, root = self.get_transform()
1009
transform.new_file('old', root, 'blah', 'id-1')
1010
transform.new_directory('subdir', root, 'subdir-id')
1012
transform, root = self.get_transform()
1014
old = transform.trans_id_tree_path('old')
1015
subdir = transform.trans_id_tree_file_id('subdir-id')
1016
self.assertEqual([], list(transform.iter_changes()))
1017
transform.delete_contents(subdir)
1018
transform.create_directory(subdir)
1019
transform.set_executability(False, old)
1020
transform.unversion_file(old)
1021
transform.version_file('id-1', old)
1022
transform.adjust_path('old', root, old)
1023
self.assertEqual([], list(transform.iter_changes()))
1025
transform.finalize()
1027
def test_rename_count(self):
1028
transform, root = self.get_transform()
1029
transform.new_file('name1', root, 'contents')
1030
self.assertEqual(transform.rename_count, 0)
1032
self.assertEqual(transform.rename_count, 1)
1033
transform2, root = self.get_transform()
1034
transform2.adjust_path('name2', root,
1035
transform2.trans_id_tree_path('name1'))
1036
self.assertEqual(transform2.rename_count, 0)
1038
self.assertEqual(transform2.rename_count, 2)
1040
def test_change_parent(self):
1041
"""Ensure that after we change a parent, the results are still right.
1043
Renames and parent changes on pending transforms can happen as part
1044
of conflict resolution, and are explicitly permitted by the
1047
This test ensures they work correctly with the rename-avoidance
1050
transform, root = self.get_transform()
1051
parent1 = transform.new_directory('parent1', root)
1052
child1 = transform.new_file('child1', parent1, 'contents')
1053
parent2 = transform.new_directory('parent2', root)
1054
transform.adjust_path('child1', parent2, child1)
1056
self.failIfExists(self.wt.abspath('parent1/child1'))
1057
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1058
# rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1059
# no rename for child1 (counting only renames during apply)
1060
self.failUnlessEqual(2, transform.rename_count)
1062
def test_cancel_parent(self):
1063
"""Cancelling a parent doesn't cause deletion of a non-empty directory
1065
This is like the test_change_parent, except that we cancel the parent
1066
before adjusting the path. The transform must detect that the
1067
directory is non-empty, and move children to safe locations.
1069
transform, root = self.get_transform()
1070
parent1 = transform.new_directory('parent1', root)
1071
child1 = transform.new_file('child1', parent1, 'contents')
1072
child2 = transform.new_file('child2', parent1, 'contents')
1074
transform.cancel_creation(parent1)
1076
self.fail('Failed to move child1 before deleting parent1')
1077
transform.cancel_creation(child2)
1078
transform.create_directory(parent1)
1080
transform.cancel_creation(parent1)
1081
# If the transform incorrectly believes that child2 is still in
1082
# parent1's limbo directory, it will try to rename it and fail
1083
# because was already moved by the first cancel_creation.
1085
self.fail('Transform still thinks child2 is a child of parent1')
1086
parent2 = transform.new_directory('parent2', root)
1087
transform.adjust_path('child1', parent2, child1)
1089
self.failIfExists(self.wt.abspath('parent1'))
1090
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1091
# rename limbo/new-3 => parent2, rename limbo/new-2 => child1
1092
self.failUnlessEqual(2, transform.rename_count)
1094
def test_adjust_and_cancel(self):
1095
"""Make sure adjust_path keeps track of limbo children properly"""
1096
transform, root = self.get_transform()
1097
parent1 = transform.new_directory('parent1', root)
1098
child1 = transform.new_file('child1', parent1, 'contents')
1099
parent2 = transform.new_directory('parent2', root)
1100
transform.adjust_path('child1', parent2, child1)
1101
transform.cancel_creation(child1)
1103
transform.cancel_creation(parent1)
1104
# if the transform thinks child1 is still in parent1's limbo
1105
# directory, it will attempt to move it and fail.
1107
self.fail('Transform still thinks child1 is a child of parent1')
1108
transform.finalize()
1110
def test_noname_contents(self):
1111
"""TreeTransform should permit deferring naming files."""
1112
transform, root = self.get_transform()
1113
parent = transform.trans_id_file_id('parent-id')
1115
transform.create_directory(parent)
1117
self.fail("Can't handle contents with no name")
1118
transform.finalize()
1120
def test_noname_contents_nested(self):
1121
"""TreeTransform should permit deferring naming files."""
1122
transform, root = self.get_transform()
1123
parent = transform.trans_id_file_id('parent-id')
1125
transform.create_directory(parent)
1127
self.fail("Can't handle contents with no name")
1128
child = transform.new_directory('child', parent)
1129
transform.adjust_path('parent', root, parent)
1131
self.failUnlessExists(self.wt.abspath('parent/child'))
1132
self.assertEqual(1, transform.rename_count)
1134
def test_reuse_name(self):
1135
"""Avoid reusing the same limbo name for different files"""
1136
transform, root = self.get_transform()
1137
parent = transform.new_directory('parent', root)
1138
child1 = transform.new_directory('child', parent)
1140
child2 = transform.new_directory('child', parent)
1142
self.fail('Tranform tried to use the same limbo name twice')
1143
transform.adjust_path('child2', parent, child2)
1145
# limbo/new-1 => parent, limbo/new-3 => parent/child2
1146
# child2 is put into top-level limbo because child1 has already
1147
# claimed the direct limbo path when child2 is created. There is no
1148
# advantage in renaming files once they're in top-level limbo, except
1150
self.assertEqual(2, transform.rename_count)
1152
def test_reuse_when_first_moved(self):
1153
"""Don't avoid direct paths when it is safe to use them"""
1154
transform, root = self.get_transform()
1155
parent = transform.new_directory('parent', root)
1156
child1 = transform.new_directory('child', parent)
1157
transform.adjust_path('child1', parent, child1)
1158
child2 = transform.new_directory('child', parent)
1160
# limbo/new-1 => parent
1161
self.assertEqual(1, transform.rename_count)
1163
def test_reuse_after_cancel(self):
1164
"""Don't avoid direct paths when it is safe to use them"""
1165
transform, root = self.get_transform()
1166
parent2 = transform.new_directory('parent2', root)
1167
child1 = transform.new_directory('child1', parent2)
1168
transform.cancel_creation(parent2)
1169
transform.create_directory(parent2)
1170
child2 = transform.new_directory('child1', parent2)
1171
transform.adjust_path('child2', parent2, child1)
1173
# limbo/new-1 => parent2, limbo/new-2 => parent2/child1
1174
self.assertEqual(2, transform.rename_count)
1176
def test_finalize_order(self):
1177
"""Finalize must be done in child-to-parent order"""
1178
transform, root = self.get_transform()
1179
parent = transform.new_directory('parent', root)
1180
child = transform.new_directory('child', parent)
1182
transform.finalize()
1184
self.fail('Tried to remove parent before child1')
1186
def test_cancel_with_cancelled_child_should_succeed(self):
1187
transform, root = self.get_transform()
1188
parent = transform.new_directory('parent', root)
1189
child = transform.new_directory('child', parent)
1190
transform.cancel_creation(child)
1191
transform.cancel_creation(parent)
1192
transform.finalize()
1194
def test_rollback_on_directory_clash(self):
1196
wt = self.make_branch_and_tree('.')
1197
tt = TreeTransform(wt) # TreeTransform obtains write lock
1199
foo = tt.new_directory('foo', tt.root)
1200
tt.new_file('bar', foo, 'foobar')
1201
baz = tt.new_directory('baz', tt.root)
1202
tt.new_file('qux', baz, 'quux')
1203
# Ask for a rename 'foo' -> 'baz'
1204
tt.adjust_path('baz', tt.root, foo)
1205
# Lie to tt that we've already resolved all conflicts.
1206
tt.apply(no_conflicts=True)
1210
# The rename will fail because the target directory is not empty (but
1211
# raises FileExists anyway).
1212
err = self.assertRaises(errors.FileExists, tt_helper)
1213
self.assertContainsRe(str(err),
1214
"^File exists: .+/baz")
1216
def test_two_directories_clash(self):
1218
wt = self.make_branch_and_tree('.')
1219
tt = TreeTransform(wt) # TreeTransform obtains write lock
1221
foo_1 = tt.new_directory('foo', tt.root)
1222
tt.new_directory('bar', foo_1)
1223
# Adding the same directory with a different content
1224
foo_2 = tt.new_directory('foo', tt.root)
1225
tt.new_directory('baz', foo_2)
1226
# Lie to tt that we've already resolved all conflicts.
1227
tt.apply(no_conflicts=True)
1231
err = self.assertRaises(errors.FileExists, tt_helper)
1232
self.assertContainsRe(str(err),
1233
"^File exists: .+/foo")
1235
def test_two_directories_clash_finalize(self):
1237
wt = self.make_branch_and_tree('.')
1238
tt = TreeTransform(wt) # TreeTransform obtains write lock
1240
foo_1 = tt.new_directory('foo', tt.root)
1241
tt.new_directory('bar', foo_1)
1242
# Adding the same directory with a different content
1243
foo_2 = tt.new_directory('foo', tt.root)
1244
tt.new_directory('baz', foo_2)
1245
# Lie to tt that we've already resolved all conflicts.
1246
tt.apply(no_conflicts=True)
1250
err = self.assertRaises(errors.FileExists, tt_helper)
1251
self.assertContainsRe(str(err),
1252
"^File exists: .+/foo")
1254
def test_file_to_directory(self):
1255
wt = self.make_branch_and_tree('.')
1256
self.build_tree(['foo'])
1259
tt = TreeTransform(wt)
1260
self.addCleanup(tt.finalize)
1261
foo_trans_id = tt.trans_id_tree_path("foo")
1262
tt.delete_contents(foo_trans_id)
1263
tt.create_directory(foo_trans_id)
1264
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1265
tt.create_file(["aa\n"], bar_trans_id)
1266
tt.version_file("bar-1", bar_trans_id)
1268
self.failUnlessExists("foo/bar")
1271
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1276
changes = wt.changes_from(wt.basis_tree())
1277
self.assertFalse(changes.has_changed(), changes)
1279
def test_file_to_symlink(self):
1280
self.requireFeature(SymlinkFeature)
1281
wt = self.make_branch_and_tree('.')
1282
self.build_tree(['foo'])
1285
tt = TreeTransform(wt)
1286
self.addCleanup(tt.finalize)
1287
foo_trans_id = tt.trans_id_tree_path("foo")
1288
tt.delete_contents(foo_trans_id)
1289
tt.create_symlink("bar", foo_trans_id)
1291
self.failUnlessExists("foo")
1293
self.addCleanup(wt.unlock)
1294
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1297
def test_dir_to_file(self):
1298
wt = self.make_branch_and_tree('.')
1299
self.build_tree(['foo/', 'foo/bar'])
1300
wt.add(['foo', 'foo/bar'])
1302
tt = TreeTransform(wt)
1303
self.addCleanup(tt.finalize)
1304
foo_trans_id = tt.trans_id_tree_path("foo")
1305
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1306
tt.delete_contents(foo_trans_id)
1307
tt.delete_versioned(bar_trans_id)
1308
tt.create_file(["aa\n"], foo_trans_id)
1310
self.failUnlessExists("foo")
1312
self.addCleanup(wt.unlock)
1313
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1316
def test_dir_to_hardlink(self):
1317
self.requireFeature(HardlinkFeature)
1318
wt = self.make_branch_and_tree('.')
1319
self.build_tree(['foo/', 'foo/bar'])
1320
wt.add(['foo', 'foo/bar'])
1322
tt = TreeTransform(wt)
1323
self.addCleanup(tt.finalize)
1324
foo_trans_id = tt.trans_id_tree_path("foo")
1325
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1326
tt.delete_contents(foo_trans_id)
1327
tt.delete_versioned(bar_trans_id)
1328
self.build_tree(['baz'])
1329
tt.create_hardlink("baz", foo_trans_id)
1331
self.failUnlessExists("foo")
1332
self.failUnlessExists("baz")
1334
self.addCleanup(wt.unlock)
1335
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1338
def test_no_final_path(self):
1339
transform, root = self.get_transform()
1340
trans_id = transform.trans_id_file_id('foo')
1341
transform.create_file('bar', trans_id)
1342
transform.cancel_creation(trans_id)
1345
def test_create_from_tree(self):
1346
tree1 = self.make_branch_and_tree('tree1')
1347
self.build_tree_contents([('tree1/foo/',), ('tree1/bar', 'baz')])
1348
tree1.add(['foo', 'bar'], ['foo-id', 'bar-id'])
1349
tree2 = self.make_branch_and_tree('tree2')
1350
tt = TreeTransform(tree2)
1351
foo_trans_id = tt.create_path('foo', tt.root)
1352
create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1353
bar_trans_id = tt.create_path('bar', tt.root)
1354
create_from_tree(tt, bar_trans_id, tree1, 'bar-id')
1356
self.assertEqual('directory', osutils.file_kind('tree2/foo'))
1357
self.assertFileEqual('baz', 'tree2/bar')
1359
def test_create_from_tree_bytes(self):
1360
"""Provided lines are used instead of tree content."""
1361
tree1 = self.make_branch_and_tree('tree1')
1362
self.build_tree_contents([('tree1/foo', 'bar'),])
1363
tree1.add('foo', 'foo-id')
1364
tree2 = self.make_branch_and_tree('tree2')
1365
tt = TreeTransform(tree2)
1366
foo_trans_id = tt.create_path('foo', tt.root)
1367
create_from_tree(tt, foo_trans_id, tree1, 'foo-id', bytes='qux')
1369
self.assertFileEqual('qux', 'tree2/foo')
1371
def test_create_from_tree_symlink(self):
1372
self.requireFeature(SymlinkFeature)
1373
tree1 = self.make_branch_and_tree('tree1')
1374
os.symlink('bar', 'tree1/foo')
1375
tree1.add('foo', 'foo-id')
1376
tt = TreeTransform(self.make_branch_and_tree('tree2'))
1377
foo_trans_id = tt.create_path('foo', tt.root)
1378
create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1380
self.assertEqual('bar', os.readlink('tree2/foo'))
494
1383
class TransformGroup(object):
495
def __init__(self, dirname):
1385
def __init__(self, dirname, root_id):
496
1386
self.name = dirname
497
1387
os.mkdir(dirname)
498
1388
self.wt = BzrDir.create_standalone_workingtree(dirname)
1389
self.wt.set_root_id(root_id)
499
1390
self.b = self.wt.branch
500
1391
self.tt = TreeTransform(self.wt)
501
1392
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
503
1395
def conflict_text(tree, merge):
504
1396
template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
505
1397
return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
508
1400
class TestTransformMerge(TestCaseInTempDir):
509
1402
def test_text_merge(self):
510
base = TransformGroup("base")
1403
root_id = generate_ids.gen_root_id()
1404
base = TransformGroup("base", root_id)
511
1405
base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
512
1406
base.tt.new_file('b', base.root, 'b1', 'b')
513
1407
base.tt.new_file('c', base.root, 'c', 'c')
698
1596
a.add(['foo', 'foo/bar', 'foo/baz'])
699
1597
a.commit('initial commit')
700
1598
b = BzrDir.create_standalone_workingtree('b')
701
build_tree(a.basis_tree(), b)
1599
basis = a.basis_tree()
1601
self.addCleanup(basis.unlock)
1602
build_tree(basis, b)
702
1603
self.assertIs(os.path.isdir('b/foo'), True)
703
1604
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
704
1605
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1607
def test_build_with_references(self):
1608
tree = self.make_branch_and_tree('source',
1609
format='dirstate-with-subtree')
1610
subtree = self.make_branch_and_tree('source/subtree',
1611
format='dirstate-with-subtree')
1612
tree.add_reference(subtree)
1613
tree.commit('a revision')
1614
tree.branch.create_checkout('target')
1615
self.failUnlessExists('target')
1616
self.failUnlessExists('target/subtree')
1618
def test_file_conflict_handling(self):
1619
"""Ensure that when building trees, conflict handling is done"""
1620
source = self.make_branch_and_tree('source')
1621
target = self.make_branch_and_tree('target')
1622
self.build_tree(['source/file', 'target/file'])
1623
source.add('file', 'new-file')
1624
source.commit('added file')
1625
build_tree(source.basis_tree(), target)
1626
self.assertEqual([DuplicateEntry('Moved existing file to',
1627
'file.moved', 'file', None, 'new-file')],
1629
target2 = self.make_branch_and_tree('target2')
1630
target_file = file('target2/file', 'wb')
1632
source_file = file('source/file', 'rb')
1634
target_file.write(source_file.read())
1639
build_tree(source.basis_tree(), target2)
1640
self.assertEqual([], target2.conflicts())
1642
def test_symlink_conflict_handling(self):
1643
"""Ensure that when building trees, conflict handling is done"""
1644
self.requireFeature(SymlinkFeature)
1645
source = self.make_branch_and_tree('source')
1646
os.symlink('foo', 'source/symlink')
1647
source.add('symlink', 'new-symlink')
1648
source.commit('added file')
1649
target = self.make_branch_and_tree('target')
1650
os.symlink('bar', 'target/symlink')
1651
build_tree(source.basis_tree(), target)
1652
self.assertEqual([DuplicateEntry('Moved existing file to',
1653
'symlink.moved', 'symlink', None, 'new-symlink')],
1655
target = self.make_branch_and_tree('target2')
1656
os.symlink('foo', 'target2/symlink')
1657
build_tree(source.basis_tree(), target)
1658
self.assertEqual([], target.conflicts())
1660
def test_directory_conflict_handling(self):
1661
"""Ensure that when building trees, conflict handling is done"""
1662
source = self.make_branch_and_tree('source')
1663
target = self.make_branch_and_tree('target')
1664
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1665
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1666
source.commit('added file')
1667
build_tree(source.basis_tree(), target)
1668
self.assertEqual([], target.conflicts())
1669
self.failUnlessExists('target/dir1/file')
1671
# Ensure contents are merged
1672
target = self.make_branch_and_tree('target2')
1673
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1674
build_tree(source.basis_tree(), target)
1675
self.assertEqual([], target.conflicts())
1676
self.failUnlessExists('target2/dir1/file2')
1677
self.failUnlessExists('target2/dir1/file')
1679
# Ensure new contents are suppressed for existing branches
1680
target = self.make_branch_and_tree('target3')
1681
self.make_branch('target3/dir1')
1682
self.build_tree(['target3/dir1/file2'])
1683
build_tree(source.basis_tree(), target)
1684
self.failIfExists('target3/dir1/file')
1685
self.failUnlessExists('target3/dir1/file2')
1686
self.failUnlessExists('target3/dir1.diverted/file')
1687
self.assertEqual([DuplicateEntry('Diverted to',
1688
'dir1.diverted', 'dir1', 'new-dir1', None)],
1691
target = self.make_branch_and_tree('target4')
1692
self.build_tree(['target4/dir1/'])
1693
self.make_branch('target4/dir1/file')
1694
build_tree(source.basis_tree(), target)
1695
self.failUnlessExists('target4/dir1/file')
1696
self.assertEqual('directory', file_kind('target4/dir1/file'))
1697
self.failUnlessExists('target4/dir1/file.diverted')
1698
self.assertEqual([DuplicateEntry('Diverted to',
1699
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1702
def test_mixed_conflict_handling(self):
1703
"""Ensure that when building trees, conflict handling is done"""
1704
source = self.make_branch_and_tree('source')
1705
target = self.make_branch_and_tree('target')
1706
self.build_tree(['source/name', 'target/name/'])
1707
source.add('name', 'new-name')
1708
source.commit('added file')
1709
build_tree(source.basis_tree(), target)
1710
self.assertEqual([DuplicateEntry('Moved existing file to',
1711
'name.moved', 'name', None, 'new-name')], target.conflicts())
1713
def test_raises_in_populated(self):
1714
source = self.make_branch_and_tree('source')
1715
self.build_tree(['source/name'])
1717
source.commit('added name')
1718
target = self.make_branch_and_tree('target')
1719
self.build_tree(['target/name'])
1721
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1722
build_tree, source.basis_tree(), target)
1724
def test_build_tree_rename_count(self):
1725
source = self.make_branch_and_tree('source')
1726
self.build_tree(['source/file1', 'source/dir1/'])
1727
source.add(['file1', 'dir1'])
1728
source.commit('add1')
1729
target1 = self.make_branch_and_tree('target1')
1730
transform_result = build_tree(source.basis_tree(), target1)
1731
self.assertEqual(2, transform_result.rename_count)
1733
self.build_tree(['source/dir1/file2'])
1734
source.add(['dir1/file2'])
1735
source.commit('add3')
1736
target2 = self.make_branch_and_tree('target2')
1737
transform_result = build_tree(source.basis_tree(), target2)
1738
# children of non-root directories should not be renamed
1739
self.assertEqual(2, transform_result.rename_count)
1741
def create_ab_tree(self):
1742
"""Create a committed test tree with two files"""
1743
source = self.make_branch_and_tree('source')
1744
self.build_tree_contents([('source/file1', 'A')])
1745
self.build_tree_contents([('source/file2', 'B')])
1746
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1747
source.commit('commit files')
1749
self.addCleanup(source.unlock)
1752
def test_build_tree_accelerator_tree(self):
1753
source = self.create_ab_tree()
1754
self.build_tree_contents([('source/file2', 'C')])
1756
real_source_get_file = source.get_file
1757
def get_file(file_id, path=None):
1758
calls.append(file_id)
1759
return real_source_get_file(file_id, path)
1760
source.get_file = get_file
1761
target = self.make_branch_and_tree('target')
1762
revision_tree = source.basis_tree()
1763
revision_tree.lock_read()
1764
self.addCleanup(revision_tree.unlock)
1765
build_tree(revision_tree, target, source)
1766
self.assertEqual(['file1-id'], calls)
1768
self.addCleanup(target.unlock)
1769
self.assertEqual([], list(target.iter_changes(revision_tree)))
1771
def test_build_tree_accelerator_tree_missing_file(self):
1772
source = self.create_ab_tree()
1773
os.unlink('source/file1')
1774
source.remove(['file2'])
1775
target = self.make_branch_and_tree('target')
1776
revision_tree = source.basis_tree()
1777
revision_tree.lock_read()
1778
self.addCleanup(revision_tree.unlock)
1779
build_tree(revision_tree, target, source)
1781
self.addCleanup(target.unlock)
1782
self.assertEqual([], list(target.iter_changes(revision_tree)))
1784
def test_build_tree_accelerator_wrong_kind(self):
1785
self.requireFeature(SymlinkFeature)
1786
source = self.make_branch_and_tree('source')
1787
self.build_tree_contents([('source/file1', '')])
1788
self.build_tree_contents([('source/file2', '')])
1789
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1790
source.commit('commit files')
1791
os.unlink('source/file2')
1792
self.build_tree_contents([('source/file2/', 'C')])
1793
os.unlink('source/file1')
1794
os.symlink('file2', 'source/file1')
1796
real_source_get_file = source.get_file
1797
def get_file(file_id, path=None):
1798
calls.append(file_id)
1799
return real_source_get_file(file_id, path)
1800
source.get_file = get_file
1801
target = self.make_branch_and_tree('target')
1802
revision_tree = source.basis_tree()
1803
revision_tree.lock_read()
1804
self.addCleanup(revision_tree.unlock)
1805
build_tree(revision_tree, target, source)
1806
self.assertEqual([], calls)
1808
self.addCleanup(target.unlock)
1809
self.assertEqual([], list(target.iter_changes(revision_tree)))
1811
def test_build_tree_hardlink(self):
1812
self.requireFeature(HardlinkFeature)
1813
source = self.create_ab_tree()
1814
target = self.make_branch_and_tree('target')
1815
revision_tree = source.basis_tree()
1816
revision_tree.lock_read()
1817
self.addCleanup(revision_tree.unlock)
1818
build_tree(revision_tree, target, source, hardlink=True)
1820
self.addCleanup(target.unlock)
1821
self.assertEqual([], list(target.iter_changes(revision_tree)))
1822
source_stat = os.stat('source/file1')
1823
target_stat = os.stat('target/file1')
1824
self.assertEqual(source_stat, target_stat)
1826
# Explicitly disallowing hardlinks should prevent them.
1827
target2 = self.make_branch_and_tree('target2')
1828
build_tree(revision_tree, target2, source, hardlink=False)
1830
self.addCleanup(target2.unlock)
1831
self.assertEqual([], list(target2.iter_changes(revision_tree)))
1832
source_stat = os.stat('source/file1')
1833
target2_stat = os.stat('target2/file1')
1834
self.assertNotEqual(source_stat, target2_stat)
1836
def test_build_tree_accelerator_tree_moved(self):
1837
source = self.make_branch_and_tree('source')
1838
self.build_tree_contents([('source/file1', 'A')])
1839
source.add(['file1'], ['file1-id'])
1840
source.commit('commit files')
1841
source.rename_one('file1', 'file2')
1843
self.addCleanup(source.unlock)
1844
target = self.make_branch_and_tree('target')
1845
revision_tree = source.basis_tree()
1846
revision_tree.lock_read()
1847
self.addCleanup(revision_tree.unlock)
1848
build_tree(revision_tree, target, source)
1850
self.addCleanup(target.unlock)
1851
self.assertEqual([], list(target.iter_changes(revision_tree)))
1853
def test_build_tree_hardlinks_preserve_execute(self):
1854
self.requireFeature(HardlinkFeature)
1855
source = self.create_ab_tree()
1856
tt = TreeTransform(source)
1857
trans_id = tt.trans_id_tree_file_id('file1-id')
1858
tt.set_executability(True, trans_id)
1860
self.assertTrue(source.is_executable('file1-id'))
1861
target = self.make_branch_and_tree('target')
1862
revision_tree = source.basis_tree()
1863
revision_tree.lock_read()
1864
self.addCleanup(revision_tree.unlock)
1865
build_tree(revision_tree, target, source, hardlink=True)
1867
self.addCleanup(target.unlock)
1868
self.assertEqual([], list(target.iter_changes(revision_tree)))
1869
self.assertTrue(source.is_executable('file1-id'))
1871
def test_case_insensitive_build_tree_inventory(self):
1872
if (tests.CaseInsensitiveFilesystemFeature.available()
1873
or tests.CaseInsCasePresFilenameFeature.available()):
1874
raise tests.UnavailableFeature('Fully case sensitive filesystem')
1875
source = self.make_branch_and_tree('source')
1876
self.build_tree(['source/file', 'source/FILE'])
1877
source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1878
source.commit('added files')
1879
# Don't try this at home, kids!
1880
# Force the tree to report that it is case insensitive
1881
target = self.make_branch_and_tree('target')
1882
target.case_sensitive = False
1883
build_tree(source.basis_tree(), target, source, delta_from_tree=True)
1884
self.assertEqual('file.moved', target.id2path('lower-id'))
1885
self.assertEqual('FILE', target.id2path('upper-id'))
1888
class TestCommitTransform(tests.TestCaseWithTransport):
1890
def get_branch(self):
1891
tree = self.make_branch_and_tree('tree')
1893
self.addCleanup(tree.unlock)
1894
tree.commit('empty commit')
1897
def get_branch_and_transform(self):
1898
branch = self.get_branch()
1899
tt = TransformPreview(branch.basis_tree())
1900
self.addCleanup(tt.finalize)
1903
def test_commit_wrong_basis(self):
1904
branch = self.get_branch()
1905
basis = branch.repository.revision_tree(
1906
_mod_revision.NULL_REVISION)
1907
tt = TransformPreview(basis)
1908
self.addCleanup(tt.finalize)
1909
e = self.assertRaises(ValueError, tt.commit, branch, '')
1910
self.assertEqual('TreeTransform not based on branch basis: null:',
1913
def test_empy_commit(self):
1914
branch, tt = self.get_branch_and_transform()
1915
rev = tt.commit(branch, 'my message')
1916
self.assertEqual(2, branch.revno())
1917
repo = branch.repository
1918
self.assertEqual('my message', repo.get_revision(rev).message)
1920
def test_merge_parents(self):
1921
branch, tt = self.get_branch_and_transform()
1922
rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
1923
self.assertEqual(['rev1b', 'rev1c'],
1924
branch.basis_tree().get_parent_ids()[1:])
1926
def test_first_commit(self):
1927
branch = self.make_branch('branch')
1929
self.addCleanup(branch.unlock)
1930
tt = TransformPreview(branch.basis_tree())
1931
self.addCleanup(tt.finalize)
1932
tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
1933
rev = tt.commit(branch, 'my message')
1934
self.assertEqual([], branch.basis_tree().get_parent_ids())
1935
self.assertNotEqual(_mod_revision.NULL_REVISION,
1936
branch.last_revision())
1938
def test_first_commit_with_merge_parents(self):
1939
branch = self.make_branch('branch')
1941
self.addCleanup(branch.unlock)
1942
tt = TransformPreview(branch.basis_tree())
1943
self.addCleanup(tt.finalize)
1944
e = self.assertRaises(ValueError, tt.commit, branch,
1945
'my message', ['rev1b-id'])
1946
self.assertEqual('Cannot supply merge parents for first commit.',
1948
self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
1950
def test_add_files(self):
1951
branch, tt = self.get_branch_and_transform()
1952
tt.new_file('file', tt.root, 'contents', 'file-id')
1953
trans_id = tt.new_directory('dir', tt.root, 'dir-id')
1954
tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
1955
rev = tt.commit(branch, 'message')
1956
tree = branch.basis_tree()
1957
self.assertEqual('file', tree.id2path('file-id'))
1958
self.assertEqual('contents', tree.get_file_text('file-id'))
1959
self.assertEqual('dir', tree.id2path('dir-id'))
1960
self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
1961
self.assertEqual('target', tree.get_symlink_target('symlink-id'))
1963
def test_add_unversioned(self):
1964
branch, tt = self.get_branch_and_transform()
1965
tt.new_file('file', tt.root, 'contents')
1966
self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
1967
'message', strict=True)
1969
def test_modify_strict(self):
1970
branch, tt = self.get_branch_and_transform()
1971
tt.new_file('file', tt.root, 'contents', 'file-id')
1972
tt.commit(branch, 'message', strict=True)
1973
tt = TransformPreview(branch.basis_tree())
1974
self.addCleanup(tt.finalize)
1975
trans_id = tt.trans_id_file_id('file-id')
1976
tt.delete_contents(trans_id)
1977
tt.create_file('contents', trans_id)
1978
tt.commit(branch, 'message', strict=True)
1980
def test_commit_malformed(self):
1981
"""Committing a malformed transform should raise an exception.
1983
In this case, we are adding a file without adding its parent.
1985
branch, tt = self.get_branch_and_transform()
1986
parent_id = tt.trans_id_file_id('parent-id')
1987
tt.new_file('file', parent_id, 'contents', 'file-id')
1988
self.assertRaises(errors.MalformedTransform, tt.commit, branch,
706
1992
class MockTransform(object):
708
1994
def has_named_child(self, by_parent, parent_id, name):
732
2020
self.assertEqual(name, 'name.~1~')
733
2021
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
734
2022
self.assertEqual(name, 'name.~4~')
2025
class TestFileMover(tests.TestCaseWithTransport):
2027
def test_file_mover(self):
2028
self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
2029
mover = _FileMover()
2030
mover.rename('a', 'q')
2031
self.failUnlessExists('q')
2032
self.failIfExists('a')
2033
self.failUnlessExists('q/b')
2034
self.failUnlessExists('c')
2035
self.failUnlessExists('c/d')
2037
def test_pre_delete_rollback(self):
2038
self.build_tree(['a/'])
2039
mover = _FileMover()
2040
mover.pre_delete('a', 'q')
2041
self.failUnlessExists('q')
2042
self.failIfExists('a')
2044
self.failIfExists('q')
2045
self.failUnlessExists('a')
2047
def test_apply_deletions(self):
2048
self.build_tree(['a/', 'b/'])
2049
mover = _FileMover()
2050
mover.pre_delete('a', 'q')
2051
mover.pre_delete('b', 'r')
2052
self.failUnlessExists('q')
2053
self.failUnlessExists('r')
2054
self.failIfExists('a')
2055
self.failIfExists('b')
2056
mover.apply_deletions()
2057
self.failIfExists('q')
2058
self.failIfExists('r')
2059
self.failIfExists('a')
2060
self.failIfExists('b')
2062
def test_file_mover_rollback(self):
2063
self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
2064
mover = _FileMover()
2065
mover.rename('c/d', 'c/f')
2066
mover.rename('c/e', 'c/d')
2068
mover.rename('a', 'c')
2069
except errors.FileExists, e:
2071
self.failUnlessExists('a')
2072
self.failUnlessExists('c/d')
2075
class Bogus(Exception):
2079
class TestTransformRollback(tests.TestCaseWithTransport):
2081
class ExceptionFileMover(_FileMover):
2083
def __init__(self, bad_source=None, bad_target=None):
2084
_FileMover.__init__(self)
2085
self.bad_source = bad_source
2086
self.bad_target = bad_target
2088
def rename(self, source, target):
2089
if (self.bad_source is not None and
2090
source.endswith(self.bad_source)):
2092
elif (self.bad_target is not None and
2093
target.endswith(self.bad_target)):
2096
_FileMover.rename(self, source, target)
2098
def test_rollback_rename(self):
2099
tree = self.make_branch_and_tree('.')
2100
self.build_tree(['a/', 'a/b'])
2101
tt = TreeTransform(tree)
2102
self.addCleanup(tt.finalize)
2103
a_id = tt.trans_id_tree_path('a')
2104
tt.adjust_path('c', tt.root, a_id)
2105
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2106
self.assertRaises(Bogus, tt.apply,
2107
_mover=self.ExceptionFileMover(bad_source='a'))
2108
self.failUnlessExists('a')
2109
self.failUnlessExists('a/b')
2111
self.failUnlessExists('c')
2112
self.failUnlessExists('c/d')
2114
def test_rollback_rename_into_place(self):
2115
tree = self.make_branch_and_tree('.')
2116
self.build_tree(['a/', 'a/b'])
2117
tt = TreeTransform(tree)
2118
self.addCleanup(tt.finalize)
2119
a_id = tt.trans_id_tree_path('a')
2120
tt.adjust_path('c', tt.root, a_id)
2121
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2122
self.assertRaises(Bogus, tt.apply,
2123
_mover=self.ExceptionFileMover(bad_target='c/d'))
2124
self.failUnlessExists('a')
2125
self.failUnlessExists('a/b')
2127
self.failUnlessExists('c')
2128
self.failUnlessExists('c/d')
2130
def test_rollback_deletion(self):
2131
tree = self.make_branch_and_tree('.')
2132
self.build_tree(['a/', 'a/b'])
2133
tt = TreeTransform(tree)
2134
self.addCleanup(tt.finalize)
2135
a_id = tt.trans_id_tree_path('a')
2136
tt.delete_contents(a_id)
2137
tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
2138
self.assertRaises(Bogus, tt.apply,
2139
_mover=self.ExceptionFileMover(bad_target='d'))
2140
self.failUnlessExists('a')
2141
self.failUnlessExists('a/b')
2143
def test_resolve_no_parent(self):
2144
wt = self.make_branch_and_tree('.')
2145
tt = TreeTransform(wt)
2146
self.addCleanup(tt.finalize)
2147
parent = tt.trans_id_file_id('parent-id')
2148
tt.new_file('file', parent, 'Contents')
2149
resolve_conflicts(tt)
2152
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
2153
('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
2155
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
2156
('', ''), ('directory', 'directory'), (False, None))
2159
class TestTransformPreview(tests.TestCaseWithTransport):
2161
def create_tree(self):
2162
tree = self.make_branch_and_tree('.')
2163
self.build_tree_contents([('a', 'content 1')])
2164
tree.set_root_id('TREE_ROOT')
2165
tree.add('a', 'a-id')
2166
tree.commit('rev1', rev_id='rev1')
2167
return tree.branch.repository.revision_tree('rev1')
2169
def get_empty_preview(self):
2170
repository = self.make_repository('repo')
2171
tree = repository.revision_tree(_mod_revision.NULL_REVISION)
2172
preview = TransformPreview(tree)
2173
self.addCleanup(preview.finalize)
2176
def test_transform_preview(self):
2177
revision_tree = self.create_tree()
2178
preview = TransformPreview(revision_tree)
2179
self.addCleanup(preview.finalize)
2181
def test_transform_preview_tree(self):
2182
revision_tree = self.create_tree()
2183
preview = TransformPreview(revision_tree)
2184
self.addCleanup(preview.finalize)
2185
preview.get_preview_tree()
2187
def test_transform_new_file(self):
2188
revision_tree = self.create_tree()
2189
preview = TransformPreview(revision_tree)
2190
self.addCleanup(preview.finalize)
2191
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2192
preview_tree = preview.get_preview_tree()
2193
self.assertEqual(preview_tree.kind('file2-id'), 'file')
2195
preview_tree.get_file('file2-id').read(), 'content B\n')
2197
def test_diff_preview_tree(self):
2198
revision_tree = self.create_tree()
2199
preview = TransformPreview(revision_tree)
2200
self.addCleanup(preview.finalize)
2201
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2202
preview_tree = preview.get_preview_tree()
2204
show_diff_trees(revision_tree, preview_tree, out)
2205
lines = out.getvalue().splitlines()
2206
self.assertEqual(lines[0], "=== added file 'file2'")
2207
# 3 lines of diff administrivia
2208
self.assertEqual(lines[4], "+content B")
2210
def test_transform_conflicts(self):
2211
revision_tree = self.create_tree()
2212
preview = TransformPreview(revision_tree)
2213
self.addCleanup(preview.finalize)
2214
preview.new_file('a', preview.root, 'content 2')
2215
resolve_conflicts(preview)
2216
trans_id = preview.trans_id_file_id('a-id')
2217
self.assertEqual('a.moved', preview.final_name(trans_id))
2219
def get_tree_and_preview_tree(self):
2220
revision_tree = self.create_tree()
2221
preview = TransformPreview(revision_tree)
2222
self.addCleanup(preview.finalize)
2223
a_trans_id = preview.trans_id_file_id('a-id')
2224
preview.delete_contents(a_trans_id)
2225
preview.create_file('b content', a_trans_id)
2226
preview_tree = preview.get_preview_tree()
2227
return revision_tree, preview_tree
2229
def test_iter_changes(self):
2230
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2231
root = revision_tree.inventory.root.file_id
2232
self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
2233
(root, root), ('a', 'a'), ('file', 'file'),
2235
list(preview_tree.iter_changes(revision_tree)))
2237
def test_include_unchanged_succeeds(self):
2238
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2239
changes = preview_tree.iter_changes(revision_tree,
2240
include_unchanged=True)
2241
root = revision_tree.inventory.root.file_id
2243
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2245
def test_specific_files(self):
2246
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2247
changes = preview_tree.iter_changes(revision_tree,
2248
specific_files=[''])
2249
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2251
def test_want_unversioned(self):
2252
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2253
changes = preview_tree.iter_changes(revision_tree,
2254
want_unversioned=True)
2255
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2257
def test_ignore_extra_trees_no_specific_files(self):
2258
# extra_trees is harmless without specific_files, so we'll silently
2259
# accept it, even though we won't use it.
2260
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2261
preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
2263
def test_ignore_require_versioned_no_specific_files(self):
2264
# require_versioned is meaningless without specific_files.
2265
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2266
preview_tree.iter_changes(revision_tree, require_versioned=False)
2268
def test_ignore_pb(self):
2269
# pb could be supported, but TT.iter_changes doesn't support it.
2270
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2271
preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
2273
def test_kind(self):
2274
revision_tree = self.create_tree()
2275
preview = TransformPreview(revision_tree)
2276
self.addCleanup(preview.finalize)
2277
preview.new_file('file', preview.root, 'contents', 'file-id')
2278
preview.new_directory('directory', preview.root, 'dir-id')
2279
preview_tree = preview.get_preview_tree()
2280
self.assertEqual('file', preview_tree.kind('file-id'))
2281
self.assertEqual('directory', preview_tree.kind('dir-id'))
2283
def test_get_file_mtime(self):
2284
preview = self.get_empty_preview()
2285
file_trans_id = preview.new_file('file', preview.root, 'contents',
2287
limbo_path = preview._limbo_name(file_trans_id)
2288
preview_tree = preview.get_preview_tree()
2289
self.assertEqual(os.stat(limbo_path).st_mtime,
2290
preview_tree.get_file_mtime('file-id'))
2292
def test_get_file_mtime_renamed(self):
2293
work_tree = self.make_branch_and_tree('tree')
2294
self.build_tree(['tree/file'])
2295
work_tree.add('file', 'file-id')
2296
preview = TransformPreview(work_tree)
2297
self.addCleanup(preview.finalize)
2298
file_trans_id = preview.trans_id_tree_file_id('file-id')
2299
preview.adjust_path('renamed', preview.root, file_trans_id)
2300
preview_tree = preview.get_preview_tree()
2301
preview_mtime = preview_tree.get_file_mtime('file-id', 'renamed')
2302
work_mtime = work_tree.get_file_mtime('file-id', 'file')
2304
def test_get_file(self):
2305
preview = self.get_empty_preview()
2306
preview.new_file('file', preview.root, 'contents', 'file-id')
2307
preview_tree = preview.get_preview_tree()
2308
tree_file = preview_tree.get_file('file-id')
2310
self.assertEqual('contents', tree_file.read())
2314
def test_get_symlink_target(self):
2315
self.requireFeature(SymlinkFeature)
2316
preview = self.get_empty_preview()
2317
preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
2318
preview_tree = preview.get_preview_tree()
2319
self.assertEqual('target',
2320
preview_tree.get_symlink_target('symlink-id'))
2322
def test_all_file_ids(self):
2323
tree = self.make_branch_and_tree('tree')
2324
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
2325
tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
2326
preview = TransformPreview(tree)
2327
self.addCleanup(preview.finalize)
2328
preview.unversion_file(preview.trans_id_file_id('b-id'))
2329
c_trans_id = preview.trans_id_file_id('c-id')
2330
preview.unversion_file(c_trans_id)
2331
preview.version_file('c-id', c_trans_id)
2332
preview_tree = preview.get_preview_tree()
2333
self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
2334
preview_tree.all_file_ids())
2336
def test_path2id_deleted_unchanged(self):
2337
tree = self.make_branch_and_tree('tree')
2338
self.build_tree(['tree/unchanged', 'tree/deleted'])
2339
tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
2340
preview = TransformPreview(tree)
2341
self.addCleanup(preview.finalize)
2342
preview.unversion_file(preview.trans_id_file_id('deleted-id'))
2343
preview_tree = preview.get_preview_tree()
2344
self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
2345
self.assertIs(None, preview_tree.path2id('deleted'))
2347
def test_path2id_created(self):
2348
tree = self.make_branch_and_tree('tree')
2349
self.build_tree(['tree/unchanged'])
2350
tree.add(['unchanged'], ['unchanged-id'])
2351
preview = TransformPreview(tree)
2352
self.addCleanup(preview.finalize)
2353
preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
2354
'contents', 'new-id')
2355
preview_tree = preview.get_preview_tree()
2356
self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
2358
def test_path2id_moved(self):
2359
tree = self.make_branch_and_tree('tree')
2360
self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
2361
tree.add(['old_parent', 'old_parent/child'],
2362
['old_parent-id', 'child-id'])
2363
preview = TransformPreview(tree)
2364
self.addCleanup(preview.finalize)
2365
new_parent = preview.new_directory('new_parent', preview.root,
2367
preview.adjust_path('child', new_parent,
2368
preview.trans_id_file_id('child-id'))
2369
preview_tree = preview.get_preview_tree()
2370
self.assertIs(None, preview_tree.path2id('old_parent/child'))
2371
self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
2373
def test_path2id_renamed_parent(self):
2374
tree = self.make_branch_and_tree('tree')
2375
self.build_tree(['tree/old_name/', 'tree/old_name/child'])
2376
tree.add(['old_name', 'old_name/child'],
2377
['parent-id', 'child-id'])
2378
preview = TransformPreview(tree)
2379
self.addCleanup(preview.finalize)
2380
preview.adjust_path('new_name', preview.root,
2381
preview.trans_id_file_id('parent-id'))
2382
preview_tree = preview.get_preview_tree()
2383
self.assertIs(None, preview_tree.path2id('old_name/child'))
2384
self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
2386
def assertMatchingIterEntries(self, tt, specific_file_ids=None):
2387
preview_tree = tt.get_preview_tree()
2388
preview_result = list(preview_tree.iter_entries_by_dir(
2392
actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
2393
self.assertEqual(actual_result, preview_result)
2395
def test_iter_entries_by_dir_new(self):
2396
tree = self.make_branch_and_tree('tree')
2397
tt = TreeTransform(tree)
2398
tt.new_file('new', tt.root, 'contents', 'new-id')
2399
self.assertMatchingIterEntries(tt)
2401
def test_iter_entries_by_dir_deleted(self):
2402
tree = self.make_branch_and_tree('tree')
2403
self.build_tree(['tree/deleted'])
2404
tree.add('deleted', 'deleted-id')
2405
tt = TreeTransform(tree)
2406
tt.delete_contents(tt.trans_id_file_id('deleted-id'))
2407
self.assertMatchingIterEntries(tt)
2409
def test_iter_entries_by_dir_unversioned(self):
2410
tree = self.make_branch_and_tree('tree')
2411
self.build_tree(['tree/removed'])
2412
tree.add('removed', 'removed-id')
2413
tt = TreeTransform(tree)
2414
tt.unversion_file(tt.trans_id_file_id('removed-id'))
2415
self.assertMatchingIterEntries(tt)
2417
def test_iter_entries_by_dir_moved(self):
2418
tree = self.make_branch_and_tree('tree')
2419
self.build_tree(['tree/moved', 'tree/new_parent/'])
2420
tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
2421
tt = TreeTransform(tree)
2422
tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
2423
tt.trans_id_file_id('moved-id'))
2424
self.assertMatchingIterEntries(tt)
2426
def test_iter_entries_by_dir_specific_file_ids(self):
2427
tree = self.make_branch_and_tree('tree')
2428
tree.set_root_id('tree-root-id')
2429
self.build_tree(['tree/parent/', 'tree/parent/child'])
2430
tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
2431
tt = TreeTransform(tree)
2432
self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
2434
def test_symlink_content_summary(self):
2435
self.requireFeature(SymlinkFeature)
2436
preview = self.get_empty_preview()
2437
preview.new_symlink('path', preview.root, 'target', 'path-id')
2438
summary = preview.get_preview_tree().path_content_summary('path')
2439
self.assertEqual(('symlink', None, None, 'target'), summary)
2441
def test_missing_content_summary(self):
2442
preview = self.get_empty_preview()
2443
summary = preview.get_preview_tree().path_content_summary('path')
2444
self.assertEqual(('missing', None, None, None), summary)
2446
def test_deleted_content_summary(self):
2447
tree = self.make_branch_and_tree('tree')
2448
self.build_tree(['tree/path/'])
2450
preview = TransformPreview(tree)
2451
self.addCleanup(preview.finalize)
2452
preview.delete_contents(preview.trans_id_tree_path('path'))
2453
summary = preview.get_preview_tree().path_content_summary('path')
2454
self.assertEqual(('missing', None, None, None), summary)
2456
def test_file_content_summary_executable(self):
2457
if not osutils.supports_executable():
2458
raise TestNotApplicable()
2459
preview = self.get_empty_preview()
2460
path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2461
preview.set_executability(True, path_id)
2462
summary = preview.get_preview_tree().path_content_summary('path')
2463
self.assertEqual(4, len(summary))
2464
self.assertEqual('file', summary[0])
2465
# size must be known
2466
self.assertEqual(len('contents'), summary[1])
2468
self.assertEqual(True, summary[2])
2469
# will not have hash (not cheap to determine)
2470
self.assertIs(None, summary[3])
2472
def test_change_executability(self):
2473
tree = self.make_branch_and_tree('tree')
2474
self.build_tree(['tree/path'])
2476
preview = TransformPreview(tree)
2477
self.addCleanup(preview.finalize)
2478
path_id = preview.trans_id_tree_path('path')
2479
preview.set_executability(True, path_id)
2480
summary = preview.get_preview_tree().path_content_summary('path')
2481
self.assertEqual(True, summary[2])
2483
def test_file_content_summary_non_exec(self):
2484
preview = self.get_empty_preview()
2485
preview.new_file('path', preview.root, 'contents', 'path-id')
2486
summary = preview.get_preview_tree().path_content_summary('path')
2487
self.assertEqual(4, len(summary))
2488
self.assertEqual('file', summary[0])
2489
# size must be known
2490
self.assertEqual(len('contents'), summary[1])
2492
self.assertEqual(False, summary[2])
2493
# will not have hash (not cheap to determine)
2494
self.assertIs(None, summary[3])
2496
def test_dir_content_summary(self):
2497
preview = self.get_empty_preview()
2498
preview.new_directory('path', preview.root, 'path-id')
2499
summary = preview.get_preview_tree().path_content_summary('path')
2500
self.assertEqual(('directory', None, None, None), summary)
2502
def test_tree_content_summary(self):
2503
preview = self.get_empty_preview()
2504
path = preview.new_directory('path', preview.root, 'path-id')
2505
preview.set_tree_reference('rev-1', path)
2506
summary = preview.get_preview_tree().path_content_summary('path')
2507
self.assertEqual(4, len(summary))
2508
self.assertEqual('tree-reference', summary[0])
2510
def test_annotate(self):
2511
tree = self.make_branch_and_tree('tree')
2512
self.build_tree_contents([('tree/file', 'a\n')])
2513
tree.add('file', 'file-id')
2514
tree.commit('a', rev_id='one')
2515
self.build_tree_contents([('tree/file', 'a\nb\n')])
2516
preview = TransformPreview(tree)
2517
self.addCleanup(preview.finalize)
2518
file_trans_id = preview.trans_id_file_id('file-id')
2519
preview.delete_contents(file_trans_id)
2520
preview.create_file('a\nb\nc\n', file_trans_id)
2521
preview_tree = preview.get_preview_tree()
2527
annotation = preview_tree.annotate_iter('file-id', 'me:')
2528
self.assertEqual(expected, annotation)
2530
def test_annotate_missing(self):
2531
preview = self.get_empty_preview()
2532
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2533
preview_tree = preview.get_preview_tree()
2539
annotation = preview_tree.annotate_iter('file-id', 'me:')
2540
self.assertEqual(expected, annotation)
2542
def test_annotate_rename(self):
2543
tree = self.make_branch_and_tree('tree')
2544
self.build_tree_contents([('tree/file', 'a\n')])
2545
tree.add('file', 'file-id')
2546
tree.commit('a', rev_id='one')
2547
preview = TransformPreview(tree)
2548
self.addCleanup(preview.finalize)
2549
file_trans_id = preview.trans_id_file_id('file-id')
2550
preview.adjust_path('newname', preview.root, file_trans_id)
2551
preview_tree = preview.get_preview_tree()
2555
annotation = preview_tree.annotate_iter('file-id', 'me:')
2556
self.assertEqual(expected, annotation)
2558
def test_annotate_deleted(self):
2559
tree = self.make_branch_and_tree('tree')
2560
self.build_tree_contents([('tree/file', 'a\n')])
2561
tree.add('file', 'file-id')
2562
tree.commit('a', rev_id='one')
2563
self.build_tree_contents([('tree/file', 'a\nb\n')])
2564
preview = TransformPreview(tree)
2565
self.addCleanup(preview.finalize)
2566
file_trans_id = preview.trans_id_file_id('file-id')
2567
preview.delete_contents(file_trans_id)
2568
preview_tree = preview.get_preview_tree()
2569
annotation = preview_tree.annotate_iter('file-id', 'me:')
2570
self.assertIs(None, annotation)
2572
def test_stored_kind(self):
2573
preview = self.get_empty_preview()
2574
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2575
preview_tree = preview.get_preview_tree()
2576
self.assertEqual('file', preview_tree.stored_kind('file-id'))
2578
def test_is_executable(self):
2579
preview = self.get_empty_preview()
2580
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2581
preview.set_executability(True, preview.trans_id_file_id('file-id'))
2582
preview_tree = preview.get_preview_tree()
2583
self.assertEqual(True, preview_tree.is_executable('file-id'))
2585
def test_get_set_parent_ids(self):
2586
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2587
self.assertEqual([], preview_tree.get_parent_ids())
2588
preview_tree.set_parent_ids(['rev-1'])
2589
self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
2591
def test_plan_file_merge(self):
2592
work_a = self.make_branch_and_tree('wta')
2593
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2594
work_a.add('file', 'file-id')
2595
base_id = work_a.commit('base version')
2596
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2597
preview = TransformPreview(work_a)
2598
self.addCleanup(preview.finalize)
2599
trans_id = preview.trans_id_file_id('file-id')
2600
preview.delete_contents(trans_id)
2601
preview.create_file('b\nc\nd\ne\n', trans_id)
2602
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2603
tree_a = preview.get_preview_tree()
2604
tree_a.set_parent_ids([base_id])
2606
('killed-a', 'a\n'),
2607
('killed-b', 'b\n'),
2608
('unchanged', 'c\n'),
2609
('unchanged', 'd\n'),
2612
], list(tree_a.plan_file_merge('file-id', tree_b)))
2614
def test_plan_file_merge_revision_tree(self):
2615
work_a = self.make_branch_and_tree('wta')
2616
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2617
work_a.add('file', 'file-id')
2618
base_id = work_a.commit('base version')
2619
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2620
preview = TransformPreview(work_a.basis_tree())
2621
self.addCleanup(preview.finalize)
2622
trans_id = preview.trans_id_file_id('file-id')
2623
preview.delete_contents(trans_id)
2624
preview.create_file('b\nc\nd\ne\n', trans_id)
2625
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2626
tree_a = preview.get_preview_tree()
2627
tree_a.set_parent_ids([base_id])
2629
('killed-a', 'a\n'),
2630
('killed-b', 'b\n'),
2631
('unchanged', 'c\n'),
2632
('unchanged', 'd\n'),
2635
], list(tree_a.plan_file_merge('file-id', tree_b)))
2637
def test_walkdirs(self):
2638
preview = self.get_empty_preview()
2639
root = preview.new_directory('', ROOT_PARENT, 'tree-root')
2640
# FIXME: new_directory should mark root.
2641
preview.adjust_path('', ROOT_PARENT, root)
2642
preview_tree = preview.get_preview_tree()
2643
file_trans_id = preview.new_file('a', preview.root, 'contents',
2645
expected = [(('', 'tree-root'),
2646
[('a', 'a', 'file', None, 'a-id', 'file')])]
2647
self.assertEqual(expected, list(preview_tree.walkdirs()))
2649
def test_extras(self):
2650
work_tree = self.make_branch_and_tree('tree')
2651
self.build_tree(['tree/removed-file', 'tree/existing-file',
2652
'tree/not-removed-file'])
2653
work_tree.add(['removed-file', 'not-removed-file'])
2654
preview = TransformPreview(work_tree)
2655
self.addCleanup(preview.finalize)
2656
preview.new_file('new-file', preview.root, 'contents')
2657
preview.new_file('new-versioned-file', preview.root, 'contents',
2659
tree = preview.get_preview_tree()
2660
preview.unversion_file(preview.trans_id_tree_path('removed-file'))
2661
self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
2664
def test_merge_into_preview(self):
2665
work_tree = self.make_branch_and_tree('tree')
2666
self.build_tree_contents([('tree/file','b\n')])
2667
work_tree.add('file', 'file-id')
2668
work_tree.commit('first commit')
2669
child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
2670
self.build_tree_contents([('child/file','b\nc\n')])
2671
child_tree.commit('child commit')
2672
child_tree.lock_write()
2673
self.addCleanup(child_tree.unlock)
2674
work_tree.lock_write()
2675
self.addCleanup(work_tree.unlock)
2676
preview = TransformPreview(work_tree)
2677
self.addCleanup(preview.finalize)
2678
file_trans_id = preview.trans_id_file_id('file-id')
2679
preview.delete_contents(file_trans_id)
2680
preview.create_file('a\nb\n', file_trans_id)
2681
pb = progress.DummyProgress()
2682
preview_tree = preview.get_preview_tree()
2683
merger = Merger.from_revision_ids(pb, preview_tree,
2684
child_tree.branch.last_revision(),
2685
other_branch=child_tree.branch,
2686
tree_branch=work_tree.branch)
2687
merger.merge_type = Merge3Merger
2688
tt = merger.make_merger().make_preview_transform()
2689
self.addCleanup(tt.finalize)
2690
final_tree = tt.get_preview_tree()
2691
self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
2693
def test_merge_preview_into_workingtree(self):
2694
tree = self.make_branch_and_tree('tree')
2695
tree.set_root_id('TREE_ROOT')
2696
tt = TransformPreview(tree)
2697
self.addCleanup(tt.finalize)
2698
tt.new_file('name', tt.root, 'content', 'file-id')
2699
tree2 = self.make_branch_and_tree('tree2')
2700
tree2.set_root_id('TREE_ROOT')
2701
pb = progress.DummyProgress()
2702
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2703
pb, tree.basis_tree())
2704
merger.merge_type = Merge3Merger
2707
def test_merge_preview_into_workingtree_handles_conflicts(self):
2708
tree = self.make_branch_and_tree('tree')
2709
self.build_tree_contents([('tree/foo', 'bar')])
2710
tree.add('foo', 'foo-id')
2712
tt = TransformPreview(tree)
2713
self.addCleanup(tt.finalize)
2714
trans_id = tt.trans_id_file_id('foo-id')
2715
tt.delete_contents(trans_id)
2716
tt.create_file('baz', trans_id)
2717
tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2718
self.build_tree_contents([('tree2/foo', 'qux')])
2719
pb = progress.DummyProgress()
2720
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2721
pb, tree.basis_tree())
2722
merger.merge_type = Merge3Merger
2725
def test_is_executable(self):
2726
tree = self.make_branch_and_tree('tree')
2727
preview = TransformPreview(tree)
2728
self.addCleanup(preview.finalize)
2729
preview.new_file('foo', preview.root, 'bar', 'baz-id')
2730
preview_tree = preview.get_preview_tree()
2731
self.assertEqual(False, preview_tree.is_executable('baz-id',
2733
self.assertEqual(False, preview_tree.is_executable('baz-id'))
2735
def test_commit_preview_tree(self):
2736
tree = self.make_branch_and_tree('tree')
2737
rev_id = tree.commit('rev1')
2738
tree.branch.lock_write()
2739
self.addCleanup(tree.branch.unlock)
2740
tt = TransformPreview(tree)
2741
tt.new_file('file', tt.root, 'contents', 'file_id')
2742
self.addCleanup(tt.finalize)
2743
preview = tt.get_preview_tree()
2744
preview.set_parent_ids([rev_id])
2745
builder = tree.branch.get_commit_builder([rev_id])
2746
list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
2747
builder.finish_inventory()
2748
rev2_id = builder.commit('rev2')
2749
rev2_tree = tree.branch.repository.revision_tree(rev2_id)
2750
self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
2752
def test_ascii_limbo_paths(self):
2753
self.requireFeature(tests.UnicodeFilenameFeature)
2754
branch = self.make_branch('any')
2755
tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
2756
tt = TransformPreview(tree)
2757
foo_id = tt.new_directory('', ROOT_PARENT)
2758
bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
2759
limbo_path = tt._limbo_name(bar_id)
2760
self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
2763
class FakeSerializer(object):
2764
"""Serializer implementation that simply returns the input.
2766
The input is returned in the order used by pack.ContainerPushParser.
2769
def bytes_record(bytes, names):
2773
class TestSerializeTransform(tests.TestCaseWithTransport):
2775
_test_needs_features = [tests.UnicodeFilenameFeature]
2777
def get_preview(self, tree=None):
2779
tree = self.make_branch_and_tree('tree')
2780
tt = TransformPreview(tree)
2781
self.addCleanup(tt.finalize)
2784
def assertSerializesTo(self, expected, tt):
2785
records = list(tt.serialize(FakeSerializer()))
2786
self.assertEqual(expected, records)
2789
def default_attribs():
2794
'_new_executability': {},
2796
'_tree_path_ids': {'': 'new-0'},
2798
'_removed_contents': [],
2799
'_non_present_ids': {},
2802
def make_records(self, attribs, contents):
2804
(((('attribs'),),), bencode.bencode(attribs))]
2805
records.extend([(((n, k),), c) for n, k, c in contents])
2808
def creation_records(self):
2809
attribs = self.default_attribs()
2810
attribs['_id_number'] = 3
2811
attribs['_new_name'] = {
2812
'new-1': u'foo\u1234'.encode('utf-8'), 'new-2': 'qux'}
2813
attribs['_new_id'] = {'new-1': 'baz', 'new-2': 'quxx'}
2814
attribs['_new_parent'] = {'new-1': 'new-0', 'new-2': 'new-0'}
2815
attribs['_new_executability'] = {'new-1': 1}
2817
('new-1', 'file', 'i 1\nbar\n'),
2818
('new-2', 'directory', ''),
2820
return self.make_records(attribs, contents)
2822
def test_serialize_creation(self):
2823
tt = self.get_preview()
2824
tt.new_file(u'foo\u1234', tt.root, 'bar', 'baz', True)
2825
tt.new_directory('qux', tt.root, 'quxx')
2826
self.assertSerializesTo(self.creation_records(), tt)
2828
def test_deserialize_creation(self):
2829
tt = self.get_preview()
2830
tt.deserialize(iter(self.creation_records()))
2831
self.assertEqual(3, tt._id_number)
2832
self.assertEqual({'new-1': u'foo\u1234',
2833
'new-2': 'qux'}, tt._new_name)
2834
self.assertEqual({'new-1': 'baz', 'new-2': 'quxx'}, tt._new_id)
2835
self.assertEqual({'new-1': tt.root, 'new-2': tt.root}, tt._new_parent)
2836
self.assertEqual({'baz': 'new-1', 'quxx': 'new-2'}, tt._r_new_id)
2837
self.assertEqual({'new-1': True}, tt._new_executability)
2838
self.assertEqual({'new-1': 'file',
2839
'new-2': 'directory'}, tt._new_contents)
2840
foo_limbo = open(tt._limbo_name('new-1'), 'rb')
2842
foo_content = foo_limbo.read()
2845
self.assertEqual('bar', foo_content)
2847
def symlink_creation_records(self):
2848
attribs = self.default_attribs()
2849
attribs['_id_number'] = 2
2850
attribs['_new_name'] = {'new-1': u'foo\u1234'.encode('utf-8')}
2851
attribs['_new_parent'] = {'new-1': 'new-0'}
2852
contents = [('new-1', 'symlink', u'bar\u1234'.encode('utf-8'))]
2853
return self.make_records(attribs, contents)
2855
def test_serialize_symlink_creation(self):
2856
self.requireFeature(tests.SymlinkFeature)
2857
tt = self.get_preview()
2858
tt.new_symlink(u'foo\u1234', tt.root, u'bar\u1234')
2859
self.assertSerializesTo(self.symlink_creation_records(), tt)
2861
def test_deserialize_symlink_creation(self):
2862
self.requireFeature(tests.SymlinkFeature)
2863
tt = self.get_preview()
2864
tt.deserialize(iter(self.symlink_creation_records()))
2865
abspath = tt._limbo_name('new-1')
2866
foo_content = osutils.readlink(abspath)
2867
self.assertEqual(u'bar\u1234', foo_content)
2869
def make_destruction_preview(self):
2870
tree = self.make_branch_and_tree('.')
2871
self.build_tree([u'foo\u1234', 'bar'])
2872
tree.add([u'foo\u1234', 'bar'], ['foo-id', 'bar-id'])
2873
return self.get_preview(tree)
2875
def destruction_records(self):
2876
attribs = self.default_attribs()
2877
attribs['_id_number'] = 3
2878
attribs['_removed_id'] = ['new-1']
2879
attribs['_removed_contents'] = ['new-2']
2880
attribs['_tree_path_ids'] = {
2882
u'foo\u1234'.encode('utf-8'): 'new-1',
2885
return self.make_records(attribs, [])
2887
def test_serialize_destruction(self):
2888
tt = self.make_destruction_preview()
2889
foo_trans_id = tt.trans_id_tree_file_id('foo-id')
2890
tt.unversion_file(foo_trans_id)
2891
bar_trans_id = tt.trans_id_tree_file_id('bar-id')
2892
tt.delete_contents(bar_trans_id)
2893
self.assertSerializesTo(self.destruction_records(), tt)
2895
def test_deserialize_destruction(self):
2896
tt = self.make_destruction_preview()
2897
tt.deserialize(iter(self.destruction_records()))
2898
self.assertEqual({u'foo\u1234': 'new-1',
2900
'': tt.root}, tt._tree_path_ids)
2901
self.assertEqual({'new-1': u'foo\u1234',
2903
tt.root: ''}, tt._tree_id_paths)
2904
self.assertEqual(set(['new-1']), tt._removed_id)
2905
self.assertEqual(set(['new-2']), tt._removed_contents)
2907
def missing_records(self):
2908
attribs = self.default_attribs()
2909
attribs['_id_number'] = 2
2910
attribs['_non_present_ids'] = {
2912
return self.make_records(attribs, [])
2914
def test_serialize_missing(self):
2915
tt = self.get_preview()
2916
boo_trans_id = tt.trans_id_file_id('boo')
2917
self.assertSerializesTo(self.missing_records(), tt)
2919
def test_deserialize_missing(self):
2920
tt = self.get_preview()
2921
tt.deserialize(iter(self.missing_records()))
2922
self.assertEqual({'boo': 'new-1'}, tt._non_present_ids)
2924
def make_modification_preview(self):
2925
LINES_ONE = 'aa\nbb\ncc\ndd\n'
2926
LINES_TWO = 'z\nbb\nx\ndd\n'
2927
tree = self.make_branch_and_tree('tree')
2928
self.build_tree_contents([('tree/file', LINES_ONE)])
2929
tree.add('file', 'file-id')
2930
return self.get_preview(tree), LINES_TWO
2932
def modification_records(self):
2933
attribs = self.default_attribs()
2934
attribs['_id_number'] = 2
2935
attribs['_tree_path_ids'] = {
2938
attribs['_removed_contents'] = ['new-1']
2939
contents = [('new-1', 'file',
2940
'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
2941
return self.make_records(attribs, contents)
2943
def test_serialize_modification(self):
2944
tt, LINES = self.make_modification_preview()
2945
trans_id = tt.trans_id_file_id('file-id')
2946
tt.delete_contents(trans_id)
2947
tt.create_file(LINES, trans_id)
2948
self.assertSerializesTo(self.modification_records(), tt)
2950
def test_deserialize_modification(self):
2951
tt, LINES = self.make_modification_preview()
2952
tt.deserialize(iter(self.modification_records()))
2953
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2955
def make_kind_change_preview(self):
2956
LINES = 'a\nb\nc\nd\n'
2957
tree = self.make_branch_and_tree('tree')
2958
self.build_tree(['tree/foo/'])
2959
tree.add('foo', 'foo-id')
2960
return self.get_preview(tree), LINES
2962
def kind_change_records(self):
2963
attribs = self.default_attribs()
2964
attribs['_id_number'] = 2
2965
attribs['_tree_path_ids'] = {
2968
attribs['_removed_contents'] = ['new-1']
2969
contents = [('new-1', 'file',
2970
'i 4\na\nb\nc\nd\n\n')]
2971
return self.make_records(attribs, contents)
2973
def test_serialize_kind_change(self):
2974
tt, LINES = self.make_kind_change_preview()
2975
trans_id = tt.trans_id_file_id('foo-id')
2976
tt.delete_contents(trans_id)
2977
tt.create_file(LINES, trans_id)
2978
self.assertSerializesTo(self.kind_change_records(), tt)
2980
def test_deserialize_kind_change(self):
2981
tt, LINES = self.make_kind_change_preview()
2982
tt.deserialize(iter(self.kind_change_records()))
2983
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2985
def make_add_contents_preview(self):
2986
LINES = 'a\nb\nc\nd\n'
2987
tree = self.make_branch_and_tree('tree')
2988
self.build_tree(['tree/foo'])
2990
os.unlink('tree/foo')
2991
return self.get_preview(tree), LINES
2993
def add_contents_records(self):
2994
attribs = self.default_attribs()
2995
attribs['_id_number'] = 2
2996
attribs['_tree_path_ids'] = {
2999
contents = [('new-1', 'file',
3000
'i 4\na\nb\nc\nd\n\n')]
3001
return self.make_records(attribs, contents)
3003
def test_serialize_add_contents(self):
3004
tt, LINES = self.make_add_contents_preview()
3005
trans_id = tt.trans_id_tree_path('foo')
3006
tt.create_file(LINES, trans_id)
3007
self.assertSerializesTo(self.add_contents_records(), tt)
3009
def test_deserialize_add_contents(self):
3010
tt, LINES = self.make_add_contents_preview()
3011
tt.deserialize(iter(self.add_contents_records()))
3012
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
3014
def test_get_parents_lines(self):
3015
LINES_ONE = 'aa\nbb\ncc\ndd\n'
3016
LINES_TWO = 'z\nbb\nx\ndd\n'
3017
tree = self.make_branch_and_tree('tree')
3018
self.build_tree_contents([('tree/file', LINES_ONE)])
3019
tree.add('file', 'file-id')
3020
tt = self.get_preview(tree)
3021
trans_id = tt.trans_id_tree_path('file')
3022
self.assertEqual((['aa\n', 'bb\n', 'cc\n', 'dd\n'],),
3023
tt._get_parents_lines(trans_id))
3025
def test_get_parents_texts(self):
3026
LINES_ONE = 'aa\nbb\ncc\ndd\n'
3027
LINES_TWO = 'z\nbb\nx\ndd\n'
3028
tree = self.make_branch_and_tree('tree')
3029
self.build_tree_contents([('tree/file', LINES_ONE)])
3030
tree.add('file', 'file-id')
3031
tt = self.get_preview(tree)
3032
trans_id = tt.trans_id_tree_path('file')
3033
self.assertEqual((LINES_ONE,),
3034
tt._get_parents_texts(trans_id))