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 install_rot13_content_filter(self, pattern):
1872
original_registry = filters._reset_registry()
1873
def restore_registry():
1874
filters._reset_registry(original_registry)
1875
self.addCleanup(restore_registry)
1876
def rot13(chunks, context=None):
1877
return [''.join(chunks).encode('rot13')]
1878
rot13filter = filters.ContentFilter(rot13, rot13)
1879
filters.register_filter_stack_map('rot13', {'yes': [rot13filter]}.get)
1880
os.mkdir(self.test_home_dir + '/.bazaar')
1881
rules_filename = self.test_home_dir + '/.bazaar/rules'
1882
f = open(rules_filename, 'wb')
1883
f.write('[name %s]\nrot13=yes\n' % (pattern,))
1885
def uninstall_rules():
1886
os.remove(rules_filename)
1888
self.addCleanup(uninstall_rules)
1891
def test_build_tree_content_filtered_files_are_not_hardlinked(self):
1892
"""build_tree will not hardlink files that have content filtering rules
1893
applied to them (but will still hardlink other files from the same tree
1896
self.requireFeature(HardlinkFeature)
1897
self.install_rot13_content_filter('file1')
1898
source = self.create_ab_tree()
1899
target = self.make_branch_and_tree('target')
1900
revision_tree = source.basis_tree()
1901
revision_tree.lock_read()
1902
self.addCleanup(revision_tree.unlock)
1903
build_tree(revision_tree, target, source, hardlink=True)
1905
self.addCleanup(target.unlock)
1906
self.assertEqual([], list(target.iter_changes(revision_tree)))
1907
source_stat = os.stat('source/file1')
1908
target_stat = os.stat('target/file1')
1909
self.assertNotEqual(source_stat, target_stat)
1910
source_stat = os.stat('source/file2')
1911
target_stat = os.stat('target/file2')
1912
self.assertEqualStat(source_stat, target_stat)
1914
def test_case_insensitive_build_tree_inventory(self):
1915
if (tests.CaseInsensitiveFilesystemFeature.available()
1916
or tests.CaseInsCasePresFilenameFeature.available()):
1917
raise tests.UnavailableFeature('Fully case sensitive filesystem')
1918
source = self.make_branch_and_tree('source')
1919
self.build_tree(['source/file', 'source/FILE'])
1920
source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1921
source.commit('added files')
1922
# Don't try this at home, kids!
1923
# Force the tree to report that it is case insensitive
1924
target = self.make_branch_and_tree('target')
1925
target.case_sensitive = False
1926
build_tree(source.basis_tree(), target, source, delta_from_tree=True)
1927
self.assertEqual('file.moved', target.id2path('lower-id'))
1928
self.assertEqual('FILE', target.id2path('upper-id'))
1931
class TestCommitTransform(tests.TestCaseWithTransport):
1933
def get_branch(self):
1934
tree = self.make_branch_and_tree('tree')
1936
self.addCleanup(tree.unlock)
1937
tree.commit('empty commit')
1940
def get_branch_and_transform(self):
1941
branch = self.get_branch()
1942
tt = TransformPreview(branch.basis_tree())
1943
self.addCleanup(tt.finalize)
1946
def test_commit_wrong_basis(self):
1947
branch = self.get_branch()
1948
basis = branch.repository.revision_tree(
1949
_mod_revision.NULL_REVISION)
1950
tt = TransformPreview(basis)
1951
self.addCleanup(tt.finalize)
1952
e = self.assertRaises(ValueError, tt.commit, branch, '')
1953
self.assertEqual('TreeTransform not based on branch basis: null:',
1956
def test_empy_commit(self):
1957
branch, tt = self.get_branch_and_transform()
1958
rev = tt.commit(branch, 'my message')
1959
self.assertEqual(2, branch.revno())
1960
repo = branch.repository
1961
self.assertEqual('my message', repo.get_revision(rev).message)
1963
def test_merge_parents(self):
1964
branch, tt = self.get_branch_and_transform()
1965
rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
1966
self.assertEqual(['rev1b', 'rev1c'],
1967
branch.basis_tree().get_parent_ids()[1:])
1969
def test_first_commit(self):
1970
branch = self.make_branch('branch')
1972
self.addCleanup(branch.unlock)
1973
tt = TransformPreview(branch.basis_tree())
1974
self.addCleanup(tt.finalize)
1975
tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
1976
rev = tt.commit(branch, 'my message')
1977
self.assertEqual([], branch.basis_tree().get_parent_ids())
1978
self.assertNotEqual(_mod_revision.NULL_REVISION,
1979
branch.last_revision())
1981
def test_first_commit_with_merge_parents(self):
1982
branch = self.make_branch('branch')
1984
self.addCleanup(branch.unlock)
1985
tt = TransformPreview(branch.basis_tree())
1986
self.addCleanup(tt.finalize)
1987
e = self.assertRaises(ValueError, tt.commit, branch,
1988
'my message', ['rev1b-id'])
1989
self.assertEqual('Cannot supply merge parents for first commit.',
1991
self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
1993
def test_add_files(self):
1994
branch, tt = self.get_branch_and_transform()
1995
tt.new_file('file', tt.root, 'contents', 'file-id')
1996
trans_id = tt.new_directory('dir', tt.root, 'dir-id')
1997
if SymlinkFeature.available():
1998
tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
1999
rev = tt.commit(branch, 'message')
2000
tree = branch.basis_tree()
2001
self.assertEqual('file', tree.id2path('file-id'))
2002
self.assertEqual('contents', tree.get_file_text('file-id'))
2003
self.assertEqual('dir', tree.id2path('dir-id'))
2004
if SymlinkFeature.available():
2005
self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
2006
self.assertEqual('target', tree.get_symlink_target('symlink-id'))
2008
def test_add_unversioned(self):
2009
branch, tt = self.get_branch_and_transform()
2010
tt.new_file('file', tt.root, 'contents')
2011
self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
2012
'message', strict=True)
2014
def test_modify_strict(self):
2015
branch, tt = self.get_branch_and_transform()
2016
tt.new_file('file', tt.root, 'contents', 'file-id')
2017
tt.commit(branch, 'message', strict=True)
2018
tt = TransformPreview(branch.basis_tree())
2019
self.addCleanup(tt.finalize)
2020
trans_id = tt.trans_id_file_id('file-id')
2021
tt.delete_contents(trans_id)
2022
tt.create_file('contents', trans_id)
2023
tt.commit(branch, 'message', strict=True)
2025
def test_commit_malformed(self):
2026
"""Committing a malformed transform should raise an exception.
2028
In this case, we are adding a file without adding its parent.
2030
branch, tt = self.get_branch_and_transform()
2031
parent_id = tt.trans_id_file_id('parent-id')
2032
tt.new_file('file', parent_id, 'contents', 'file-id')
2033
self.assertRaises(errors.MalformedTransform, tt.commit, branch,
706
2037
class MockTransform(object):
708
2039
def has_named_child(self, by_parent, parent_id, name):
732
2065
self.assertEqual(name, 'name.~1~')
733
2066
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
734
2067
self.assertEqual(name, 'name.~4~')
2070
class TestFileMover(tests.TestCaseWithTransport):
2072
def test_file_mover(self):
2073
self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
2074
mover = _FileMover()
2075
mover.rename('a', 'q')
2076
self.failUnlessExists('q')
2077
self.failIfExists('a')
2078
self.failUnlessExists('q/b')
2079
self.failUnlessExists('c')
2080
self.failUnlessExists('c/d')
2082
def test_pre_delete_rollback(self):
2083
self.build_tree(['a/'])
2084
mover = _FileMover()
2085
mover.pre_delete('a', 'q')
2086
self.failUnlessExists('q')
2087
self.failIfExists('a')
2089
self.failIfExists('q')
2090
self.failUnlessExists('a')
2092
def test_apply_deletions(self):
2093
self.build_tree(['a/', 'b/'])
2094
mover = _FileMover()
2095
mover.pre_delete('a', 'q')
2096
mover.pre_delete('b', 'r')
2097
self.failUnlessExists('q')
2098
self.failUnlessExists('r')
2099
self.failIfExists('a')
2100
self.failIfExists('b')
2101
mover.apply_deletions()
2102
self.failIfExists('q')
2103
self.failIfExists('r')
2104
self.failIfExists('a')
2105
self.failIfExists('b')
2107
def test_file_mover_rollback(self):
2108
self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
2109
mover = _FileMover()
2110
mover.rename('c/d', 'c/f')
2111
mover.rename('c/e', 'c/d')
2113
mover.rename('a', 'c')
2114
except errors.FileExists, e:
2116
self.failUnlessExists('a')
2117
self.failUnlessExists('c/d')
2120
class Bogus(Exception):
2124
class TestTransformRollback(tests.TestCaseWithTransport):
2126
class ExceptionFileMover(_FileMover):
2128
def __init__(self, bad_source=None, bad_target=None):
2129
_FileMover.__init__(self)
2130
self.bad_source = bad_source
2131
self.bad_target = bad_target
2133
def rename(self, source, target):
2134
if (self.bad_source is not None and
2135
source.endswith(self.bad_source)):
2137
elif (self.bad_target is not None and
2138
target.endswith(self.bad_target)):
2141
_FileMover.rename(self, source, target)
2143
def test_rollback_rename(self):
2144
tree = self.make_branch_and_tree('.')
2145
self.build_tree(['a/', 'a/b'])
2146
tt = TreeTransform(tree)
2147
self.addCleanup(tt.finalize)
2148
a_id = tt.trans_id_tree_path('a')
2149
tt.adjust_path('c', tt.root, a_id)
2150
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2151
self.assertRaises(Bogus, tt.apply,
2152
_mover=self.ExceptionFileMover(bad_source='a'))
2153
self.failUnlessExists('a')
2154
self.failUnlessExists('a/b')
2156
self.failUnlessExists('c')
2157
self.failUnlessExists('c/d')
2159
def test_rollback_rename_into_place(self):
2160
tree = self.make_branch_and_tree('.')
2161
self.build_tree(['a/', 'a/b'])
2162
tt = TreeTransform(tree)
2163
self.addCleanup(tt.finalize)
2164
a_id = tt.trans_id_tree_path('a')
2165
tt.adjust_path('c', tt.root, a_id)
2166
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2167
self.assertRaises(Bogus, tt.apply,
2168
_mover=self.ExceptionFileMover(bad_target='c/d'))
2169
self.failUnlessExists('a')
2170
self.failUnlessExists('a/b')
2172
self.failUnlessExists('c')
2173
self.failUnlessExists('c/d')
2175
def test_rollback_deletion(self):
2176
tree = self.make_branch_and_tree('.')
2177
self.build_tree(['a/', 'a/b'])
2178
tt = TreeTransform(tree)
2179
self.addCleanup(tt.finalize)
2180
a_id = tt.trans_id_tree_path('a')
2181
tt.delete_contents(a_id)
2182
tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
2183
self.assertRaises(Bogus, tt.apply,
2184
_mover=self.ExceptionFileMover(bad_target='d'))
2185
self.failUnlessExists('a')
2186
self.failUnlessExists('a/b')
2188
def test_resolve_no_parent(self):
2189
wt = self.make_branch_and_tree('.')
2190
tt = TreeTransform(wt)
2191
self.addCleanup(tt.finalize)
2192
parent = tt.trans_id_file_id('parent-id')
2193
tt.new_file('file', parent, 'Contents')
2194
resolve_conflicts(tt)
2197
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
2198
('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
2200
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
2201
('', ''), ('directory', 'directory'), (False, None))
2204
class TestTransformPreview(tests.TestCaseWithTransport):
2206
def create_tree(self):
2207
tree = self.make_branch_and_tree('.')
2208
self.build_tree_contents([('a', 'content 1')])
2209
tree.set_root_id('TREE_ROOT')
2210
tree.add('a', 'a-id')
2211
tree.commit('rev1', rev_id='rev1')
2212
return tree.branch.repository.revision_tree('rev1')
2214
def get_empty_preview(self):
2215
repository = self.make_repository('repo')
2216
tree = repository.revision_tree(_mod_revision.NULL_REVISION)
2217
preview = TransformPreview(tree)
2218
self.addCleanup(preview.finalize)
2221
def test_transform_preview(self):
2222
revision_tree = self.create_tree()
2223
preview = TransformPreview(revision_tree)
2224
self.addCleanup(preview.finalize)
2226
def test_transform_preview_tree(self):
2227
revision_tree = self.create_tree()
2228
preview = TransformPreview(revision_tree)
2229
self.addCleanup(preview.finalize)
2230
preview.get_preview_tree()
2232
def test_transform_new_file(self):
2233
revision_tree = self.create_tree()
2234
preview = TransformPreview(revision_tree)
2235
self.addCleanup(preview.finalize)
2236
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2237
preview_tree = preview.get_preview_tree()
2238
self.assertEqual(preview_tree.kind('file2-id'), 'file')
2240
preview_tree.get_file('file2-id').read(), 'content B\n')
2242
def test_diff_preview_tree(self):
2243
revision_tree = self.create_tree()
2244
preview = TransformPreview(revision_tree)
2245
self.addCleanup(preview.finalize)
2246
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2247
preview_tree = preview.get_preview_tree()
2249
show_diff_trees(revision_tree, preview_tree, out)
2250
lines = out.getvalue().splitlines()
2251
self.assertEqual(lines[0], "=== added file 'file2'")
2252
# 3 lines of diff administrivia
2253
self.assertEqual(lines[4], "+content B")
2255
def test_transform_conflicts(self):
2256
revision_tree = self.create_tree()
2257
preview = TransformPreview(revision_tree)
2258
self.addCleanup(preview.finalize)
2259
preview.new_file('a', preview.root, 'content 2')
2260
resolve_conflicts(preview)
2261
trans_id = preview.trans_id_file_id('a-id')
2262
self.assertEqual('a.moved', preview.final_name(trans_id))
2264
def get_tree_and_preview_tree(self):
2265
revision_tree = self.create_tree()
2266
preview = TransformPreview(revision_tree)
2267
self.addCleanup(preview.finalize)
2268
a_trans_id = preview.trans_id_file_id('a-id')
2269
preview.delete_contents(a_trans_id)
2270
preview.create_file('b content', a_trans_id)
2271
preview_tree = preview.get_preview_tree()
2272
return revision_tree, preview_tree
2274
def test_iter_changes(self):
2275
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2276
root = revision_tree.inventory.root.file_id
2277
self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
2278
(root, root), ('a', 'a'), ('file', 'file'),
2280
list(preview_tree.iter_changes(revision_tree)))
2282
def test_include_unchanged_succeeds(self):
2283
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2284
changes = preview_tree.iter_changes(revision_tree,
2285
include_unchanged=True)
2286
root = revision_tree.inventory.root.file_id
2288
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2290
def test_specific_files(self):
2291
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2292
changes = preview_tree.iter_changes(revision_tree,
2293
specific_files=[''])
2294
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2296
def test_want_unversioned(self):
2297
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2298
changes = preview_tree.iter_changes(revision_tree,
2299
want_unversioned=True)
2300
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2302
def test_ignore_extra_trees_no_specific_files(self):
2303
# extra_trees is harmless without specific_files, so we'll silently
2304
# accept it, even though we won't use it.
2305
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2306
preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
2308
def test_ignore_require_versioned_no_specific_files(self):
2309
# require_versioned is meaningless without specific_files.
2310
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2311
preview_tree.iter_changes(revision_tree, require_versioned=False)
2313
def test_ignore_pb(self):
2314
# pb could be supported, but TT.iter_changes doesn't support it.
2315
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2316
preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
2318
def test_kind(self):
2319
revision_tree = self.create_tree()
2320
preview = TransformPreview(revision_tree)
2321
self.addCleanup(preview.finalize)
2322
preview.new_file('file', preview.root, 'contents', 'file-id')
2323
preview.new_directory('directory', preview.root, 'dir-id')
2324
preview_tree = preview.get_preview_tree()
2325
self.assertEqual('file', preview_tree.kind('file-id'))
2326
self.assertEqual('directory', preview_tree.kind('dir-id'))
2328
def test_get_file_mtime(self):
2329
preview = self.get_empty_preview()
2330
file_trans_id = preview.new_file('file', preview.root, 'contents',
2332
limbo_path = preview._limbo_name(file_trans_id)
2333
preview_tree = preview.get_preview_tree()
2334
self.assertEqual(os.stat(limbo_path).st_mtime,
2335
preview_tree.get_file_mtime('file-id'))
2337
def test_get_file_mtime_renamed(self):
2338
work_tree = self.make_branch_and_tree('tree')
2339
self.build_tree(['tree/file'])
2340
work_tree.add('file', 'file-id')
2341
preview = TransformPreview(work_tree)
2342
self.addCleanup(preview.finalize)
2343
file_trans_id = preview.trans_id_tree_file_id('file-id')
2344
preview.adjust_path('renamed', preview.root, file_trans_id)
2345
preview_tree = preview.get_preview_tree()
2346
preview_mtime = preview_tree.get_file_mtime('file-id', 'renamed')
2347
work_mtime = work_tree.get_file_mtime('file-id', 'file')
2349
def test_get_file(self):
2350
preview = self.get_empty_preview()
2351
preview.new_file('file', preview.root, 'contents', 'file-id')
2352
preview_tree = preview.get_preview_tree()
2353
tree_file = preview_tree.get_file('file-id')
2355
self.assertEqual('contents', tree_file.read())
2359
def test_get_symlink_target(self):
2360
self.requireFeature(SymlinkFeature)
2361
preview = self.get_empty_preview()
2362
preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
2363
preview_tree = preview.get_preview_tree()
2364
self.assertEqual('target',
2365
preview_tree.get_symlink_target('symlink-id'))
2367
def test_all_file_ids(self):
2368
tree = self.make_branch_and_tree('tree')
2369
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
2370
tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
2371
preview = TransformPreview(tree)
2372
self.addCleanup(preview.finalize)
2373
preview.unversion_file(preview.trans_id_file_id('b-id'))
2374
c_trans_id = preview.trans_id_file_id('c-id')
2375
preview.unversion_file(c_trans_id)
2376
preview.version_file('c-id', c_trans_id)
2377
preview_tree = preview.get_preview_tree()
2378
self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
2379
preview_tree.all_file_ids())
2381
def test_path2id_deleted_unchanged(self):
2382
tree = self.make_branch_and_tree('tree')
2383
self.build_tree(['tree/unchanged', 'tree/deleted'])
2384
tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
2385
preview = TransformPreview(tree)
2386
self.addCleanup(preview.finalize)
2387
preview.unversion_file(preview.trans_id_file_id('deleted-id'))
2388
preview_tree = preview.get_preview_tree()
2389
self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
2390
self.assertIs(None, preview_tree.path2id('deleted'))
2392
def test_path2id_created(self):
2393
tree = self.make_branch_and_tree('tree')
2394
self.build_tree(['tree/unchanged'])
2395
tree.add(['unchanged'], ['unchanged-id'])
2396
preview = TransformPreview(tree)
2397
self.addCleanup(preview.finalize)
2398
preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
2399
'contents', 'new-id')
2400
preview_tree = preview.get_preview_tree()
2401
self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
2403
def test_path2id_moved(self):
2404
tree = self.make_branch_and_tree('tree')
2405
self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
2406
tree.add(['old_parent', 'old_parent/child'],
2407
['old_parent-id', 'child-id'])
2408
preview = TransformPreview(tree)
2409
self.addCleanup(preview.finalize)
2410
new_parent = preview.new_directory('new_parent', preview.root,
2412
preview.adjust_path('child', new_parent,
2413
preview.trans_id_file_id('child-id'))
2414
preview_tree = preview.get_preview_tree()
2415
self.assertIs(None, preview_tree.path2id('old_parent/child'))
2416
self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
2418
def test_path2id_renamed_parent(self):
2419
tree = self.make_branch_and_tree('tree')
2420
self.build_tree(['tree/old_name/', 'tree/old_name/child'])
2421
tree.add(['old_name', 'old_name/child'],
2422
['parent-id', 'child-id'])
2423
preview = TransformPreview(tree)
2424
self.addCleanup(preview.finalize)
2425
preview.adjust_path('new_name', preview.root,
2426
preview.trans_id_file_id('parent-id'))
2427
preview_tree = preview.get_preview_tree()
2428
self.assertIs(None, preview_tree.path2id('old_name/child'))
2429
self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
2431
def assertMatchingIterEntries(self, tt, specific_file_ids=None):
2432
preview_tree = tt.get_preview_tree()
2433
preview_result = list(preview_tree.iter_entries_by_dir(
2437
actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
2438
self.assertEqual(actual_result, preview_result)
2440
def test_iter_entries_by_dir_new(self):
2441
tree = self.make_branch_and_tree('tree')
2442
tt = TreeTransform(tree)
2443
tt.new_file('new', tt.root, 'contents', 'new-id')
2444
self.assertMatchingIterEntries(tt)
2446
def test_iter_entries_by_dir_deleted(self):
2447
tree = self.make_branch_and_tree('tree')
2448
self.build_tree(['tree/deleted'])
2449
tree.add('deleted', 'deleted-id')
2450
tt = TreeTransform(tree)
2451
tt.delete_contents(tt.trans_id_file_id('deleted-id'))
2452
self.assertMatchingIterEntries(tt)
2454
def test_iter_entries_by_dir_unversioned(self):
2455
tree = self.make_branch_and_tree('tree')
2456
self.build_tree(['tree/removed'])
2457
tree.add('removed', 'removed-id')
2458
tt = TreeTransform(tree)
2459
tt.unversion_file(tt.trans_id_file_id('removed-id'))
2460
self.assertMatchingIterEntries(tt)
2462
def test_iter_entries_by_dir_moved(self):
2463
tree = self.make_branch_and_tree('tree')
2464
self.build_tree(['tree/moved', 'tree/new_parent/'])
2465
tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
2466
tt = TreeTransform(tree)
2467
tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
2468
tt.trans_id_file_id('moved-id'))
2469
self.assertMatchingIterEntries(tt)
2471
def test_iter_entries_by_dir_specific_file_ids(self):
2472
tree = self.make_branch_and_tree('tree')
2473
tree.set_root_id('tree-root-id')
2474
self.build_tree(['tree/parent/', 'tree/parent/child'])
2475
tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
2476
tt = TreeTransform(tree)
2477
self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
2479
def test_symlink_content_summary(self):
2480
self.requireFeature(SymlinkFeature)
2481
preview = self.get_empty_preview()
2482
preview.new_symlink('path', preview.root, 'target', 'path-id')
2483
summary = preview.get_preview_tree().path_content_summary('path')
2484
self.assertEqual(('symlink', None, None, 'target'), summary)
2486
def test_missing_content_summary(self):
2487
preview = self.get_empty_preview()
2488
summary = preview.get_preview_tree().path_content_summary('path')
2489
self.assertEqual(('missing', None, None, None), summary)
2491
def test_deleted_content_summary(self):
2492
tree = self.make_branch_and_tree('tree')
2493
self.build_tree(['tree/path/'])
2495
preview = TransformPreview(tree)
2496
self.addCleanup(preview.finalize)
2497
preview.delete_contents(preview.trans_id_tree_path('path'))
2498
summary = preview.get_preview_tree().path_content_summary('path')
2499
self.assertEqual(('missing', None, None, None), summary)
2501
def test_file_content_summary_executable(self):
2502
preview = self.get_empty_preview()
2503
path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2504
preview.set_executability(True, path_id)
2505
summary = preview.get_preview_tree().path_content_summary('path')
2506
self.assertEqual(4, len(summary))
2507
self.assertEqual('file', summary[0])
2508
# size must be known
2509
self.assertEqual(len('contents'), summary[1])
2511
self.assertEqual(True, summary[2])
2512
# will not have hash (not cheap to determine)
2513
self.assertIs(None, summary[3])
2515
def test_change_executability(self):
2516
tree = self.make_branch_and_tree('tree')
2517
self.build_tree(['tree/path'])
2519
preview = TransformPreview(tree)
2520
self.addCleanup(preview.finalize)
2521
path_id = preview.trans_id_tree_path('path')
2522
preview.set_executability(True, path_id)
2523
summary = preview.get_preview_tree().path_content_summary('path')
2524
self.assertEqual(True, summary[2])
2526
def test_file_content_summary_non_exec(self):
2527
preview = self.get_empty_preview()
2528
preview.new_file('path', preview.root, 'contents', 'path-id')
2529
summary = preview.get_preview_tree().path_content_summary('path')
2530
self.assertEqual(4, len(summary))
2531
self.assertEqual('file', summary[0])
2532
# size must be known
2533
self.assertEqual(len('contents'), summary[1])
2535
self.assertEqual(False, summary[2])
2536
# will not have hash (not cheap to determine)
2537
self.assertIs(None, summary[3])
2539
def test_dir_content_summary(self):
2540
preview = self.get_empty_preview()
2541
preview.new_directory('path', preview.root, 'path-id')
2542
summary = preview.get_preview_tree().path_content_summary('path')
2543
self.assertEqual(('directory', None, None, None), summary)
2545
def test_tree_content_summary(self):
2546
preview = self.get_empty_preview()
2547
path = preview.new_directory('path', preview.root, 'path-id')
2548
preview.set_tree_reference('rev-1', path)
2549
summary = preview.get_preview_tree().path_content_summary('path')
2550
self.assertEqual(4, len(summary))
2551
self.assertEqual('tree-reference', summary[0])
2553
def test_annotate(self):
2554
tree = self.make_branch_and_tree('tree')
2555
self.build_tree_contents([('tree/file', 'a\n')])
2556
tree.add('file', 'file-id')
2557
tree.commit('a', rev_id='one')
2558
self.build_tree_contents([('tree/file', 'a\nb\n')])
2559
preview = TransformPreview(tree)
2560
self.addCleanup(preview.finalize)
2561
file_trans_id = preview.trans_id_file_id('file-id')
2562
preview.delete_contents(file_trans_id)
2563
preview.create_file('a\nb\nc\n', file_trans_id)
2564
preview_tree = preview.get_preview_tree()
2570
annotation = preview_tree.annotate_iter('file-id', 'me:')
2571
self.assertEqual(expected, annotation)
2573
def test_annotate_missing(self):
2574
preview = self.get_empty_preview()
2575
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2576
preview_tree = preview.get_preview_tree()
2582
annotation = preview_tree.annotate_iter('file-id', 'me:')
2583
self.assertEqual(expected, annotation)
2585
def test_annotate_rename(self):
2586
tree = self.make_branch_and_tree('tree')
2587
self.build_tree_contents([('tree/file', 'a\n')])
2588
tree.add('file', 'file-id')
2589
tree.commit('a', rev_id='one')
2590
preview = TransformPreview(tree)
2591
self.addCleanup(preview.finalize)
2592
file_trans_id = preview.trans_id_file_id('file-id')
2593
preview.adjust_path('newname', preview.root, file_trans_id)
2594
preview_tree = preview.get_preview_tree()
2598
annotation = preview_tree.annotate_iter('file-id', 'me:')
2599
self.assertEqual(expected, annotation)
2601
def test_annotate_deleted(self):
2602
tree = self.make_branch_and_tree('tree')
2603
self.build_tree_contents([('tree/file', 'a\n')])
2604
tree.add('file', 'file-id')
2605
tree.commit('a', rev_id='one')
2606
self.build_tree_contents([('tree/file', 'a\nb\n')])
2607
preview = TransformPreview(tree)
2608
self.addCleanup(preview.finalize)
2609
file_trans_id = preview.trans_id_file_id('file-id')
2610
preview.delete_contents(file_trans_id)
2611
preview_tree = preview.get_preview_tree()
2612
annotation = preview_tree.annotate_iter('file-id', 'me:')
2613
self.assertIs(None, annotation)
2615
def test_stored_kind(self):
2616
preview = self.get_empty_preview()
2617
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2618
preview_tree = preview.get_preview_tree()
2619
self.assertEqual('file', preview_tree.stored_kind('file-id'))
2621
def test_is_executable(self):
2622
preview = self.get_empty_preview()
2623
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2624
preview.set_executability(True, preview.trans_id_file_id('file-id'))
2625
preview_tree = preview.get_preview_tree()
2626
self.assertEqual(True, preview_tree.is_executable('file-id'))
2628
def test_get_set_parent_ids(self):
2629
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2630
self.assertEqual([], preview_tree.get_parent_ids())
2631
preview_tree.set_parent_ids(['rev-1'])
2632
self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
2634
def test_plan_file_merge(self):
2635
work_a = self.make_branch_and_tree('wta')
2636
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2637
work_a.add('file', 'file-id')
2638
base_id = work_a.commit('base version')
2639
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2640
preview = TransformPreview(work_a)
2641
self.addCleanup(preview.finalize)
2642
trans_id = preview.trans_id_file_id('file-id')
2643
preview.delete_contents(trans_id)
2644
preview.create_file('b\nc\nd\ne\n', trans_id)
2645
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2646
tree_a = preview.get_preview_tree()
2647
tree_a.set_parent_ids([base_id])
2649
('killed-a', 'a\n'),
2650
('killed-b', 'b\n'),
2651
('unchanged', 'c\n'),
2652
('unchanged', 'd\n'),
2655
], list(tree_a.plan_file_merge('file-id', tree_b)))
2657
def test_plan_file_merge_revision_tree(self):
2658
work_a = self.make_branch_and_tree('wta')
2659
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2660
work_a.add('file', 'file-id')
2661
base_id = work_a.commit('base version')
2662
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2663
preview = TransformPreview(work_a.basis_tree())
2664
self.addCleanup(preview.finalize)
2665
trans_id = preview.trans_id_file_id('file-id')
2666
preview.delete_contents(trans_id)
2667
preview.create_file('b\nc\nd\ne\n', trans_id)
2668
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2669
tree_a = preview.get_preview_tree()
2670
tree_a.set_parent_ids([base_id])
2672
('killed-a', 'a\n'),
2673
('killed-b', 'b\n'),
2674
('unchanged', 'c\n'),
2675
('unchanged', 'd\n'),
2678
], list(tree_a.plan_file_merge('file-id', tree_b)))
2680
def test_walkdirs(self):
2681
preview = self.get_empty_preview()
2682
root = preview.new_directory('', ROOT_PARENT, 'tree-root')
2683
# FIXME: new_directory should mark root.
2684
preview.adjust_path('', ROOT_PARENT, root)
2685
preview_tree = preview.get_preview_tree()
2686
file_trans_id = preview.new_file('a', preview.root, 'contents',
2688
expected = [(('', 'tree-root'),
2689
[('a', 'a', 'file', None, 'a-id', 'file')])]
2690
self.assertEqual(expected, list(preview_tree.walkdirs()))
2692
def test_extras(self):
2693
work_tree = self.make_branch_and_tree('tree')
2694
self.build_tree(['tree/removed-file', 'tree/existing-file',
2695
'tree/not-removed-file'])
2696
work_tree.add(['removed-file', 'not-removed-file'])
2697
preview = TransformPreview(work_tree)
2698
self.addCleanup(preview.finalize)
2699
preview.new_file('new-file', preview.root, 'contents')
2700
preview.new_file('new-versioned-file', preview.root, 'contents',
2702
tree = preview.get_preview_tree()
2703
preview.unversion_file(preview.trans_id_tree_path('removed-file'))
2704
self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
2707
def test_merge_into_preview(self):
2708
work_tree = self.make_branch_and_tree('tree')
2709
self.build_tree_contents([('tree/file','b\n')])
2710
work_tree.add('file', 'file-id')
2711
work_tree.commit('first commit')
2712
child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
2713
self.build_tree_contents([('child/file','b\nc\n')])
2714
child_tree.commit('child commit')
2715
child_tree.lock_write()
2716
self.addCleanup(child_tree.unlock)
2717
work_tree.lock_write()
2718
self.addCleanup(work_tree.unlock)
2719
preview = TransformPreview(work_tree)
2720
self.addCleanup(preview.finalize)
2721
file_trans_id = preview.trans_id_file_id('file-id')
2722
preview.delete_contents(file_trans_id)
2723
preview.create_file('a\nb\n', file_trans_id)
2724
pb = progress.DummyProgress()
2725
preview_tree = preview.get_preview_tree()
2726
merger = Merger.from_revision_ids(pb, preview_tree,
2727
child_tree.branch.last_revision(),
2728
other_branch=child_tree.branch,
2729
tree_branch=work_tree.branch)
2730
merger.merge_type = Merge3Merger
2731
tt = merger.make_merger().make_preview_transform()
2732
self.addCleanup(tt.finalize)
2733
final_tree = tt.get_preview_tree()
2734
self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
2736
def test_merge_preview_into_workingtree(self):
2737
tree = self.make_branch_and_tree('tree')
2738
tree.set_root_id('TREE_ROOT')
2739
tt = TransformPreview(tree)
2740
self.addCleanup(tt.finalize)
2741
tt.new_file('name', tt.root, 'content', 'file-id')
2742
tree2 = self.make_branch_and_tree('tree2')
2743
tree2.set_root_id('TREE_ROOT')
2744
pb = progress.DummyProgress()
2745
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2746
pb, tree.basis_tree())
2747
merger.merge_type = Merge3Merger
2750
def test_merge_preview_into_workingtree_handles_conflicts(self):
2751
tree = self.make_branch_and_tree('tree')
2752
self.build_tree_contents([('tree/foo', 'bar')])
2753
tree.add('foo', 'foo-id')
2755
tt = TransformPreview(tree)
2756
self.addCleanup(tt.finalize)
2757
trans_id = tt.trans_id_file_id('foo-id')
2758
tt.delete_contents(trans_id)
2759
tt.create_file('baz', trans_id)
2760
tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2761
self.build_tree_contents([('tree2/foo', 'qux')])
2762
pb = progress.DummyProgress()
2763
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2764
pb, tree.basis_tree())
2765
merger.merge_type = Merge3Merger
2768
def test_is_executable(self):
2769
tree = self.make_branch_and_tree('tree')
2770
preview = TransformPreview(tree)
2771
self.addCleanup(preview.finalize)
2772
preview.new_file('foo', preview.root, 'bar', 'baz-id')
2773
preview_tree = preview.get_preview_tree()
2774
self.assertEqual(False, preview_tree.is_executable('baz-id',
2776
self.assertEqual(False, preview_tree.is_executable('baz-id'))
2778
def test_commit_preview_tree(self):
2779
tree = self.make_branch_and_tree('tree')
2780
rev_id = tree.commit('rev1')
2781
tree.branch.lock_write()
2782
self.addCleanup(tree.branch.unlock)
2783
tt = TransformPreview(tree)
2784
tt.new_file('file', tt.root, 'contents', 'file_id')
2785
self.addCleanup(tt.finalize)
2786
preview = tt.get_preview_tree()
2787
preview.set_parent_ids([rev_id])
2788
builder = tree.branch.get_commit_builder([rev_id])
2789
list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
2790
builder.finish_inventory()
2791
rev2_id = builder.commit('rev2')
2792
rev2_tree = tree.branch.repository.revision_tree(rev2_id)
2793
self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
2795
def test_ascii_limbo_paths(self):
2796
self.requireFeature(tests.UnicodeFilenameFeature)
2797
branch = self.make_branch('any')
2798
tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
2799
tt = TransformPreview(tree)
2800
self.addCleanup(tt.finalize)
2801
foo_id = tt.new_directory('', ROOT_PARENT)
2802
bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
2803
limbo_path = tt._limbo_name(bar_id)
2804
self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
2807
class FakeSerializer(object):
2808
"""Serializer implementation that simply returns the input.
2810
The input is returned in the order used by pack.ContainerPushParser.
2813
def bytes_record(bytes, names):
2817
class TestSerializeTransform(tests.TestCaseWithTransport):
2819
_test_needs_features = [tests.UnicodeFilenameFeature]
2821
def get_preview(self, tree=None):
2823
tree = self.make_branch_and_tree('tree')
2824
tt = TransformPreview(tree)
2825
self.addCleanup(tt.finalize)
2828
def assertSerializesTo(self, expected, tt):
2829
records = list(tt.serialize(FakeSerializer()))
2830
self.assertEqual(expected, records)
2833
def default_attribs():
2838
'_new_executability': {},
2840
'_tree_path_ids': {'': 'new-0'},
2842
'_removed_contents': [],
2843
'_non_present_ids': {},
2846
def make_records(self, attribs, contents):
2848
(((('attribs'),),), bencode.bencode(attribs))]
2849
records.extend([(((n, k),), c) for n, k, c in contents])
2852
def creation_records(self):
2853
attribs = self.default_attribs()
2854
attribs['_id_number'] = 3
2855
attribs['_new_name'] = {
2856
'new-1': u'foo\u1234'.encode('utf-8'), 'new-2': 'qux'}
2857
attribs['_new_id'] = {'new-1': 'baz', 'new-2': 'quxx'}
2858
attribs['_new_parent'] = {'new-1': 'new-0', 'new-2': 'new-0'}
2859
attribs['_new_executability'] = {'new-1': 1}
2861
('new-1', 'file', 'i 1\nbar\n'),
2862
('new-2', 'directory', ''),
2864
return self.make_records(attribs, contents)
2866
def test_serialize_creation(self):
2867
tt = self.get_preview()
2868
tt.new_file(u'foo\u1234', tt.root, 'bar', 'baz', True)
2869
tt.new_directory('qux', tt.root, 'quxx')
2870
self.assertSerializesTo(self.creation_records(), tt)
2872
def test_deserialize_creation(self):
2873
tt = self.get_preview()
2874
tt.deserialize(iter(self.creation_records()))
2875
self.assertEqual(3, tt._id_number)
2876
self.assertEqual({'new-1': u'foo\u1234',
2877
'new-2': 'qux'}, tt._new_name)
2878
self.assertEqual({'new-1': 'baz', 'new-2': 'quxx'}, tt._new_id)
2879
self.assertEqual({'new-1': tt.root, 'new-2': tt.root}, tt._new_parent)
2880
self.assertEqual({'baz': 'new-1', 'quxx': 'new-2'}, tt._r_new_id)
2881
self.assertEqual({'new-1': True}, tt._new_executability)
2882
self.assertEqual({'new-1': 'file',
2883
'new-2': 'directory'}, tt._new_contents)
2884
foo_limbo = open(tt._limbo_name('new-1'), 'rb')
2886
foo_content = foo_limbo.read()
2889
self.assertEqual('bar', foo_content)
2891
def symlink_creation_records(self):
2892
attribs = self.default_attribs()
2893
attribs['_id_number'] = 2
2894
attribs['_new_name'] = {'new-1': u'foo\u1234'.encode('utf-8')}
2895
attribs['_new_parent'] = {'new-1': 'new-0'}
2896
contents = [('new-1', 'symlink', u'bar\u1234'.encode('utf-8'))]
2897
return self.make_records(attribs, contents)
2899
def test_serialize_symlink_creation(self):
2900
self.requireFeature(tests.SymlinkFeature)
2901
tt = self.get_preview()
2902
tt.new_symlink(u'foo\u1234', tt.root, u'bar\u1234')
2903
self.assertSerializesTo(self.symlink_creation_records(), tt)
2905
def test_deserialize_symlink_creation(self):
2906
self.requireFeature(tests.SymlinkFeature)
2907
tt = self.get_preview()
2908
tt.deserialize(iter(self.symlink_creation_records()))
2909
abspath = tt._limbo_name('new-1')
2910
foo_content = osutils.readlink(abspath)
2911
self.assertEqual(u'bar\u1234', foo_content)
2913
def make_destruction_preview(self):
2914
tree = self.make_branch_and_tree('.')
2915
self.build_tree([u'foo\u1234', 'bar'])
2916
tree.add([u'foo\u1234', 'bar'], ['foo-id', 'bar-id'])
2917
return self.get_preview(tree)
2919
def destruction_records(self):
2920
attribs = self.default_attribs()
2921
attribs['_id_number'] = 3
2922
attribs['_removed_id'] = ['new-1']
2923
attribs['_removed_contents'] = ['new-2']
2924
attribs['_tree_path_ids'] = {
2926
u'foo\u1234'.encode('utf-8'): 'new-1',
2929
return self.make_records(attribs, [])
2931
def test_serialize_destruction(self):
2932
tt = self.make_destruction_preview()
2933
foo_trans_id = tt.trans_id_tree_file_id('foo-id')
2934
tt.unversion_file(foo_trans_id)
2935
bar_trans_id = tt.trans_id_tree_file_id('bar-id')
2936
tt.delete_contents(bar_trans_id)
2937
self.assertSerializesTo(self.destruction_records(), tt)
2939
def test_deserialize_destruction(self):
2940
tt = self.make_destruction_preview()
2941
tt.deserialize(iter(self.destruction_records()))
2942
self.assertEqual({u'foo\u1234': 'new-1',
2944
'': tt.root}, tt._tree_path_ids)
2945
self.assertEqual({'new-1': u'foo\u1234',
2947
tt.root: ''}, tt._tree_id_paths)
2948
self.assertEqual(set(['new-1']), tt._removed_id)
2949
self.assertEqual(set(['new-2']), tt._removed_contents)
2951
def missing_records(self):
2952
attribs = self.default_attribs()
2953
attribs['_id_number'] = 2
2954
attribs['_non_present_ids'] = {
2956
return self.make_records(attribs, [])
2958
def test_serialize_missing(self):
2959
tt = self.get_preview()
2960
boo_trans_id = tt.trans_id_file_id('boo')
2961
self.assertSerializesTo(self.missing_records(), tt)
2963
def test_deserialize_missing(self):
2964
tt = self.get_preview()
2965
tt.deserialize(iter(self.missing_records()))
2966
self.assertEqual({'boo': 'new-1'}, tt._non_present_ids)
2968
def make_modification_preview(self):
2969
LINES_ONE = 'aa\nbb\ncc\ndd\n'
2970
LINES_TWO = 'z\nbb\nx\ndd\n'
2971
tree = self.make_branch_and_tree('tree')
2972
self.build_tree_contents([('tree/file', LINES_ONE)])
2973
tree.add('file', 'file-id')
2974
return self.get_preview(tree), LINES_TWO
2976
def modification_records(self):
2977
attribs = self.default_attribs()
2978
attribs['_id_number'] = 2
2979
attribs['_tree_path_ids'] = {
2982
attribs['_removed_contents'] = ['new-1']
2983
contents = [('new-1', 'file',
2984
'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
2985
return self.make_records(attribs, contents)
2987
def test_serialize_modification(self):
2988
tt, LINES = self.make_modification_preview()
2989
trans_id = tt.trans_id_file_id('file-id')
2990
tt.delete_contents(trans_id)
2991
tt.create_file(LINES, trans_id)
2992
self.assertSerializesTo(self.modification_records(), tt)
2994
def test_deserialize_modification(self):
2995
tt, LINES = self.make_modification_preview()
2996
tt.deserialize(iter(self.modification_records()))
2997
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2999
def make_kind_change_preview(self):
3000
LINES = 'a\nb\nc\nd\n'
3001
tree = self.make_branch_and_tree('tree')
3002
self.build_tree(['tree/foo/'])
3003
tree.add('foo', 'foo-id')
3004
return self.get_preview(tree), LINES
3006
def kind_change_records(self):
3007
attribs = self.default_attribs()
3008
attribs['_id_number'] = 2
3009
attribs['_tree_path_ids'] = {
3012
attribs['_removed_contents'] = ['new-1']
3013
contents = [('new-1', 'file',
3014
'i 4\na\nb\nc\nd\n\n')]
3015
return self.make_records(attribs, contents)
3017
def test_serialize_kind_change(self):
3018
tt, LINES = self.make_kind_change_preview()
3019
trans_id = tt.trans_id_file_id('foo-id')
3020
tt.delete_contents(trans_id)
3021
tt.create_file(LINES, trans_id)
3022
self.assertSerializesTo(self.kind_change_records(), tt)
3024
def test_deserialize_kind_change(self):
3025
tt, LINES = self.make_kind_change_preview()
3026
tt.deserialize(iter(self.kind_change_records()))
3027
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
3029
def make_add_contents_preview(self):
3030
LINES = 'a\nb\nc\nd\n'
3031
tree = self.make_branch_and_tree('tree')
3032
self.build_tree(['tree/foo'])
3034
os.unlink('tree/foo')
3035
return self.get_preview(tree), LINES
3037
def add_contents_records(self):
3038
attribs = self.default_attribs()
3039
attribs['_id_number'] = 2
3040
attribs['_tree_path_ids'] = {
3043
contents = [('new-1', 'file',
3044
'i 4\na\nb\nc\nd\n\n')]
3045
return self.make_records(attribs, contents)
3047
def test_serialize_add_contents(self):
3048
tt, LINES = self.make_add_contents_preview()
3049
trans_id = tt.trans_id_tree_path('foo')
3050
tt.create_file(LINES, trans_id)
3051
self.assertSerializesTo(self.add_contents_records(), tt)
3053
def test_deserialize_add_contents(self):
3054
tt, LINES = self.make_add_contents_preview()
3055
tt.deserialize(iter(self.add_contents_records()))
3056
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
3058
def test_get_parents_lines(self):
3059
LINES_ONE = 'aa\nbb\ncc\ndd\n'
3060
LINES_TWO = 'z\nbb\nx\ndd\n'
3061
tree = self.make_branch_and_tree('tree')
3062
self.build_tree_contents([('tree/file', LINES_ONE)])
3063
tree.add('file', 'file-id')
3064
tt = self.get_preview(tree)
3065
trans_id = tt.trans_id_tree_path('file')
3066
self.assertEqual((['aa\n', 'bb\n', 'cc\n', 'dd\n'],),
3067
tt._get_parents_lines(trans_id))
3069
def test_get_parents_texts(self):
3070
LINES_ONE = 'aa\nbb\ncc\ndd\n'
3071
LINES_TWO = 'z\nbb\nx\ndd\n'
3072
tree = self.make_branch_and_tree('tree')
3073
self.build_tree_contents([('tree/file', LINES_ONE)])
3074
tree.add('file', 'file-id')
3075
tt = self.get_preview(tree)
3076
trans_id = tt.trans_id_tree_path('file')
3077
self.assertEqual((LINES_ONE,),
3078
tt._get_parents_texts(trans_id))