508
842
transform, root = self.get_transform()
509
843
wt = transform._tree
845
self.addCleanup(wt.unlock)
510
846
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
512
sac = transform.new_file('set_after_creation', root, 'Set after creation', 'sac')
848
sac = transform.new_file('set_after_creation', root,
849
'Set after creation', 'sac')
513
850
transform.set_executability(True, sac)
514
uws = transform.new_file('unset_without_set', root, 'Unset badly', 'uws')
851
uws = transform.new_file('unset_without_set', root, 'Unset badly',
515
853
self.assertRaises(KeyError, transform.set_executability, None, uws)
516
854
transform.apply()
517
855
self.assertTrue(wt.is_executable('soc'))
518
856
self.assertTrue(wt.is_executable('sac'))
858
def test_preserve_mode(self):
859
"""File mode is preserved when replacing content"""
860
if sys.platform == 'win32':
861
raise TestSkipped('chmod has no effect on win32')
862
transform, root = self.get_transform()
863
transform.new_file('file1', root, 'contents', 'file1-id', True)
866
self.addCleanup(self.wt.unlock)
867
self.assertTrue(self.wt.is_executable('file1-id'))
868
transform, root = self.get_transform()
869
file1_id = transform.trans_id_tree_file_id('file1-id')
870
transform.delete_contents(file1_id)
871
transform.create_file('contents2', file1_id)
873
self.assertTrue(self.wt.is_executable('file1-id'))
875
def test__set_mode_stats_correctly(self):
876
"""_set_mode stats to determine file mode."""
877
if sys.platform == 'win32':
878
raise TestSkipped('chmod has no effect on win32')
882
def instrumented_stat(path):
883
stat_paths.append(path)
884
return real_stat(path)
886
transform, root = self.get_transform()
888
bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
889
file_id='bar-id-1', executable=False)
892
transform, root = self.get_transform()
893
bar1_id = transform.trans_id_tree_path('bar')
894
bar2_id = transform.trans_id_tree_path('bar2')
896
os.stat = instrumented_stat
897
transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
902
bar1_abspath = self.wt.abspath('bar')
903
self.assertEqual([bar1_abspath], stat_paths)
905
def test_iter_changes(self):
906
self.wt.set_root_id('eert_toor')
907
transform, root = self.get_transform()
908
transform.new_file('old', root, 'blah', 'id-1', True)
910
transform, root = self.get_transform()
912
self.assertEqual([], list(transform.iter_changes()))
913
old = transform.trans_id_tree_file_id('id-1')
914
transform.unversion_file(old)
915
self.assertEqual([('id-1', ('old', None), False, (True, False),
916
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
917
(True, True))], list(transform.iter_changes()))
918
transform.new_directory('new', root, 'id-1')
919
self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
920
('eert_toor', 'eert_toor'), ('old', 'new'),
921
('file', 'directory'),
922
(True, False))], list(transform.iter_changes()))
926
def test_iter_changes_new(self):
927
self.wt.set_root_id('eert_toor')
928
transform, root = self.get_transform()
929
transform.new_file('old', root, 'blah')
931
transform, root = self.get_transform()
933
old = transform.trans_id_tree_path('old')
934
transform.version_file('id-1', old)
935
self.assertEqual([('id-1', (None, 'old'), False, (False, True),
936
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
937
(False, False))], list(transform.iter_changes()))
941
def test_iter_changes_modifications(self):
942
self.wt.set_root_id('eert_toor')
943
transform, root = self.get_transform()
944
transform.new_file('old', root, 'blah', 'id-1')
945
transform.new_file('new', root, 'blah')
946
transform.new_directory('subdir', root, 'subdir-id')
948
transform, root = self.get_transform()
950
old = transform.trans_id_tree_path('old')
951
subdir = transform.trans_id_tree_file_id('subdir-id')
952
new = transform.trans_id_tree_path('new')
953
self.assertEqual([], list(transform.iter_changes()))
956
transform.delete_contents(old)
957
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
958
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
959
(False, False))], list(transform.iter_changes()))
962
transform.create_file('blah', old)
963
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
964
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
965
(False, False))], list(transform.iter_changes()))
966
transform.cancel_deletion(old)
967
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
968
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
969
(False, False))], list(transform.iter_changes()))
970
transform.cancel_creation(old)
972
# move file_id to a different file
973
self.assertEqual([], list(transform.iter_changes()))
974
transform.unversion_file(old)
975
transform.version_file('id-1', new)
976
transform.adjust_path('old', root, new)
977
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
978
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
979
(False, False))], list(transform.iter_changes()))
980
transform.cancel_versioning(new)
981
transform._removed_id = set()
984
self.assertEqual([], list(transform.iter_changes()))
985
transform.set_executability(True, old)
986
self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
987
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
988
(False, True))], list(transform.iter_changes()))
989
transform.set_executability(None, old)
992
self.assertEqual([], list(transform.iter_changes()))
993
transform.adjust_path('new', root, old)
994
transform._new_parent = {}
995
self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
996
('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
997
(False, False))], list(transform.iter_changes()))
998
transform._new_name = {}
1001
self.assertEqual([], list(transform.iter_changes()))
1002
transform.adjust_path('new', subdir, old)
1003
transform._new_name = {}
1004
self.assertEqual([('id-1', ('old', 'subdir/old'), False,
1005
(True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
1006
('file', 'file'), (False, False))],
1007
list(transform.iter_changes()))
1008
transform._new_path = {}
1011
transform.finalize()
1013
def test_iter_changes_modified_bleed(self):
1014
self.wt.set_root_id('eert_toor')
1015
"""Modified flag should not bleed from one change to another"""
1016
# unfortunately, we have no guarantee that file1 (which is modified)
1017
# will be applied before file2. And if it's applied after file2, it
1018
# obviously can't bleed into file2's change output. But for now, it
1020
transform, root = self.get_transform()
1021
transform.new_file('file1', root, 'blah', 'id-1')
1022
transform.new_file('file2', root, 'blah', 'id-2')
1024
transform, root = self.get_transform()
1026
transform.delete_contents(transform.trans_id_file_id('id-1'))
1027
transform.set_executability(True,
1028
transform.trans_id_file_id('id-2'))
1029
self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
1030
('eert_toor', 'eert_toor'), ('file1', u'file1'),
1031
('file', None), (False, False)),
1032
('id-2', (u'file2', u'file2'), False, (True, True),
1033
('eert_toor', 'eert_toor'), ('file2', u'file2'),
1034
('file', 'file'), (False, True))],
1035
list(transform.iter_changes()))
1037
transform.finalize()
1039
def test_iter_changes_move_missing(self):
1040
"""Test moving ids with no files around"""
1041
self.wt.set_root_id('toor_eert')
1042
# Need two steps because versioning a non-existant file is a conflict.
1043
transform, root = self.get_transform()
1044
transform.new_directory('floater', root, 'floater-id')
1046
transform, root = self.get_transform()
1047
transform.delete_contents(transform.trans_id_tree_path('floater'))
1049
transform, root = self.get_transform()
1050
floater = transform.trans_id_tree_path('floater')
1052
transform.adjust_path('flitter', root, floater)
1053
self.assertEqual([('floater-id', ('floater', 'flitter'), False,
1054
(True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
1055
(None, None), (False, False))], list(transform.iter_changes()))
1057
transform.finalize()
1059
def test_iter_changes_pointless(self):
1060
"""Ensure that no-ops are not treated as modifications"""
1061
self.wt.set_root_id('eert_toor')
1062
transform, root = self.get_transform()
1063
transform.new_file('old', root, 'blah', 'id-1')
1064
transform.new_directory('subdir', root, 'subdir-id')
1066
transform, root = self.get_transform()
1068
old = transform.trans_id_tree_path('old')
1069
subdir = transform.trans_id_tree_file_id('subdir-id')
1070
self.assertEqual([], list(transform.iter_changes()))
1071
transform.delete_contents(subdir)
1072
transform.create_directory(subdir)
1073
transform.set_executability(False, old)
1074
transform.unversion_file(old)
1075
transform.version_file('id-1', old)
1076
transform.adjust_path('old', root, old)
1077
self.assertEqual([], list(transform.iter_changes()))
1079
transform.finalize()
1081
def test_rename_count(self):
1082
transform, root = self.get_transform()
1083
transform.new_file('name1', root, 'contents')
1084
self.assertEqual(transform.rename_count, 0)
1086
self.assertEqual(transform.rename_count, 1)
1087
transform2, root = self.get_transform()
1088
transform2.adjust_path('name2', root,
1089
transform2.trans_id_tree_path('name1'))
1090
self.assertEqual(transform2.rename_count, 0)
1092
self.assertEqual(transform2.rename_count, 2)
1094
def test_change_parent(self):
1095
"""Ensure that after we change a parent, the results are still right.
1097
Renames and parent changes on pending transforms can happen as part
1098
of conflict resolution, and are explicitly permitted by the
1101
This test ensures they work correctly with the rename-avoidance
1104
transform, root = self.get_transform()
1105
parent1 = transform.new_directory('parent1', root)
1106
child1 = transform.new_file('child1', parent1, 'contents')
1107
parent2 = transform.new_directory('parent2', root)
1108
transform.adjust_path('child1', parent2, child1)
1110
self.failIfExists(self.wt.abspath('parent1/child1'))
1111
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1112
# rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1113
# no rename for child1 (counting only renames during apply)
1114
self.failUnlessEqual(2, transform.rename_count)
1116
def test_cancel_parent(self):
1117
"""Cancelling a parent doesn't cause deletion of a non-empty directory
1119
This is like the test_change_parent, except that we cancel the parent
1120
before adjusting the path. The transform must detect that the
1121
directory is non-empty, and move children to safe locations.
1123
transform, root = self.get_transform()
1124
parent1 = transform.new_directory('parent1', root)
1125
child1 = transform.new_file('child1', parent1, 'contents')
1126
child2 = transform.new_file('child2', parent1, 'contents')
1128
transform.cancel_creation(parent1)
1130
self.fail('Failed to move child1 before deleting parent1')
1131
transform.cancel_creation(child2)
1132
transform.create_directory(parent1)
1134
transform.cancel_creation(parent1)
1135
# If the transform incorrectly believes that child2 is still in
1136
# parent1's limbo directory, it will try to rename it and fail
1137
# because was already moved by the first cancel_creation.
1139
self.fail('Transform still thinks child2 is a child of parent1')
1140
parent2 = transform.new_directory('parent2', root)
1141
transform.adjust_path('child1', parent2, child1)
1143
self.failIfExists(self.wt.abspath('parent1'))
1144
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1145
# rename limbo/new-3 => parent2, rename limbo/new-2 => child1
1146
self.failUnlessEqual(2, transform.rename_count)
1148
def test_adjust_and_cancel(self):
1149
"""Make sure adjust_path keeps track of limbo children properly"""
1150
transform, root = self.get_transform()
1151
parent1 = transform.new_directory('parent1', root)
1152
child1 = transform.new_file('child1', parent1, 'contents')
1153
parent2 = transform.new_directory('parent2', root)
1154
transform.adjust_path('child1', parent2, child1)
1155
transform.cancel_creation(child1)
1157
transform.cancel_creation(parent1)
1158
# if the transform thinks child1 is still in parent1's limbo
1159
# directory, it will attempt to move it and fail.
1161
self.fail('Transform still thinks child1 is a child of parent1')
1162
transform.finalize()
1164
def test_noname_contents(self):
1165
"""TreeTransform should permit deferring naming files."""
1166
transform, root = self.get_transform()
1167
parent = transform.trans_id_file_id('parent-id')
1169
transform.create_directory(parent)
1171
self.fail("Can't handle contents with no name")
1172
transform.finalize()
1174
def test_noname_contents_nested(self):
1175
"""TreeTransform should permit deferring naming files."""
1176
transform, root = self.get_transform()
1177
parent = transform.trans_id_file_id('parent-id')
1179
transform.create_directory(parent)
1181
self.fail("Can't handle contents with no name")
1182
child = transform.new_directory('child', parent)
1183
transform.adjust_path('parent', root, parent)
1185
self.failUnlessExists(self.wt.abspath('parent/child'))
1186
self.assertEqual(1, transform.rename_count)
1188
def test_reuse_name(self):
1189
"""Avoid reusing the same limbo name for different files"""
1190
transform, root = self.get_transform()
1191
parent = transform.new_directory('parent', root)
1192
child1 = transform.new_directory('child', parent)
1194
child2 = transform.new_directory('child', parent)
1196
self.fail('Tranform tried to use the same limbo name twice')
1197
transform.adjust_path('child2', parent, child2)
1199
# limbo/new-1 => parent, limbo/new-3 => parent/child2
1200
# child2 is put into top-level limbo because child1 has already
1201
# claimed the direct limbo path when child2 is created. There is no
1202
# advantage in renaming files once they're in top-level limbo, except
1204
self.assertEqual(2, transform.rename_count)
1206
def test_reuse_when_first_moved(self):
1207
"""Don't avoid direct paths when it is safe to use them"""
1208
transform, root = self.get_transform()
1209
parent = transform.new_directory('parent', root)
1210
child1 = transform.new_directory('child', parent)
1211
transform.adjust_path('child1', parent, child1)
1212
child2 = transform.new_directory('child', parent)
1214
# limbo/new-1 => parent
1215
self.assertEqual(1, transform.rename_count)
1217
def test_reuse_after_cancel(self):
1218
"""Don't avoid direct paths when it is safe to use them"""
1219
transform, root = self.get_transform()
1220
parent2 = transform.new_directory('parent2', root)
1221
child1 = transform.new_directory('child1', parent2)
1222
transform.cancel_creation(parent2)
1223
transform.create_directory(parent2)
1224
child2 = transform.new_directory('child1', parent2)
1225
transform.adjust_path('child2', parent2, child1)
1227
# limbo/new-1 => parent2, limbo/new-2 => parent2/child1
1228
self.assertEqual(2, transform.rename_count)
1230
def test_finalize_order(self):
1231
"""Finalize must be done in child-to-parent order"""
1232
transform, root = self.get_transform()
1233
parent = transform.new_directory('parent', root)
1234
child = transform.new_directory('child', parent)
1236
transform.finalize()
1238
self.fail('Tried to remove parent before child1')
1240
def test_cancel_with_cancelled_child_should_succeed(self):
1241
transform, root = self.get_transform()
1242
parent = transform.new_directory('parent', root)
1243
child = transform.new_directory('child', parent)
1244
transform.cancel_creation(child)
1245
transform.cancel_creation(parent)
1246
transform.finalize()
1248
def test_rollback_on_directory_clash(self):
1250
wt = self.make_branch_and_tree('.')
1251
tt = TreeTransform(wt) # TreeTransform obtains write lock
1253
foo = tt.new_directory('foo', tt.root)
1254
tt.new_file('bar', foo, 'foobar')
1255
baz = tt.new_directory('baz', tt.root)
1256
tt.new_file('qux', baz, 'quux')
1257
# Ask for a rename 'foo' -> 'baz'
1258
tt.adjust_path('baz', tt.root, foo)
1259
# Lie to tt that we've already resolved all conflicts.
1260
tt.apply(no_conflicts=True)
1264
# The rename will fail because the target directory is not empty (but
1265
# raises FileExists anyway).
1266
err = self.assertRaises(errors.FileExists, tt_helper)
1267
self.assertContainsRe(str(err),
1268
"^File exists: .+/baz")
1270
def test_two_directories_clash(self):
1272
wt = self.make_branch_and_tree('.')
1273
tt = TreeTransform(wt) # TreeTransform obtains write lock
1275
foo_1 = tt.new_directory('foo', tt.root)
1276
tt.new_directory('bar', foo_1)
1277
# Adding the same directory with a different content
1278
foo_2 = tt.new_directory('foo', tt.root)
1279
tt.new_directory('baz', foo_2)
1280
# Lie to tt that we've already resolved all conflicts.
1281
tt.apply(no_conflicts=True)
1285
err = self.assertRaises(errors.FileExists, tt_helper)
1286
self.assertContainsRe(str(err),
1287
"^File exists: .+/foo")
1289
def test_two_directories_clash_finalize(self):
1291
wt = self.make_branch_and_tree('.')
1292
tt = TreeTransform(wt) # TreeTransform obtains write lock
1294
foo_1 = tt.new_directory('foo', tt.root)
1295
tt.new_directory('bar', foo_1)
1296
# Adding the same directory with a different content
1297
foo_2 = tt.new_directory('foo', tt.root)
1298
tt.new_directory('baz', foo_2)
1299
# Lie to tt that we've already resolved all conflicts.
1300
tt.apply(no_conflicts=True)
1304
err = self.assertRaises(errors.FileExists, tt_helper)
1305
self.assertContainsRe(str(err),
1306
"^File exists: .+/foo")
1308
def test_file_to_directory(self):
1309
wt = self.make_branch_and_tree('.')
1310
self.build_tree(['foo'])
1313
tt = TreeTransform(wt)
1314
self.addCleanup(tt.finalize)
1315
foo_trans_id = tt.trans_id_tree_path("foo")
1316
tt.delete_contents(foo_trans_id)
1317
tt.create_directory(foo_trans_id)
1318
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1319
tt.create_file(["aa\n"], bar_trans_id)
1320
tt.version_file("bar-1", bar_trans_id)
1322
self.failUnlessExists("foo/bar")
1325
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1330
changes = wt.changes_from(wt.basis_tree())
1331
self.assertFalse(changes.has_changed(), changes)
1333
def test_file_to_symlink(self):
1334
self.requireFeature(SymlinkFeature)
1335
wt = self.make_branch_and_tree('.')
1336
self.build_tree(['foo'])
1339
tt = TreeTransform(wt)
1340
self.addCleanup(tt.finalize)
1341
foo_trans_id = tt.trans_id_tree_path("foo")
1342
tt.delete_contents(foo_trans_id)
1343
tt.create_symlink("bar", foo_trans_id)
1345
self.failUnlessExists("foo")
1347
self.addCleanup(wt.unlock)
1348
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1351
def test_dir_to_file(self):
1352
wt = self.make_branch_and_tree('.')
1353
self.build_tree(['foo/', 'foo/bar'])
1354
wt.add(['foo', 'foo/bar'])
1356
tt = TreeTransform(wt)
1357
self.addCleanup(tt.finalize)
1358
foo_trans_id = tt.trans_id_tree_path("foo")
1359
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1360
tt.delete_contents(foo_trans_id)
1361
tt.delete_versioned(bar_trans_id)
1362
tt.create_file(["aa\n"], foo_trans_id)
1364
self.failUnlessExists("foo")
1366
self.addCleanup(wt.unlock)
1367
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1370
def test_dir_to_hardlink(self):
1371
self.requireFeature(HardlinkFeature)
1372
wt = self.make_branch_and_tree('.')
1373
self.build_tree(['foo/', 'foo/bar'])
1374
wt.add(['foo', 'foo/bar'])
1376
tt = TreeTransform(wt)
1377
self.addCleanup(tt.finalize)
1378
foo_trans_id = tt.trans_id_tree_path("foo")
1379
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1380
tt.delete_contents(foo_trans_id)
1381
tt.delete_versioned(bar_trans_id)
1382
self.build_tree(['baz'])
1383
tt.create_hardlink("baz", foo_trans_id)
1385
self.failUnlessExists("foo")
1386
self.failUnlessExists("baz")
1388
self.addCleanup(wt.unlock)
1389
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1392
def test_no_final_path(self):
1393
transform, root = self.get_transform()
1394
trans_id = transform.trans_id_file_id('foo')
1395
transform.create_file('bar', trans_id)
1396
transform.cancel_creation(trans_id)
1399
def test_create_from_tree(self):
1400
tree1 = self.make_branch_and_tree('tree1')
1401
self.build_tree_contents([('tree1/foo/',), ('tree1/bar', 'baz')])
1402
tree1.add(['foo', 'bar'], ['foo-id', 'bar-id'])
1403
tree2 = self.make_branch_and_tree('tree2')
1404
tt = TreeTransform(tree2)
1405
foo_trans_id = tt.create_path('foo', tt.root)
1406
create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1407
bar_trans_id = tt.create_path('bar', tt.root)
1408
create_from_tree(tt, bar_trans_id, tree1, 'bar-id')
1410
self.assertEqual('directory', osutils.file_kind('tree2/foo'))
1411
self.assertFileEqual('baz', 'tree2/bar')
1413
def test_create_from_tree_bytes(self):
1414
"""Provided lines are used instead of tree content."""
1415
tree1 = self.make_branch_and_tree('tree1')
1416
self.build_tree_contents([('tree1/foo', 'bar'),])
1417
tree1.add('foo', 'foo-id')
1418
tree2 = self.make_branch_and_tree('tree2')
1419
tt = TreeTransform(tree2)
1420
foo_trans_id = tt.create_path('foo', tt.root)
1421
create_from_tree(tt, foo_trans_id, tree1, 'foo-id', bytes='qux')
1423
self.assertFileEqual('qux', 'tree2/foo')
1425
def test_create_from_tree_symlink(self):
1426
self.requireFeature(SymlinkFeature)
1427
tree1 = self.make_branch_and_tree('tree1')
1428
os.symlink('bar', 'tree1/foo')
1429
tree1.add('foo', 'foo-id')
1430
tt = TreeTransform(self.make_branch_and_tree('tree2'))
1431
foo_trans_id = tt.create_path('foo', tt.root)
1432
create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1434
self.assertEqual('bar', os.readlink('tree2/foo'))
521
1437
class TransformGroup(object):
522
def __init__(self, dirname):
1439
def __init__(self, dirname, root_id):
523
1440
self.name = dirname
524
1441
os.mkdir(dirname)
525
1442
self.wt = BzrDir.create_standalone_workingtree(dirname)
1443
self.wt.set_root_id(root_id)
526
1444
self.b = self.wt.branch
527
1445
self.tt = TreeTransform(self.wt)
528
1446
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
530
1449
def conflict_text(tree, merge):
531
1450
template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
532
1451
return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
535
1454
class TestTransformMerge(TestCaseInTempDir):
536
1456
def test_text_merge(self):
537
base = TransformGroup("base")
1457
root_id = generate_ids.gen_root_id()
1458
base = TransformGroup("base", root_id)
538
1459
base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
539
1460
base.tt.new_file('b', base.root, 'b1', 'b')
540
1461
base.tt.new_file('c', base.root, 'c', 'c')
725
1650
a.add(['foo', 'foo/bar', 'foo/baz'])
726
1651
a.commit('initial commit')
727
1652
b = BzrDir.create_standalone_workingtree('b')
728
build_tree(a.basis_tree(), b)
1653
basis = a.basis_tree()
1655
self.addCleanup(basis.unlock)
1656
build_tree(basis, b)
729
1657
self.assertIs(os.path.isdir('b/foo'), True)
730
1658
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
731
1659
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1661
def test_build_with_references(self):
1662
tree = self.make_branch_and_tree('source',
1663
format='dirstate-with-subtree')
1664
subtree = self.make_branch_and_tree('source/subtree',
1665
format='dirstate-with-subtree')
1666
tree.add_reference(subtree)
1667
tree.commit('a revision')
1668
tree.branch.create_checkout('target')
1669
self.failUnlessExists('target')
1670
self.failUnlessExists('target/subtree')
1672
def test_file_conflict_handling(self):
1673
"""Ensure that when building trees, conflict handling is done"""
1674
source = self.make_branch_and_tree('source')
1675
target = self.make_branch_and_tree('target')
1676
self.build_tree(['source/file', 'target/file'])
1677
source.add('file', 'new-file')
1678
source.commit('added file')
1679
build_tree(source.basis_tree(), target)
1680
self.assertEqual([DuplicateEntry('Moved existing file to',
1681
'file.moved', 'file', None, 'new-file')],
1683
target2 = self.make_branch_and_tree('target2')
1684
target_file = file('target2/file', 'wb')
1686
source_file = file('source/file', 'rb')
1688
target_file.write(source_file.read())
1693
build_tree(source.basis_tree(), target2)
1694
self.assertEqual([], target2.conflicts())
1696
def test_symlink_conflict_handling(self):
1697
"""Ensure that when building trees, conflict handling is done"""
1698
self.requireFeature(SymlinkFeature)
1699
source = self.make_branch_and_tree('source')
1700
os.symlink('foo', 'source/symlink')
1701
source.add('symlink', 'new-symlink')
1702
source.commit('added file')
1703
target = self.make_branch_and_tree('target')
1704
os.symlink('bar', 'target/symlink')
1705
build_tree(source.basis_tree(), target)
1706
self.assertEqual([DuplicateEntry('Moved existing file to',
1707
'symlink.moved', 'symlink', None, 'new-symlink')],
1709
target = self.make_branch_and_tree('target2')
1710
os.symlink('foo', 'target2/symlink')
1711
build_tree(source.basis_tree(), target)
1712
self.assertEqual([], target.conflicts())
1714
def test_directory_conflict_handling(self):
1715
"""Ensure that when building trees, conflict handling is done"""
1716
source = self.make_branch_and_tree('source')
1717
target = self.make_branch_and_tree('target')
1718
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1719
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1720
source.commit('added file')
1721
build_tree(source.basis_tree(), target)
1722
self.assertEqual([], target.conflicts())
1723
self.failUnlessExists('target/dir1/file')
1725
# Ensure contents are merged
1726
target = self.make_branch_and_tree('target2')
1727
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1728
build_tree(source.basis_tree(), target)
1729
self.assertEqual([], target.conflicts())
1730
self.failUnlessExists('target2/dir1/file2')
1731
self.failUnlessExists('target2/dir1/file')
1733
# Ensure new contents are suppressed for existing branches
1734
target = self.make_branch_and_tree('target3')
1735
self.make_branch('target3/dir1')
1736
self.build_tree(['target3/dir1/file2'])
1737
build_tree(source.basis_tree(), target)
1738
self.failIfExists('target3/dir1/file')
1739
self.failUnlessExists('target3/dir1/file2')
1740
self.failUnlessExists('target3/dir1.diverted/file')
1741
self.assertEqual([DuplicateEntry('Diverted to',
1742
'dir1.diverted', 'dir1', 'new-dir1', None)],
1745
target = self.make_branch_and_tree('target4')
1746
self.build_tree(['target4/dir1/'])
1747
self.make_branch('target4/dir1/file')
1748
build_tree(source.basis_tree(), target)
1749
self.failUnlessExists('target4/dir1/file')
1750
self.assertEqual('directory', file_kind('target4/dir1/file'))
1751
self.failUnlessExists('target4/dir1/file.diverted')
1752
self.assertEqual([DuplicateEntry('Diverted to',
1753
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1756
def test_mixed_conflict_handling(self):
1757
"""Ensure that when building trees, conflict handling is done"""
1758
source = self.make_branch_and_tree('source')
1759
target = self.make_branch_and_tree('target')
1760
self.build_tree(['source/name', 'target/name/'])
1761
source.add('name', 'new-name')
1762
source.commit('added file')
1763
build_tree(source.basis_tree(), target)
1764
self.assertEqual([DuplicateEntry('Moved existing file to',
1765
'name.moved', 'name', None, 'new-name')], target.conflicts())
1767
def test_raises_in_populated(self):
1768
source = self.make_branch_and_tree('source')
1769
self.build_tree(['source/name'])
1771
source.commit('added name')
1772
target = self.make_branch_and_tree('target')
1773
self.build_tree(['target/name'])
1775
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1776
build_tree, source.basis_tree(), target)
1778
def test_build_tree_rename_count(self):
1779
source = self.make_branch_and_tree('source')
1780
self.build_tree(['source/file1', 'source/dir1/'])
1781
source.add(['file1', 'dir1'])
1782
source.commit('add1')
1783
target1 = self.make_branch_and_tree('target1')
1784
transform_result = build_tree(source.basis_tree(), target1)
1785
self.assertEqual(2, transform_result.rename_count)
1787
self.build_tree(['source/dir1/file2'])
1788
source.add(['dir1/file2'])
1789
source.commit('add3')
1790
target2 = self.make_branch_and_tree('target2')
1791
transform_result = build_tree(source.basis_tree(), target2)
1792
# children of non-root directories should not be renamed
1793
self.assertEqual(2, transform_result.rename_count)
1795
def create_ab_tree(self):
1796
"""Create a committed test tree with two files"""
1797
source = self.make_branch_and_tree('source')
1798
self.build_tree_contents([('source/file1', 'A')])
1799
self.build_tree_contents([('source/file2', 'B')])
1800
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1801
source.commit('commit files')
1803
self.addCleanup(source.unlock)
1806
def test_build_tree_accelerator_tree(self):
1807
source = self.create_ab_tree()
1808
self.build_tree_contents([('source/file2', 'C')])
1810
real_source_get_file = source.get_file
1811
def get_file(file_id, path=None):
1812
calls.append(file_id)
1813
return real_source_get_file(file_id, path)
1814
source.get_file = get_file
1815
target = self.make_branch_and_tree('target')
1816
revision_tree = source.basis_tree()
1817
revision_tree.lock_read()
1818
self.addCleanup(revision_tree.unlock)
1819
build_tree(revision_tree, target, source)
1820
self.assertEqual(['file1-id'], calls)
1822
self.addCleanup(target.unlock)
1823
self.assertEqual([], list(target.iter_changes(revision_tree)))
1825
def test_build_tree_accelerator_tree_missing_file(self):
1826
source = self.create_ab_tree()
1827
os.unlink('source/file1')
1828
source.remove(['file2'])
1829
target = self.make_branch_and_tree('target')
1830
revision_tree = source.basis_tree()
1831
revision_tree.lock_read()
1832
self.addCleanup(revision_tree.unlock)
1833
build_tree(revision_tree, target, source)
1835
self.addCleanup(target.unlock)
1836
self.assertEqual([], list(target.iter_changes(revision_tree)))
1838
def test_build_tree_accelerator_wrong_kind(self):
1839
self.requireFeature(SymlinkFeature)
1840
source = self.make_branch_and_tree('source')
1841
self.build_tree_contents([('source/file1', '')])
1842
self.build_tree_contents([('source/file2', '')])
1843
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1844
source.commit('commit files')
1845
os.unlink('source/file2')
1846
self.build_tree_contents([('source/file2/', 'C')])
1847
os.unlink('source/file1')
1848
os.symlink('file2', 'source/file1')
1850
real_source_get_file = source.get_file
1851
def get_file(file_id, path=None):
1852
calls.append(file_id)
1853
return real_source_get_file(file_id, path)
1854
source.get_file = get_file
1855
target = self.make_branch_and_tree('target')
1856
revision_tree = source.basis_tree()
1857
revision_tree.lock_read()
1858
self.addCleanup(revision_tree.unlock)
1859
build_tree(revision_tree, target, source)
1860
self.assertEqual([], calls)
1862
self.addCleanup(target.unlock)
1863
self.assertEqual([], list(target.iter_changes(revision_tree)))
1865
def test_build_tree_hardlink(self):
1866
self.requireFeature(HardlinkFeature)
1867
source = self.create_ab_tree()
1868
target = self.make_branch_and_tree('target')
1869
revision_tree = source.basis_tree()
1870
revision_tree.lock_read()
1871
self.addCleanup(revision_tree.unlock)
1872
build_tree(revision_tree, target, source, hardlink=True)
1874
self.addCleanup(target.unlock)
1875
self.assertEqual([], list(target.iter_changes(revision_tree)))
1876
source_stat = os.stat('source/file1')
1877
target_stat = os.stat('target/file1')
1878
self.assertEqual(source_stat, target_stat)
1880
# Explicitly disallowing hardlinks should prevent them.
1881
target2 = self.make_branch_and_tree('target2')
1882
build_tree(revision_tree, target2, source, hardlink=False)
1884
self.addCleanup(target2.unlock)
1885
self.assertEqual([], list(target2.iter_changes(revision_tree)))
1886
source_stat = os.stat('source/file1')
1887
target2_stat = os.stat('target2/file1')
1888
self.assertNotEqual(source_stat, target2_stat)
1890
def test_build_tree_accelerator_tree_moved(self):
1891
source = self.make_branch_and_tree('source')
1892
self.build_tree_contents([('source/file1', 'A')])
1893
source.add(['file1'], ['file1-id'])
1894
source.commit('commit files')
1895
source.rename_one('file1', 'file2')
1897
self.addCleanup(source.unlock)
1898
target = self.make_branch_and_tree('target')
1899
revision_tree = source.basis_tree()
1900
revision_tree.lock_read()
1901
self.addCleanup(revision_tree.unlock)
1902
build_tree(revision_tree, target, source)
1904
self.addCleanup(target.unlock)
1905
self.assertEqual([], list(target.iter_changes(revision_tree)))
1907
def test_build_tree_hardlinks_preserve_execute(self):
1908
self.requireFeature(HardlinkFeature)
1909
source = self.create_ab_tree()
1910
tt = TreeTransform(source)
1911
trans_id = tt.trans_id_tree_file_id('file1-id')
1912
tt.set_executability(True, trans_id)
1914
self.assertTrue(source.is_executable('file1-id'))
1915
target = self.make_branch_and_tree('target')
1916
revision_tree = source.basis_tree()
1917
revision_tree.lock_read()
1918
self.addCleanup(revision_tree.unlock)
1919
build_tree(revision_tree, target, source, hardlink=True)
1921
self.addCleanup(target.unlock)
1922
self.assertEqual([], list(target.iter_changes(revision_tree)))
1923
self.assertTrue(source.is_executable('file1-id'))
1925
def install_rot13_content_filter(self, pattern):
1927
# self.addCleanup(filters._reset_registry, filters._reset_registry())
1928
# below, but that looks a bit... hard to read even if it's exactly
1930
original_registry = filters._reset_registry()
1931
def restore_registry():
1932
filters._reset_registry(original_registry)
1933
self.addCleanup(restore_registry)
1934
def rot13(chunks, context=None):
1935
return [''.join(chunks).encode('rot13')]
1936
rot13filter = filters.ContentFilter(rot13, rot13)
1937
filters.register_filter_stack_map('rot13', {'yes': [rot13filter]}.get)
1938
os.mkdir(self.test_home_dir + '/.bazaar')
1939
rules_filename = self.test_home_dir + '/.bazaar/rules'
1940
f = open(rules_filename, 'wb')
1941
f.write('[name %s]\nrot13=yes\n' % (pattern,))
1943
def uninstall_rules():
1944
os.remove(rules_filename)
1946
self.addCleanup(uninstall_rules)
1949
def test_build_tree_content_filtered_files_are_not_hardlinked(self):
1950
"""build_tree will not hardlink files that have content filtering rules
1951
applied to them (but will still hardlink other files from the same tree
1954
self.requireFeature(HardlinkFeature)
1955
self.install_rot13_content_filter('file1')
1956
source = self.create_ab_tree()
1957
target = self.make_branch_and_tree('target')
1958
revision_tree = source.basis_tree()
1959
revision_tree.lock_read()
1960
self.addCleanup(revision_tree.unlock)
1961
build_tree(revision_tree, target, source, hardlink=True)
1963
self.addCleanup(target.unlock)
1964
self.assertEqual([], list(target.iter_changes(revision_tree)))
1965
source_stat = os.stat('source/file1')
1966
target_stat = os.stat('target/file1')
1967
self.assertNotEqual(source_stat, target_stat)
1968
source_stat = os.stat('source/file2')
1969
target_stat = os.stat('target/file2')
1970
self.assertEqualStat(source_stat, target_stat)
1972
def test_case_insensitive_build_tree_inventory(self):
1973
if (tests.CaseInsensitiveFilesystemFeature.available()
1974
or tests.CaseInsCasePresFilenameFeature.available()):
1975
raise tests.UnavailableFeature('Fully case sensitive filesystem')
1976
source = self.make_branch_and_tree('source')
1977
self.build_tree(['source/file', 'source/FILE'])
1978
source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1979
source.commit('added files')
1980
# Don't try this at home, kids!
1981
# Force the tree to report that it is case insensitive
1982
target = self.make_branch_and_tree('target')
1983
target.case_sensitive = False
1984
build_tree(source.basis_tree(), target, source, delta_from_tree=True)
1985
self.assertEqual('file.moved', target.id2path('lower-id'))
1986
self.assertEqual('FILE', target.id2path('upper-id'))
1989
class TestCommitTransform(tests.TestCaseWithTransport):
1991
def get_branch(self):
1992
tree = self.make_branch_and_tree('tree')
1994
self.addCleanup(tree.unlock)
1995
tree.commit('empty commit')
1998
def get_branch_and_transform(self):
1999
branch = self.get_branch()
2000
tt = TransformPreview(branch.basis_tree())
2001
self.addCleanup(tt.finalize)
2004
def test_commit_wrong_basis(self):
2005
branch = self.get_branch()
2006
basis = branch.repository.revision_tree(
2007
_mod_revision.NULL_REVISION)
2008
tt = TransformPreview(basis)
2009
self.addCleanup(tt.finalize)
2010
e = self.assertRaises(ValueError, tt.commit, branch, '')
2011
self.assertEqual('TreeTransform not based on branch basis: null:',
2014
def test_empy_commit(self):
2015
branch, tt = self.get_branch_and_transform()
2016
rev = tt.commit(branch, 'my message')
2017
self.assertEqual(2, branch.revno())
2018
repo = branch.repository
2019
self.assertEqual('my message', repo.get_revision(rev).message)
2021
def test_merge_parents(self):
2022
branch, tt = self.get_branch_and_transform()
2023
rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
2024
self.assertEqual(['rev1b', 'rev1c'],
2025
branch.basis_tree().get_parent_ids()[1:])
2027
def test_first_commit(self):
2028
branch = self.make_branch('branch')
2030
self.addCleanup(branch.unlock)
2031
tt = TransformPreview(branch.basis_tree())
2032
self.addCleanup(tt.finalize)
2033
tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
2034
rev = tt.commit(branch, 'my message')
2035
self.assertEqual([], branch.basis_tree().get_parent_ids())
2036
self.assertNotEqual(_mod_revision.NULL_REVISION,
2037
branch.last_revision())
2039
def test_first_commit_with_merge_parents(self):
2040
branch = self.make_branch('branch')
2042
self.addCleanup(branch.unlock)
2043
tt = TransformPreview(branch.basis_tree())
2044
self.addCleanup(tt.finalize)
2045
e = self.assertRaises(ValueError, tt.commit, branch,
2046
'my message', ['rev1b-id'])
2047
self.assertEqual('Cannot supply merge parents for first commit.',
2049
self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
2051
def test_add_files(self):
2052
branch, tt = self.get_branch_and_transform()
2053
tt.new_file('file', tt.root, 'contents', 'file-id')
2054
trans_id = tt.new_directory('dir', tt.root, 'dir-id')
2055
if SymlinkFeature.available():
2056
tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
2057
rev = tt.commit(branch, 'message')
2058
tree = branch.basis_tree()
2059
self.assertEqual('file', tree.id2path('file-id'))
2060
self.assertEqual('contents', tree.get_file_text('file-id'))
2061
self.assertEqual('dir', tree.id2path('dir-id'))
2062
if SymlinkFeature.available():
2063
self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
2064
self.assertEqual('target', tree.get_symlink_target('symlink-id'))
2066
def test_add_unversioned(self):
2067
branch, tt = self.get_branch_and_transform()
2068
tt.new_file('file', tt.root, 'contents')
2069
self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
2070
'message', strict=True)
2072
def test_modify_strict(self):
2073
branch, tt = self.get_branch_and_transform()
2074
tt.new_file('file', tt.root, 'contents', 'file-id')
2075
tt.commit(branch, 'message', strict=True)
2076
tt = TransformPreview(branch.basis_tree())
2077
self.addCleanup(tt.finalize)
2078
trans_id = tt.trans_id_file_id('file-id')
2079
tt.delete_contents(trans_id)
2080
tt.create_file('contents', trans_id)
2081
tt.commit(branch, 'message', strict=True)
2083
def test_commit_malformed(self):
2084
"""Committing a malformed transform should raise an exception.
2086
In this case, we are adding a file without adding its parent.
2088
branch, tt = self.get_branch_and_transform()
2089
parent_id = tt.trans_id_file_id('parent-id')
2090
tt.new_file('file', parent_id, 'contents', 'file-id')
2091
self.assertRaises(errors.MalformedTransform, tt.commit, branch,
733
2095
class MockTransform(object):
735
2097
def has_named_child(self, by_parent, parent_id, name):
759
2123
self.assertEqual(name, 'name.~1~')
760
2124
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
761
2125
self.assertEqual(name, 'name.~4~')
2128
class TestFileMover(tests.TestCaseWithTransport):
2130
def test_file_mover(self):
2131
self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
2132
mover = _FileMover()
2133
mover.rename('a', 'q')
2134
self.failUnlessExists('q')
2135
self.failIfExists('a')
2136
self.failUnlessExists('q/b')
2137
self.failUnlessExists('c')
2138
self.failUnlessExists('c/d')
2140
def test_pre_delete_rollback(self):
2141
self.build_tree(['a/'])
2142
mover = _FileMover()
2143
mover.pre_delete('a', 'q')
2144
self.failUnlessExists('q')
2145
self.failIfExists('a')
2147
self.failIfExists('q')
2148
self.failUnlessExists('a')
2150
def test_apply_deletions(self):
2151
self.build_tree(['a/', 'b/'])
2152
mover = _FileMover()
2153
mover.pre_delete('a', 'q')
2154
mover.pre_delete('b', 'r')
2155
self.failUnlessExists('q')
2156
self.failUnlessExists('r')
2157
self.failIfExists('a')
2158
self.failIfExists('b')
2159
mover.apply_deletions()
2160
self.failIfExists('q')
2161
self.failIfExists('r')
2162
self.failIfExists('a')
2163
self.failIfExists('b')
2165
def test_file_mover_rollback(self):
2166
self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
2167
mover = _FileMover()
2168
mover.rename('c/d', 'c/f')
2169
mover.rename('c/e', 'c/d')
2171
mover.rename('a', 'c')
2172
except errors.FileExists, e:
2174
self.failUnlessExists('a')
2175
self.failUnlessExists('c/d')
2178
class Bogus(Exception):
2182
class TestTransformRollback(tests.TestCaseWithTransport):
2184
class ExceptionFileMover(_FileMover):
2186
def __init__(self, bad_source=None, bad_target=None):
2187
_FileMover.__init__(self)
2188
self.bad_source = bad_source
2189
self.bad_target = bad_target
2191
def rename(self, source, target):
2192
if (self.bad_source is not None and
2193
source.endswith(self.bad_source)):
2195
elif (self.bad_target is not None and
2196
target.endswith(self.bad_target)):
2199
_FileMover.rename(self, source, target)
2201
def test_rollback_rename(self):
2202
tree = self.make_branch_and_tree('.')
2203
self.build_tree(['a/', 'a/b'])
2204
tt = TreeTransform(tree)
2205
self.addCleanup(tt.finalize)
2206
a_id = tt.trans_id_tree_path('a')
2207
tt.adjust_path('c', tt.root, a_id)
2208
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2209
self.assertRaises(Bogus, tt.apply,
2210
_mover=self.ExceptionFileMover(bad_source='a'))
2211
self.failUnlessExists('a')
2212
self.failUnlessExists('a/b')
2214
self.failUnlessExists('c')
2215
self.failUnlessExists('c/d')
2217
def test_rollback_rename_into_place(self):
2218
tree = self.make_branch_and_tree('.')
2219
self.build_tree(['a/', 'a/b'])
2220
tt = TreeTransform(tree)
2221
self.addCleanup(tt.finalize)
2222
a_id = tt.trans_id_tree_path('a')
2223
tt.adjust_path('c', tt.root, a_id)
2224
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2225
self.assertRaises(Bogus, tt.apply,
2226
_mover=self.ExceptionFileMover(bad_target='c/d'))
2227
self.failUnlessExists('a')
2228
self.failUnlessExists('a/b')
2230
self.failUnlessExists('c')
2231
self.failUnlessExists('c/d')
2233
def test_rollback_deletion(self):
2234
tree = self.make_branch_and_tree('.')
2235
self.build_tree(['a/', 'a/b'])
2236
tt = TreeTransform(tree)
2237
self.addCleanup(tt.finalize)
2238
a_id = tt.trans_id_tree_path('a')
2239
tt.delete_contents(a_id)
2240
tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
2241
self.assertRaises(Bogus, tt.apply,
2242
_mover=self.ExceptionFileMover(bad_target='d'))
2243
self.failUnlessExists('a')
2244
self.failUnlessExists('a/b')
2246
def test_resolve_no_parent(self):
2247
wt = self.make_branch_and_tree('.')
2248
tt = TreeTransform(wt)
2249
self.addCleanup(tt.finalize)
2250
parent = tt.trans_id_file_id('parent-id')
2251
tt.new_file('file', parent, 'Contents')
2252
resolve_conflicts(tt)
2255
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
2256
('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
2258
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
2259
('', ''), ('directory', 'directory'), (False, None))
2262
class TestTransformPreview(tests.TestCaseWithTransport):
2264
def create_tree(self):
2265
tree = self.make_branch_and_tree('.')
2266
self.build_tree_contents([('a', 'content 1')])
2267
tree.set_root_id('TREE_ROOT')
2268
tree.add('a', 'a-id')
2269
tree.commit('rev1', rev_id='rev1')
2270
return tree.branch.repository.revision_tree('rev1')
2272
def get_empty_preview(self):
2273
repository = self.make_repository('repo')
2274
tree = repository.revision_tree(_mod_revision.NULL_REVISION)
2275
preview = TransformPreview(tree)
2276
self.addCleanup(preview.finalize)
2279
def test_transform_preview(self):
2280
revision_tree = self.create_tree()
2281
preview = TransformPreview(revision_tree)
2282
self.addCleanup(preview.finalize)
2284
def test_transform_preview_tree(self):
2285
revision_tree = self.create_tree()
2286
preview = TransformPreview(revision_tree)
2287
self.addCleanup(preview.finalize)
2288
preview.get_preview_tree()
2290
def test_transform_new_file(self):
2291
revision_tree = self.create_tree()
2292
preview = TransformPreview(revision_tree)
2293
self.addCleanup(preview.finalize)
2294
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2295
preview_tree = preview.get_preview_tree()
2296
self.assertEqual(preview_tree.kind('file2-id'), 'file')
2298
preview_tree.get_file('file2-id').read(), 'content B\n')
2300
def test_diff_preview_tree(self):
2301
revision_tree = self.create_tree()
2302
preview = TransformPreview(revision_tree)
2303
self.addCleanup(preview.finalize)
2304
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2305
preview_tree = preview.get_preview_tree()
2307
show_diff_trees(revision_tree, preview_tree, out)
2308
lines = out.getvalue().splitlines()
2309
self.assertEqual(lines[0], "=== added file 'file2'")
2310
# 3 lines of diff administrivia
2311
self.assertEqual(lines[4], "+content B")
2313
def test_transform_conflicts(self):
2314
revision_tree = self.create_tree()
2315
preview = TransformPreview(revision_tree)
2316
self.addCleanup(preview.finalize)
2317
preview.new_file('a', preview.root, 'content 2')
2318
resolve_conflicts(preview)
2319
trans_id = preview.trans_id_file_id('a-id')
2320
self.assertEqual('a.moved', preview.final_name(trans_id))
2322
def get_tree_and_preview_tree(self):
2323
revision_tree = self.create_tree()
2324
preview = TransformPreview(revision_tree)
2325
self.addCleanup(preview.finalize)
2326
a_trans_id = preview.trans_id_file_id('a-id')
2327
preview.delete_contents(a_trans_id)
2328
preview.create_file('b content', a_trans_id)
2329
preview_tree = preview.get_preview_tree()
2330
return revision_tree, preview_tree
2332
def test_iter_changes(self):
2333
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2334
root = revision_tree.inventory.root.file_id
2335
self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
2336
(root, root), ('a', 'a'), ('file', 'file'),
2338
list(preview_tree.iter_changes(revision_tree)))
2340
def test_include_unchanged_succeeds(self):
2341
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2342
changes = preview_tree.iter_changes(revision_tree,
2343
include_unchanged=True)
2344
root = revision_tree.inventory.root.file_id
2346
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2348
def test_specific_files(self):
2349
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2350
changes = preview_tree.iter_changes(revision_tree,
2351
specific_files=[''])
2352
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2354
def test_want_unversioned(self):
2355
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2356
changes = preview_tree.iter_changes(revision_tree,
2357
want_unversioned=True)
2358
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2360
def test_ignore_extra_trees_no_specific_files(self):
2361
# extra_trees is harmless without specific_files, so we'll silently
2362
# accept it, even though we won't use it.
2363
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2364
preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
2366
def test_ignore_require_versioned_no_specific_files(self):
2367
# require_versioned is meaningless without specific_files.
2368
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2369
preview_tree.iter_changes(revision_tree, require_versioned=False)
2371
def test_ignore_pb(self):
2372
# pb could be supported, but TT.iter_changes doesn't support it.
2373
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2374
preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
2376
def test_kind(self):
2377
revision_tree = self.create_tree()
2378
preview = TransformPreview(revision_tree)
2379
self.addCleanup(preview.finalize)
2380
preview.new_file('file', preview.root, 'contents', 'file-id')
2381
preview.new_directory('directory', preview.root, 'dir-id')
2382
preview_tree = preview.get_preview_tree()
2383
self.assertEqual('file', preview_tree.kind('file-id'))
2384
self.assertEqual('directory', preview_tree.kind('dir-id'))
2386
def test_get_file_mtime(self):
2387
preview = self.get_empty_preview()
2388
file_trans_id = preview.new_file('file', preview.root, 'contents',
2390
limbo_path = preview._limbo_name(file_trans_id)
2391
preview_tree = preview.get_preview_tree()
2392
self.assertEqual(os.stat(limbo_path).st_mtime,
2393
preview_tree.get_file_mtime('file-id'))
2395
def test_get_file_mtime_renamed(self):
2396
work_tree = self.make_branch_and_tree('tree')
2397
self.build_tree(['tree/file'])
2398
work_tree.add('file', 'file-id')
2399
preview = TransformPreview(work_tree)
2400
self.addCleanup(preview.finalize)
2401
file_trans_id = preview.trans_id_tree_file_id('file-id')
2402
preview.adjust_path('renamed', preview.root, file_trans_id)
2403
preview_tree = preview.get_preview_tree()
2404
preview_mtime = preview_tree.get_file_mtime('file-id', 'renamed')
2405
work_mtime = work_tree.get_file_mtime('file-id', 'file')
2407
def test_get_file(self):
2408
preview = self.get_empty_preview()
2409
preview.new_file('file', preview.root, 'contents', 'file-id')
2410
preview_tree = preview.get_preview_tree()
2411
tree_file = preview_tree.get_file('file-id')
2413
self.assertEqual('contents', tree_file.read())
2417
def test_get_symlink_target(self):
2418
self.requireFeature(SymlinkFeature)
2419
preview = self.get_empty_preview()
2420
preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
2421
preview_tree = preview.get_preview_tree()
2422
self.assertEqual('target',
2423
preview_tree.get_symlink_target('symlink-id'))
2425
def test_all_file_ids(self):
2426
tree = self.make_branch_and_tree('tree')
2427
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
2428
tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
2429
preview = TransformPreview(tree)
2430
self.addCleanup(preview.finalize)
2431
preview.unversion_file(preview.trans_id_file_id('b-id'))
2432
c_trans_id = preview.trans_id_file_id('c-id')
2433
preview.unversion_file(c_trans_id)
2434
preview.version_file('c-id', c_trans_id)
2435
preview_tree = preview.get_preview_tree()
2436
self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
2437
preview_tree.all_file_ids())
2439
def test_path2id_deleted_unchanged(self):
2440
tree = self.make_branch_and_tree('tree')
2441
self.build_tree(['tree/unchanged', 'tree/deleted'])
2442
tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
2443
preview = TransformPreview(tree)
2444
self.addCleanup(preview.finalize)
2445
preview.unversion_file(preview.trans_id_file_id('deleted-id'))
2446
preview_tree = preview.get_preview_tree()
2447
self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
2448
self.assertIs(None, preview_tree.path2id('deleted'))
2450
def test_path2id_created(self):
2451
tree = self.make_branch_and_tree('tree')
2452
self.build_tree(['tree/unchanged'])
2453
tree.add(['unchanged'], ['unchanged-id'])
2454
preview = TransformPreview(tree)
2455
self.addCleanup(preview.finalize)
2456
preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
2457
'contents', 'new-id')
2458
preview_tree = preview.get_preview_tree()
2459
self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
2461
def test_path2id_moved(self):
2462
tree = self.make_branch_and_tree('tree')
2463
self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
2464
tree.add(['old_parent', 'old_parent/child'],
2465
['old_parent-id', 'child-id'])
2466
preview = TransformPreview(tree)
2467
self.addCleanup(preview.finalize)
2468
new_parent = preview.new_directory('new_parent', preview.root,
2470
preview.adjust_path('child', new_parent,
2471
preview.trans_id_file_id('child-id'))
2472
preview_tree = preview.get_preview_tree()
2473
self.assertIs(None, preview_tree.path2id('old_parent/child'))
2474
self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
2476
def test_path2id_renamed_parent(self):
2477
tree = self.make_branch_and_tree('tree')
2478
self.build_tree(['tree/old_name/', 'tree/old_name/child'])
2479
tree.add(['old_name', 'old_name/child'],
2480
['parent-id', 'child-id'])
2481
preview = TransformPreview(tree)
2482
self.addCleanup(preview.finalize)
2483
preview.adjust_path('new_name', preview.root,
2484
preview.trans_id_file_id('parent-id'))
2485
preview_tree = preview.get_preview_tree()
2486
self.assertIs(None, preview_tree.path2id('old_name/child'))
2487
self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
2489
def assertMatchingIterEntries(self, tt, specific_file_ids=None):
2490
preview_tree = tt.get_preview_tree()
2491
preview_result = list(preview_tree.iter_entries_by_dir(
2495
actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
2496
self.assertEqual(actual_result, preview_result)
2498
def test_iter_entries_by_dir_new(self):
2499
tree = self.make_branch_and_tree('tree')
2500
tt = TreeTransform(tree)
2501
tt.new_file('new', tt.root, 'contents', 'new-id')
2502
self.assertMatchingIterEntries(tt)
2504
def test_iter_entries_by_dir_deleted(self):
2505
tree = self.make_branch_and_tree('tree')
2506
self.build_tree(['tree/deleted'])
2507
tree.add('deleted', 'deleted-id')
2508
tt = TreeTransform(tree)
2509
tt.delete_contents(tt.trans_id_file_id('deleted-id'))
2510
self.assertMatchingIterEntries(tt)
2512
def test_iter_entries_by_dir_unversioned(self):
2513
tree = self.make_branch_and_tree('tree')
2514
self.build_tree(['tree/removed'])
2515
tree.add('removed', 'removed-id')
2516
tt = TreeTransform(tree)
2517
tt.unversion_file(tt.trans_id_file_id('removed-id'))
2518
self.assertMatchingIterEntries(tt)
2520
def test_iter_entries_by_dir_moved(self):
2521
tree = self.make_branch_and_tree('tree')
2522
self.build_tree(['tree/moved', 'tree/new_parent/'])
2523
tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
2524
tt = TreeTransform(tree)
2525
tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
2526
tt.trans_id_file_id('moved-id'))
2527
self.assertMatchingIterEntries(tt)
2529
def test_iter_entries_by_dir_specific_file_ids(self):
2530
tree = self.make_branch_and_tree('tree')
2531
tree.set_root_id('tree-root-id')
2532
self.build_tree(['tree/parent/', 'tree/parent/child'])
2533
tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
2534
tt = TreeTransform(tree)
2535
self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
2537
def test_symlink_content_summary(self):
2538
self.requireFeature(SymlinkFeature)
2539
preview = self.get_empty_preview()
2540
preview.new_symlink('path', preview.root, 'target', 'path-id')
2541
summary = preview.get_preview_tree().path_content_summary('path')
2542
self.assertEqual(('symlink', None, None, 'target'), summary)
2544
def test_missing_content_summary(self):
2545
preview = self.get_empty_preview()
2546
summary = preview.get_preview_tree().path_content_summary('path')
2547
self.assertEqual(('missing', None, None, None), summary)
2549
def test_deleted_content_summary(self):
2550
tree = self.make_branch_and_tree('tree')
2551
self.build_tree(['tree/path/'])
2553
preview = TransformPreview(tree)
2554
self.addCleanup(preview.finalize)
2555
preview.delete_contents(preview.trans_id_tree_path('path'))
2556
summary = preview.get_preview_tree().path_content_summary('path')
2557
self.assertEqual(('missing', None, None, None), summary)
2559
def test_file_content_summary_executable(self):
2560
preview = self.get_empty_preview()
2561
path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2562
preview.set_executability(True, path_id)
2563
summary = preview.get_preview_tree().path_content_summary('path')
2564
self.assertEqual(4, len(summary))
2565
self.assertEqual('file', summary[0])
2566
# size must be known
2567
self.assertEqual(len('contents'), summary[1])
2569
self.assertEqual(True, summary[2])
2570
# will not have hash (not cheap to determine)
2571
self.assertIs(None, summary[3])
2573
def test_change_executability(self):
2574
tree = self.make_branch_and_tree('tree')
2575
self.build_tree(['tree/path'])
2577
preview = TransformPreview(tree)
2578
self.addCleanup(preview.finalize)
2579
path_id = preview.trans_id_tree_path('path')
2580
preview.set_executability(True, path_id)
2581
summary = preview.get_preview_tree().path_content_summary('path')
2582
self.assertEqual(True, summary[2])
2584
def test_file_content_summary_non_exec(self):
2585
preview = self.get_empty_preview()
2586
preview.new_file('path', preview.root, 'contents', 'path-id')
2587
summary = preview.get_preview_tree().path_content_summary('path')
2588
self.assertEqual(4, len(summary))
2589
self.assertEqual('file', summary[0])
2590
# size must be known
2591
self.assertEqual(len('contents'), summary[1])
2593
self.assertEqual(False, summary[2])
2594
# will not have hash (not cheap to determine)
2595
self.assertIs(None, summary[3])
2597
def test_dir_content_summary(self):
2598
preview = self.get_empty_preview()
2599
preview.new_directory('path', preview.root, 'path-id')
2600
summary = preview.get_preview_tree().path_content_summary('path')
2601
self.assertEqual(('directory', None, None, None), summary)
2603
def test_tree_content_summary(self):
2604
preview = self.get_empty_preview()
2605
path = preview.new_directory('path', preview.root, 'path-id')
2606
preview.set_tree_reference('rev-1', path)
2607
summary = preview.get_preview_tree().path_content_summary('path')
2608
self.assertEqual(4, len(summary))
2609
self.assertEqual('tree-reference', summary[0])
2611
def test_annotate(self):
2612
tree = self.make_branch_and_tree('tree')
2613
self.build_tree_contents([('tree/file', 'a\n')])
2614
tree.add('file', 'file-id')
2615
tree.commit('a', rev_id='one')
2616
self.build_tree_contents([('tree/file', 'a\nb\n')])
2617
preview = TransformPreview(tree)
2618
self.addCleanup(preview.finalize)
2619
file_trans_id = preview.trans_id_file_id('file-id')
2620
preview.delete_contents(file_trans_id)
2621
preview.create_file('a\nb\nc\n', file_trans_id)
2622
preview_tree = preview.get_preview_tree()
2628
annotation = preview_tree.annotate_iter('file-id', 'me:')
2629
self.assertEqual(expected, annotation)
2631
def test_annotate_missing(self):
2632
preview = self.get_empty_preview()
2633
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2634
preview_tree = preview.get_preview_tree()
2640
annotation = preview_tree.annotate_iter('file-id', 'me:')
2641
self.assertEqual(expected, annotation)
2643
def test_annotate_rename(self):
2644
tree = self.make_branch_and_tree('tree')
2645
self.build_tree_contents([('tree/file', 'a\n')])
2646
tree.add('file', 'file-id')
2647
tree.commit('a', rev_id='one')
2648
preview = TransformPreview(tree)
2649
self.addCleanup(preview.finalize)
2650
file_trans_id = preview.trans_id_file_id('file-id')
2651
preview.adjust_path('newname', preview.root, file_trans_id)
2652
preview_tree = preview.get_preview_tree()
2656
annotation = preview_tree.annotate_iter('file-id', 'me:')
2657
self.assertEqual(expected, annotation)
2659
def test_annotate_deleted(self):
2660
tree = self.make_branch_and_tree('tree')
2661
self.build_tree_contents([('tree/file', 'a\n')])
2662
tree.add('file', 'file-id')
2663
tree.commit('a', rev_id='one')
2664
self.build_tree_contents([('tree/file', 'a\nb\n')])
2665
preview = TransformPreview(tree)
2666
self.addCleanup(preview.finalize)
2667
file_trans_id = preview.trans_id_file_id('file-id')
2668
preview.delete_contents(file_trans_id)
2669
preview_tree = preview.get_preview_tree()
2670
annotation = preview_tree.annotate_iter('file-id', 'me:')
2671
self.assertIs(None, annotation)
2673
def test_stored_kind(self):
2674
preview = self.get_empty_preview()
2675
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2676
preview_tree = preview.get_preview_tree()
2677
self.assertEqual('file', preview_tree.stored_kind('file-id'))
2679
def test_is_executable(self):
2680
preview = self.get_empty_preview()
2681
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2682
preview.set_executability(True, preview.trans_id_file_id('file-id'))
2683
preview_tree = preview.get_preview_tree()
2684
self.assertEqual(True, preview_tree.is_executable('file-id'))
2686
def test_get_set_parent_ids(self):
2687
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2688
self.assertEqual([], preview_tree.get_parent_ids())
2689
preview_tree.set_parent_ids(['rev-1'])
2690
self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
2692
def test_plan_file_merge(self):
2693
work_a = self.make_branch_and_tree('wta')
2694
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2695
work_a.add('file', 'file-id')
2696
base_id = work_a.commit('base version')
2697
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2698
preview = TransformPreview(work_a)
2699
self.addCleanup(preview.finalize)
2700
trans_id = preview.trans_id_file_id('file-id')
2701
preview.delete_contents(trans_id)
2702
preview.create_file('b\nc\nd\ne\n', trans_id)
2703
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2704
tree_a = preview.get_preview_tree()
2705
tree_a.set_parent_ids([base_id])
2707
('killed-a', 'a\n'),
2708
('killed-b', 'b\n'),
2709
('unchanged', 'c\n'),
2710
('unchanged', 'd\n'),
2713
], list(tree_a.plan_file_merge('file-id', tree_b)))
2715
def test_plan_file_merge_revision_tree(self):
2716
work_a = self.make_branch_and_tree('wta')
2717
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2718
work_a.add('file', 'file-id')
2719
base_id = work_a.commit('base version')
2720
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2721
preview = TransformPreview(work_a.basis_tree())
2722
self.addCleanup(preview.finalize)
2723
trans_id = preview.trans_id_file_id('file-id')
2724
preview.delete_contents(trans_id)
2725
preview.create_file('b\nc\nd\ne\n', trans_id)
2726
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2727
tree_a = preview.get_preview_tree()
2728
tree_a.set_parent_ids([base_id])
2730
('killed-a', 'a\n'),
2731
('killed-b', 'b\n'),
2732
('unchanged', 'c\n'),
2733
('unchanged', 'd\n'),
2736
], list(tree_a.plan_file_merge('file-id', tree_b)))
2738
def test_walkdirs(self):
2739
preview = self.get_empty_preview()
2740
root = preview.new_directory('', ROOT_PARENT, 'tree-root')
2741
# FIXME: new_directory should mark root.
2742
preview.fixup_new_roots()
2743
preview_tree = preview.get_preview_tree()
2744
file_trans_id = preview.new_file('a', preview.root, 'contents',
2746
expected = [(('', 'tree-root'),
2747
[('a', 'a', 'file', None, 'a-id', 'file')])]
2748
self.assertEqual(expected, list(preview_tree.walkdirs()))
2750
def test_extras(self):
2751
work_tree = self.make_branch_and_tree('tree')
2752
self.build_tree(['tree/removed-file', 'tree/existing-file',
2753
'tree/not-removed-file'])
2754
work_tree.add(['removed-file', 'not-removed-file'])
2755
preview = TransformPreview(work_tree)
2756
self.addCleanup(preview.finalize)
2757
preview.new_file('new-file', preview.root, 'contents')
2758
preview.new_file('new-versioned-file', preview.root, 'contents',
2760
tree = preview.get_preview_tree()
2761
preview.unversion_file(preview.trans_id_tree_path('removed-file'))
2762
self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
2765
def test_merge_into_preview(self):
2766
work_tree = self.make_branch_and_tree('tree')
2767
self.build_tree_contents([('tree/file','b\n')])
2768
work_tree.add('file', 'file-id')
2769
work_tree.commit('first commit')
2770
child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
2771
self.build_tree_contents([('child/file','b\nc\n')])
2772
child_tree.commit('child commit')
2773
child_tree.lock_write()
2774
self.addCleanup(child_tree.unlock)
2775
work_tree.lock_write()
2776
self.addCleanup(work_tree.unlock)
2777
preview = TransformPreview(work_tree)
2778
self.addCleanup(preview.finalize)
2779
file_trans_id = preview.trans_id_file_id('file-id')
2780
preview.delete_contents(file_trans_id)
2781
preview.create_file('a\nb\n', file_trans_id)
2782
pb = progress.DummyProgress()
2783
preview_tree = preview.get_preview_tree()
2784
merger = Merger.from_revision_ids(pb, preview_tree,
2785
child_tree.branch.last_revision(),
2786
other_branch=child_tree.branch,
2787
tree_branch=work_tree.branch)
2788
merger.merge_type = Merge3Merger
2789
tt = merger.make_merger().make_preview_transform()
2790
self.addCleanup(tt.finalize)
2791
final_tree = tt.get_preview_tree()
2792
self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
2794
def test_merge_preview_into_workingtree(self):
2795
tree = self.make_branch_and_tree('tree')
2796
tree.set_root_id('TREE_ROOT')
2797
tt = TransformPreview(tree)
2798
self.addCleanup(tt.finalize)
2799
tt.new_file('name', tt.root, 'content', 'file-id')
2800
tree2 = self.make_branch_and_tree('tree2')
2801
tree2.set_root_id('TREE_ROOT')
2802
pb = progress.DummyProgress()
2803
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2804
pb, tree.basis_tree())
2805
merger.merge_type = Merge3Merger
2808
def test_merge_preview_into_workingtree_handles_conflicts(self):
2809
tree = self.make_branch_and_tree('tree')
2810
self.build_tree_contents([('tree/foo', 'bar')])
2811
tree.add('foo', 'foo-id')
2813
tt = TransformPreview(tree)
2814
self.addCleanup(tt.finalize)
2815
trans_id = tt.trans_id_file_id('foo-id')
2816
tt.delete_contents(trans_id)
2817
tt.create_file('baz', trans_id)
2818
tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2819
self.build_tree_contents([('tree2/foo', 'qux')])
2820
pb = progress.DummyProgress()
2821
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2822
pb, tree.basis_tree())
2823
merger.merge_type = Merge3Merger
2826
def test_is_executable(self):
2827
tree = self.make_branch_and_tree('tree')
2828
preview = TransformPreview(tree)
2829
self.addCleanup(preview.finalize)
2830
preview.new_file('foo', preview.root, 'bar', 'baz-id')
2831
preview_tree = preview.get_preview_tree()
2832
self.assertEqual(False, preview_tree.is_executable('baz-id',
2834
self.assertEqual(False, preview_tree.is_executable('baz-id'))
2836
def test_commit_preview_tree(self):
2837
tree = self.make_branch_and_tree('tree')
2838
rev_id = tree.commit('rev1')
2839
tree.branch.lock_write()
2840
self.addCleanup(tree.branch.unlock)
2841
tt = TransformPreview(tree)
2842
tt.new_file('file', tt.root, 'contents', 'file_id')
2843
self.addCleanup(tt.finalize)
2844
preview = tt.get_preview_tree()
2845
preview.set_parent_ids([rev_id])
2846
builder = tree.branch.get_commit_builder([rev_id])
2847
list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
2848
builder.finish_inventory()
2849
rev2_id = builder.commit('rev2')
2850
rev2_tree = tree.branch.repository.revision_tree(rev2_id)
2851
self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
2853
def test_ascii_limbo_paths(self):
2854
self.requireFeature(tests.UnicodeFilenameFeature)
2855
branch = self.make_branch('any')
2856
tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
2857
tt = TransformPreview(tree)
2858
self.addCleanup(tt.finalize)
2859
foo_id = tt.new_directory('', ROOT_PARENT)
2860
bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
2861
limbo_path = tt._limbo_name(bar_id)
2862
self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
2865
class FakeSerializer(object):
2866
"""Serializer implementation that simply returns the input.
2868
The input is returned in the order used by pack.ContainerPushParser.
2871
def bytes_record(bytes, names):
2875
class TestSerializeTransform(tests.TestCaseWithTransport):
2877
_test_needs_features = [tests.UnicodeFilenameFeature]
2879
def get_preview(self, tree=None):
2881
tree = self.make_branch_and_tree('tree')
2882
tt = TransformPreview(tree)
2883
self.addCleanup(tt.finalize)
2886
def assertSerializesTo(self, expected, tt):
2887
records = list(tt.serialize(FakeSerializer()))
2888
self.assertEqual(expected, records)
2891
def default_attribs():
2896
'_new_executability': {},
2898
'_tree_path_ids': {'': 'new-0'},
2900
'_removed_contents': [],
2901
'_non_present_ids': {},
2904
def make_records(self, attribs, contents):
2906
(((('attribs'),),), bencode.bencode(attribs))]
2907
records.extend([(((n, k),), c) for n, k, c in contents])
2910
def creation_records(self):
2911
attribs = self.default_attribs()
2912
attribs['_id_number'] = 3
2913
attribs['_new_name'] = {
2914
'new-1': u'foo\u1234'.encode('utf-8'), 'new-2': 'qux'}
2915
attribs['_new_id'] = {'new-1': 'baz', 'new-2': 'quxx'}
2916
attribs['_new_parent'] = {'new-1': 'new-0', 'new-2': 'new-0'}
2917
attribs['_new_executability'] = {'new-1': 1}
2919
('new-1', 'file', 'i 1\nbar\n'),
2920
('new-2', 'directory', ''),
2922
return self.make_records(attribs, contents)
2924
def test_serialize_creation(self):
2925
tt = self.get_preview()
2926
tt.new_file(u'foo\u1234', tt.root, 'bar', 'baz', True)
2927
tt.new_directory('qux', tt.root, 'quxx')
2928
self.assertSerializesTo(self.creation_records(), tt)
2930
def test_deserialize_creation(self):
2931
tt = self.get_preview()
2932
tt.deserialize(iter(self.creation_records()))
2933
self.assertEqual(3, tt._id_number)
2934
self.assertEqual({'new-1': u'foo\u1234',
2935
'new-2': 'qux'}, tt._new_name)
2936
self.assertEqual({'new-1': 'baz', 'new-2': 'quxx'}, tt._new_id)
2937
self.assertEqual({'new-1': tt.root, 'new-2': tt.root}, tt._new_parent)
2938
self.assertEqual({'baz': 'new-1', 'quxx': 'new-2'}, tt._r_new_id)
2939
self.assertEqual({'new-1': True}, tt._new_executability)
2940
self.assertEqual({'new-1': 'file',
2941
'new-2': 'directory'}, tt._new_contents)
2942
foo_limbo = open(tt._limbo_name('new-1'), 'rb')
2944
foo_content = foo_limbo.read()
2947
self.assertEqual('bar', foo_content)
2949
def symlink_creation_records(self):
2950
attribs = self.default_attribs()
2951
attribs['_id_number'] = 2
2952
attribs['_new_name'] = {'new-1': u'foo\u1234'.encode('utf-8')}
2953
attribs['_new_parent'] = {'new-1': 'new-0'}
2954
contents = [('new-1', 'symlink', u'bar\u1234'.encode('utf-8'))]
2955
return self.make_records(attribs, contents)
2957
def test_serialize_symlink_creation(self):
2958
self.requireFeature(tests.SymlinkFeature)
2959
tt = self.get_preview()
2960
tt.new_symlink(u'foo\u1234', tt.root, u'bar\u1234')
2961
self.assertSerializesTo(self.symlink_creation_records(), tt)
2963
def test_deserialize_symlink_creation(self):
2964
self.requireFeature(tests.SymlinkFeature)
2965
tt = self.get_preview()
2966
tt.deserialize(iter(self.symlink_creation_records()))
2967
abspath = tt._limbo_name('new-1')
2968
foo_content = osutils.readlink(abspath)
2969
self.assertEqual(u'bar\u1234', foo_content)
2971
def make_destruction_preview(self):
2972
tree = self.make_branch_and_tree('.')
2973
self.build_tree([u'foo\u1234', 'bar'])
2974
tree.add([u'foo\u1234', 'bar'], ['foo-id', 'bar-id'])
2975
return self.get_preview(tree)
2977
def destruction_records(self):
2978
attribs = self.default_attribs()
2979
attribs['_id_number'] = 3
2980
attribs['_removed_id'] = ['new-1']
2981
attribs['_removed_contents'] = ['new-2']
2982
attribs['_tree_path_ids'] = {
2984
u'foo\u1234'.encode('utf-8'): 'new-1',
2987
return self.make_records(attribs, [])
2989
def test_serialize_destruction(self):
2990
tt = self.make_destruction_preview()
2991
foo_trans_id = tt.trans_id_tree_file_id('foo-id')
2992
tt.unversion_file(foo_trans_id)
2993
bar_trans_id = tt.trans_id_tree_file_id('bar-id')
2994
tt.delete_contents(bar_trans_id)
2995
self.assertSerializesTo(self.destruction_records(), tt)
2997
def test_deserialize_destruction(self):
2998
tt = self.make_destruction_preview()
2999
tt.deserialize(iter(self.destruction_records()))
3000
self.assertEqual({u'foo\u1234': 'new-1',
3002
'': tt.root}, tt._tree_path_ids)
3003
self.assertEqual({'new-1': u'foo\u1234',
3005
tt.root: ''}, tt._tree_id_paths)
3006
self.assertEqual(set(['new-1']), tt._removed_id)
3007
self.assertEqual(set(['new-2']), tt._removed_contents)
3009
def missing_records(self):
3010
attribs = self.default_attribs()
3011
attribs['_id_number'] = 2
3012
attribs['_non_present_ids'] = {
3014
return self.make_records(attribs, [])
3016
def test_serialize_missing(self):
3017
tt = self.get_preview()
3018
boo_trans_id = tt.trans_id_file_id('boo')
3019
self.assertSerializesTo(self.missing_records(), tt)
3021
def test_deserialize_missing(self):
3022
tt = self.get_preview()
3023
tt.deserialize(iter(self.missing_records()))
3024
self.assertEqual({'boo': 'new-1'}, tt._non_present_ids)
3026
def make_modification_preview(self):
3027
LINES_ONE = 'aa\nbb\ncc\ndd\n'
3028
LINES_TWO = 'z\nbb\nx\ndd\n'
3029
tree = self.make_branch_and_tree('tree')
3030
self.build_tree_contents([('tree/file', LINES_ONE)])
3031
tree.add('file', 'file-id')
3032
return self.get_preview(tree), LINES_TWO
3034
def modification_records(self):
3035
attribs = self.default_attribs()
3036
attribs['_id_number'] = 2
3037
attribs['_tree_path_ids'] = {
3040
attribs['_removed_contents'] = ['new-1']
3041
contents = [('new-1', 'file',
3042
'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
3043
return self.make_records(attribs, contents)
3045
def test_serialize_modification(self):
3046
tt, LINES = self.make_modification_preview()
3047
trans_id = tt.trans_id_file_id('file-id')
3048
tt.delete_contents(trans_id)
3049
tt.create_file(LINES, trans_id)
3050
self.assertSerializesTo(self.modification_records(), tt)
3052
def test_deserialize_modification(self):
3053
tt, LINES = self.make_modification_preview()
3054
tt.deserialize(iter(self.modification_records()))
3055
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
3057
def make_kind_change_preview(self):
3058
LINES = 'a\nb\nc\nd\n'
3059
tree = self.make_branch_and_tree('tree')
3060
self.build_tree(['tree/foo/'])
3061
tree.add('foo', 'foo-id')
3062
return self.get_preview(tree), LINES
3064
def kind_change_records(self):
3065
attribs = self.default_attribs()
3066
attribs['_id_number'] = 2
3067
attribs['_tree_path_ids'] = {
3070
attribs['_removed_contents'] = ['new-1']
3071
contents = [('new-1', 'file',
3072
'i 4\na\nb\nc\nd\n\n')]
3073
return self.make_records(attribs, contents)
3075
def test_serialize_kind_change(self):
3076
tt, LINES = self.make_kind_change_preview()
3077
trans_id = tt.trans_id_file_id('foo-id')
3078
tt.delete_contents(trans_id)
3079
tt.create_file(LINES, trans_id)
3080
self.assertSerializesTo(self.kind_change_records(), tt)
3082
def test_deserialize_kind_change(self):
3083
tt, LINES = self.make_kind_change_preview()
3084
tt.deserialize(iter(self.kind_change_records()))
3085
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
3087
def make_add_contents_preview(self):
3088
LINES = 'a\nb\nc\nd\n'
3089
tree = self.make_branch_and_tree('tree')
3090
self.build_tree(['tree/foo'])
3092
os.unlink('tree/foo')
3093
return self.get_preview(tree), LINES
3095
def add_contents_records(self):
3096
attribs = self.default_attribs()
3097
attribs['_id_number'] = 2
3098
attribs['_tree_path_ids'] = {
3101
contents = [('new-1', 'file',
3102
'i 4\na\nb\nc\nd\n\n')]
3103
return self.make_records(attribs, contents)
3105
def test_serialize_add_contents(self):
3106
tt, LINES = self.make_add_contents_preview()
3107
trans_id = tt.trans_id_tree_path('foo')
3108
tt.create_file(LINES, trans_id)
3109
self.assertSerializesTo(self.add_contents_records(), tt)
3111
def test_deserialize_add_contents(self):
3112
tt, LINES = self.make_add_contents_preview()
3113
tt.deserialize(iter(self.add_contents_records()))
3114
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
3116
def test_get_parents_lines(self):
3117
LINES_ONE = 'aa\nbb\ncc\ndd\n'
3118
LINES_TWO = 'z\nbb\nx\ndd\n'
3119
tree = self.make_branch_and_tree('tree')
3120
self.build_tree_contents([('tree/file', LINES_ONE)])
3121
tree.add('file', 'file-id')
3122
tt = self.get_preview(tree)
3123
trans_id = tt.trans_id_tree_path('file')
3124
self.assertEqual((['aa\n', 'bb\n', 'cc\n', 'dd\n'],),
3125
tt._get_parents_lines(trans_id))
3127
def test_get_parents_texts(self):
3128
LINES_ONE = 'aa\nbb\ncc\ndd\n'
3129
LINES_TWO = 'z\nbb\nx\ndd\n'
3130
tree = self.make_branch_and_tree('tree')
3131
self.build_tree_contents([('tree/file', LINES_ONE)])
3132
tree.add('file', 'file-id')
3133
tt = self.get_preview(tree)
3134
trans_id = tt.trans_id_tree_path('file')
3135
self.assertEqual((LINES_ONE,),
3136
tt._get_parents_texts(trans_id))