479
852
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')
855
def test_rename_fails(self):
856
# see https://bugs.launchpad.net/bzr/+bug/491763
857
create, root_id = self.get_transform()
858
first_dir = create.new_directory('first-dir', root_id, 'first-id')
859
myfile = create.new_file('myfile', root_id, 'myfile-text',
488
self.assertEqual(find_interesting(wt, wt, ['vfile']),
490
self.assertRaises(NotVersionedError, find_interesting, wt, wt,
862
# make the file and directory readonly in the hope this will prevent
864
osutils.make_readonly(self.wt.abspath('first-dir'))
865
osutils.make_readonly(self.wt.abspath('myfile'))
866
# now transform to rename
867
rename_transform, root_id = self.get_transform()
868
file_trans_id = rename_transform.trans_id_file_id('myfile-id')
869
dir_id = rename_transform.trans_id_file_id('first-id')
870
rename_transform.adjust_path('newname', dir_id, file_trans_id)
871
e = self.assertRaises(errors.TransformRenameFailed,
872
rename_transform.apply)
874
# "Failed to rename .../work/.bzr/checkout/limbo/new-1
875
# to .../first-dir/newname: [Errno 13] Permission denied"
876
# so the first filename is not visible in it; we expect a strerror but
877
# it may vary per OS and language so it's not checked here
878
self.assertContainsRe(str(e),
879
"Failed to rename .*first-dir.newname:")
881
def test_set_executability_order(self):
882
"""Ensure that executability behaves the same, no matter what order.
884
- create file and set executability simultaneously
885
- create file and set executability afterward
886
- unsetting the executability of a file whose executability has not been
887
declared should throw an exception (this may happen when a
888
merge attempts to create a file with a duplicate ID)
890
transform, root = self.get_transform()
893
self.addCleanup(wt.unlock)
894
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
896
sac = transform.new_file('set_after_creation', root,
897
'Set after creation', 'sac')
898
transform.set_executability(True, sac)
899
uws = transform.new_file('unset_without_set', root, 'Unset badly',
901
self.assertRaises(KeyError, transform.set_executability, None, uws)
903
self.assertTrue(wt.is_executable('soc'))
904
self.assertTrue(wt.is_executable('sac'))
906
def test_preserve_mode(self):
907
"""File mode is preserved when replacing content"""
908
if sys.platform == 'win32':
909
raise TestSkipped('chmod has no effect on win32')
910
transform, root = self.get_transform()
911
transform.new_file('file1', root, 'contents', 'file1-id', True)
914
self.addCleanup(self.wt.unlock)
915
self.assertTrue(self.wt.is_executable('file1-id'))
916
transform, root = self.get_transform()
917
file1_id = transform.trans_id_tree_file_id('file1-id')
918
transform.delete_contents(file1_id)
919
transform.create_file('contents2', file1_id)
921
self.assertTrue(self.wt.is_executable('file1-id'))
923
def test__set_mode_stats_correctly(self):
924
"""_set_mode stats to determine file mode."""
925
if sys.platform == 'win32':
926
raise TestSkipped('chmod has no effect on win32')
930
def instrumented_stat(path):
931
stat_paths.append(path)
932
return real_stat(path)
934
transform, root = self.get_transform()
936
bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
937
file_id='bar-id-1', executable=False)
940
transform, root = self.get_transform()
941
bar1_id = transform.trans_id_tree_path('bar')
942
bar2_id = transform.trans_id_tree_path('bar2')
944
os.stat = instrumented_stat
945
transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
950
bar1_abspath = self.wt.abspath('bar')
951
self.assertEqual([bar1_abspath], stat_paths)
953
def test_iter_changes(self):
954
self.wt.set_root_id('eert_toor')
955
transform, root = self.get_transform()
956
transform.new_file('old', root, 'blah', 'id-1', True)
958
transform, root = self.get_transform()
960
self.assertEqual([], list(transform.iter_changes()))
961
old = transform.trans_id_tree_file_id('id-1')
962
transform.unversion_file(old)
963
self.assertEqual([('id-1', ('old', None), False, (True, False),
964
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
965
(True, True))], list(transform.iter_changes()))
966
transform.new_directory('new', root, 'id-1')
967
self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
968
('eert_toor', 'eert_toor'), ('old', 'new'),
969
('file', 'directory'),
970
(True, False))], list(transform.iter_changes()))
974
def test_iter_changes_new(self):
975
self.wt.set_root_id('eert_toor')
976
transform, root = self.get_transform()
977
transform.new_file('old', root, 'blah')
979
transform, root = self.get_transform()
981
old = transform.trans_id_tree_path('old')
982
transform.version_file('id-1', old)
983
self.assertEqual([('id-1', (None, 'old'), False, (False, True),
984
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
985
(False, False))], list(transform.iter_changes()))
989
def test_iter_changes_modifications(self):
990
self.wt.set_root_id('eert_toor')
991
transform, root = self.get_transform()
992
transform.new_file('old', root, 'blah', 'id-1')
993
transform.new_file('new', root, 'blah')
994
transform.new_directory('subdir', root, 'subdir-id')
996
transform, root = self.get_transform()
998
old = transform.trans_id_tree_path('old')
999
subdir = transform.trans_id_tree_file_id('subdir-id')
1000
new = transform.trans_id_tree_path('new')
1001
self.assertEqual([], list(transform.iter_changes()))
1004
transform.delete_contents(old)
1005
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
1006
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
1007
(False, False))], list(transform.iter_changes()))
1010
transform.create_file('blah', old)
1011
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
1012
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1013
(False, False))], list(transform.iter_changes()))
1014
transform.cancel_deletion(old)
1015
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
1016
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1017
(False, False))], list(transform.iter_changes()))
1018
transform.cancel_creation(old)
1020
# move file_id to a different file
1021
self.assertEqual([], list(transform.iter_changes()))
1022
transform.unversion_file(old)
1023
transform.version_file('id-1', new)
1024
transform.adjust_path('old', root, new)
1025
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
1026
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1027
(False, False))], list(transform.iter_changes()))
1028
transform.cancel_versioning(new)
1029
transform._removed_id = set()
1032
self.assertEqual([], list(transform.iter_changes()))
1033
transform.set_executability(True, old)
1034
self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
1035
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1036
(False, True))], list(transform.iter_changes()))
1037
transform.set_executability(None, old)
1040
self.assertEqual([], list(transform.iter_changes()))
1041
transform.adjust_path('new', root, old)
1042
transform._new_parent = {}
1043
self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
1044
('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
1045
(False, False))], list(transform.iter_changes()))
1046
transform._new_name = {}
1049
self.assertEqual([], list(transform.iter_changes()))
1050
transform.adjust_path('new', subdir, old)
1051
transform._new_name = {}
1052
self.assertEqual([('id-1', ('old', 'subdir/old'), False,
1053
(True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
1054
('file', 'file'), (False, False))],
1055
list(transform.iter_changes()))
1056
transform._new_path = {}
1059
transform.finalize()
1061
def test_iter_changes_modified_bleed(self):
1062
self.wt.set_root_id('eert_toor')
1063
"""Modified flag should not bleed from one change to another"""
1064
# unfortunately, we have no guarantee that file1 (which is modified)
1065
# will be applied before file2. And if it's applied after file2, it
1066
# obviously can't bleed into file2's change output. But for now, it
1068
transform, root = self.get_transform()
1069
transform.new_file('file1', root, 'blah', 'id-1')
1070
transform.new_file('file2', root, 'blah', 'id-2')
1072
transform, root = self.get_transform()
1074
transform.delete_contents(transform.trans_id_file_id('id-1'))
1075
transform.set_executability(True,
1076
transform.trans_id_file_id('id-2'))
1077
self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
1078
('eert_toor', 'eert_toor'), ('file1', u'file1'),
1079
('file', None), (False, False)),
1080
('id-2', (u'file2', u'file2'), False, (True, True),
1081
('eert_toor', 'eert_toor'), ('file2', u'file2'),
1082
('file', 'file'), (False, True))],
1083
list(transform.iter_changes()))
1085
transform.finalize()
1087
def test_iter_changes_move_missing(self):
1088
"""Test moving ids with no files around"""
1089
self.wt.set_root_id('toor_eert')
1090
# Need two steps because versioning a non-existant file is a conflict.
1091
transform, root = self.get_transform()
1092
transform.new_directory('floater', root, 'floater-id')
1094
transform, root = self.get_transform()
1095
transform.delete_contents(transform.trans_id_tree_path('floater'))
1097
transform, root = self.get_transform()
1098
floater = transform.trans_id_tree_path('floater')
1100
transform.adjust_path('flitter', root, floater)
1101
self.assertEqual([('floater-id', ('floater', 'flitter'), False,
1102
(True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
1103
(None, None), (False, False))], list(transform.iter_changes()))
1105
transform.finalize()
1107
def test_iter_changes_pointless(self):
1108
"""Ensure that no-ops are not treated as modifications"""
1109
self.wt.set_root_id('eert_toor')
1110
transform, root = self.get_transform()
1111
transform.new_file('old', root, 'blah', 'id-1')
1112
transform.new_directory('subdir', root, 'subdir-id')
1114
transform, root = self.get_transform()
1116
old = transform.trans_id_tree_path('old')
1117
subdir = transform.trans_id_tree_file_id('subdir-id')
1118
self.assertEqual([], list(transform.iter_changes()))
1119
transform.delete_contents(subdir)
1120
transform.create_directory(subdir)
1121
transform.set_executability(False, old)
1122
transform.unversion_file(old)
1123
transform.version_file('id-1', old)
1124
transform.adjust_path('old', root, old)
1125
self.assertEqual([], list(transform.iter_changes()))
1127
transform.finalize()
1129
def test_rename_count(self):
1130
transform, root = self.get_transform()
1131
transform.new_file('name1', root, 'contents')
1132
self.assertEqual(transform.rename_count, 0)
1134
self.assertEqual(transform.rename_count, 1)
1135
transform2, root = self.get_transform()
1136
transform2.adjust_path('name2', root,
1137
transform2.trans_id_tree_path('name1'))
1138
self.assertEqual(transform2.rename_count, 0)
1140
self.assertEqual(transform2.rename_count, 2)
1142
def test_change_parent(self):
1143
"""Ensure that after we change a parent, the results are still right.
1145
Renames and parent changes on pending transforms can happen as part
1146
of conflict resolution, and are explicitly permitted by the
1149
This test ensures they work correctly with the rename-avoidance
1152
transform, root = self.get_transform()
1153
parent1 = transform.new_directory('parent1', root)
1154
child1 = transform.new_file('child1', parent1, 'contents')
1155
parent2 = transform.new_directory('parent2', root)
1156
transform.adjust_path('child1', parent2, child1)
1158
self.failIfExists(self.wt.abspath('parent1/child1'))
1159
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1160
# rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1161
# no rename for child1 (counting only renames during apply)
1162
self.failUnlessEqual(2, transform.rename_count)
1164
def test_cancel_parent(self):
1165
"""Cancelling a parent doesn't cause deletion of a non-empty directory
1167
This is like the test_change_parent, except that we cancel the parent
1168
before adjusting the path. The transform must detect that the
1169
directory is non-empty, and move children to safe locations.
1171
transform, root = self.get_transform()
1172
parent1 = transform.new_directory('parent1', root)
1173
child1 = transform.new_file('child1', parent1, 'contents')
1174
child2 = transform.new_file('child2', parent1, 'contents')
1176
transform.cancel_creation(parent1)
1178
self.fail('Failed to move child1 before deleting parent1')
1179
transform.cancel_creation(child2)
1180
transform.create_directory(parent1)
1182
transform.cancel_creation(parent1)
1183
# If the transform incorrectly believes that child2 is still in
1184
# parent1's limbo directory, it will try to rename it and fail
1185
# because was already moved by the first cancel_creation.
1187
self.fail('Transform still thinks child2 is a child of parent1')
1188
parent2 = transform.new_directory('parent2', root)
1189
transform.adjust_path('child1', parent2, child1)
1191
self.failIfExists(self.wt.abspath('parent1'))
1192
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1193
# rename limbo/new-3 => parent2, rename limbo/new-2 => child1
1194
self.failUnlessEqual(2, transform.rename_count)
1196
def test_adjust_and_cancel(self):
1197
"""Make sure adjust_path keeps track of limbo children properly"""
1198
transform, root = self.get_transform()
1199
parent1 = transform.new_directory('parent1', root)
1200
child1 = transform.new_file('child1', parent1, 'contents')
1201
parent2 = transform.new_directory('parent2', root)
1202
transform.adjust_path('child1', parent2, child1)
1203
transform.cancel_creation(child1)
1205
transform.cancel_creation(parent1)
1206
# if the transform thinks child1 is still in parent1's limbo
1207
# directory, it will attempt to move it and fail.
1209
self.fail('Transform still thinks child1 is a child of parent1')
1210
transform.finalize()
1212
def test_noname_contents(self):
1213
"""TreeTransform should permit deferring naming files."""
1214
transform, root = self.get_transform()
1215
parent = transform.trans_id_file_id('parent-id')
1217
transform.create_directory(parent)
1219
self.fail("Can't handle contents with no name")
1220
transform.finalize()
1222
def test_noname_contents_nested(self):
1223
"""TreeTransform should permit deferring naming files."""
1224
transform, root = self.get_transform()
1225
parent = transform.trans_id_file_id('parent-id')
1227
transform.create_directory(parent)
1229
self.fail("Can't handle contents with no name")
1230
child = transform.new_directory('child', parent)
1231
transform.adjust_path('parent', root, parent)
1233
self.failUnlessExists(self.wt.abspath('parent/child'))
1234
self.assertEqual(1, transform.rename_count)
1236
def test_reuse_name(self):
1237
"""Avoid reusing the same limbo name for different files"""
1238
transform, root = self.get_transform()
1239
parent = transform.new_directory('parent', root)
1240
child1 = transform.new_directory('child', parent)
1242
child2 = transform.new_directory('child', parent)
1244
self.fail('Tranform tried to use the same limbo name twice')
1245
transform.adjust_path('child2', parent, child2)
1247
# limbo/new-1 => parent, limbo/new-3 => parent/child2
1248
# child2 is put into top-level limbo because child1 has already
1249
# claimed the direct limbo path when child2 is created. There is no
1250
# advantage in renaming files once they're in top-level limbo, except
1252
self.assertEqual(2, transform.rename_count)
1254
def test_reuse_when_first_moved(self):
1255
"""Don't avoid direct paths when it is safe to use them"""
1256
transform, root = self.get_transform()
1257
parent = transform.new_directory('parent', root)
1258
child1 = transform.new_directory('child', parent)
1259
transform.adjust_path('child1', parent, child1)
1260
child2 = transform.new_directory('child', parent)
1262
# limbo/new-1 => parent
1263
self.assertEqual(1, transform.rename_count)
1265
def test_reuse_after_cancel(self):
1266
"""Don't avoid direct paths when it is safe to use them"""
1267
transform, root = self.get_transform()
1268
parent2 = transform.new_directory('parent2', root)
1269
child1 = transform.new_directory('child1', parent2)
1270
transform.cancel_creation(parent2)
1271
transform.create_directory(parent2)
1272
child2 = transform.new_directory('child1', parent2)
1273
transform.adjust_path('child2', parent2, child1)
1275
# limbo/new-1 => parent2, limbo/new-2 => parent2/child1
1276
self.assertEqual(2, transform.rename_count)
1278
def test_finalize_order(self):
1279
"""Finalize must be done in child-to-parent order"""
1280
transform, root = self.get_transform()
1281
parent = transform.new_directory('parent', root)
1282
child = transform.new_directory('child', parent)
1284
transform.finalize()
1286
self.fail('Tried to remove parent before child1')
1288
def test_cancel_with_cancelled_child_should_succeed(self):
1289
transform, root = self.get_transform()
1290
parent = transform.new_directory('parent', root)
1291
child = transform.new_directory('child', parent)
1292
transform.cancel_creation(child)
1293
transform.cancel_creation(parent)
1294
transform.finalize()
1296
def test_rollback_on_directory_clash(self):
1298
wt = self.make_branch_and_tree('.')
1299
tt = TreeTransform(wt) # TreeTransform obtains write lock
1301
foo = tt.new_directory('foo', tt.root)
1302
tt.new_file('bar', foo, 'foobar')
1303
baz = tt.new_directory('baz', tt.root)
1304
tt.new_file('qux', baz, 'quux')
1305
# Ask for a rename 'foo' -> 'baz'
1306
tt.adjust_path('baz', tt.root, foo)
1307
# Lie to tt that we've already resolved all conflicts.
1308
tt.apply(no_conflicts=True)
1312
# The rename will fail because the target directory is not empty (but
1313
# raises FileExists anyway).
1314
err = self.assertRaises(errors.FileExists, tt_helper)
1315
self.assertContainsRe(str(err),
1316
"^File exists: .+/baz")
1318
def test_two_directories_clash(self):
1320
wt = self.make_branch_and_tree('.')
1321
tt = TreeTransform(wt) # TreeTransform obtains write lock
1323
foo_1 = tt.new_directory('foo', tt.root)
1324
tt.new_directory('bar', foo_1)
1325
# Adding the same directory with a different content
1326
foo_2 = tt.new_directory('foo', tt.root)
1327
tt.new_directory('baz', foo_2)
1328
# Lie to tt that we've already resolved all conflicts.
1329
tt.apply(no_conflicts=True)
1333
err = self.assertRaises(errors.FileExists, tt_helper)
1334
self.assertContainsRe(str(err),
1335
"^File exists: .+/foo")
1337
def test_two_directories_clash_finalize(self):
1339
wt = self.make_branch_and_tree('.')
1340
tt = TreeTransform(wt) # TreeTransform obtains write lock
1342
foo_1 = tt.new_directory('foo', tt.root)
1343
tt.new_directory('bar', foo_1)
1344
# Adding the same directory with a different content
1345
foo_2 = tt.new_directory('foo', tt.root)
1346
tt.new_directory('baz', foo_2)
1347
# Lie to tt that we've already resolved all conflicts.
1348
tt.apply(no_conflicts=True)
1352
err = self.assertRaises(errors.FileExists, tt_helper)
1353
self.assertContainsRe(str(err),
1354
"^File exists: .+/foo")
1356
def test_file_to_directory(self):
1357
wt = self.make_branch_and_tree('.')
1358
self.build_tree(['foo'])
1361
tt = TreeTransform(wt)
1362
self.addCleanup(tt.finalize)
1363
foo_trans_id = tt.trans_id_tree_path("foo")
1364
tt.delete_contents(foo_trans_id)
1365
tt.create_directory(foo_trans_id)
1366
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1367
tt.create_file(["aa\n"], bar_trans_id)
1368
tt.version_file("bar-1", bar_trans_id)
1370
self.failUnlessExists("foo/bar")
1373
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1378
changes = wt.changes_from(wt.basis_tree())
1379
self.assertFalse(changes.has_changed(), changes)
1381
def test_file_to_symlink(self):
1382
self.requireFeature(SymlinkFeature)
1383
wt = self.make_branch_and_tree('.')
1384
self.build_tree(['foo'])
1387
tt = TreeTransform(wt)
1388
self.addCleanup(tt.finalize)
1389
foo_trans_id = tt.trans_id_tree_path("foo")
1390
tt.delete_contents(foo_trans_id)
1391
tt.create_symlink("bar", foo_trans_id)
1393
self.failUnlessExists("foo")
1395
self.addCleanup(wt.unlock)
1396
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1399
def test_dir_to_file(self):
1400
wt = self.make_branch_and_tree('.')
1401
self.build_tree(['foo/', 'foo/bar'])
1402
wt.add(['foo', 'foo/bar'])
1404
tt = TreeTransform(wt)
1405
self.addCleanup(tt.finalize)
1406
foo_trans_id = tt.trans_id_tree_path("foo")
1407
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1408
tt.delete_contents(foo_trans_id)
1409
tt.delete_versioned(bar_trans_id)
1410
tt.create_file(["aa\n"], foo_trans_id)
1412
self.failUnlessExists("foo")
1414
self.addCleanup(wt.unlock)
1415
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1418
def test_dir_to_hardlink(self):
1419
self.requireFeature(HardlinkFeature)
1420
wt = self.make_branch_and_tree('.')
1421
self.build_tree(['foo/', 'foo/bar'])
1422
wt.add(['foo', 'foo/bar'])
1424
tt = TreeTransform(wt)
1425
self.addCleanup(tt.finalize)
1426
foo_trans_id = tt.trans_id_tree_path("foo")
1427
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1428
tt.delete_contents(foo_trans_id)
1429
tt.delete_versioned(bar_trans_id)
1430
self.build_tree(['baz'])
1431
tt.create_hardlink("baz", foo_trans_id)
1433
self.failUnlessExists("foo")
1434
self.failUnlessExists("baz")
1436
self.addCleanup(wt.unlock)
1437
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1440
def test_no_final_path(self):
1441
transform, root = self.get_transform()
1442
trans_id = transform.trans_id_file_id('foo')
1443
transform.create_file('bar', trans_id)
1444
transform.cancel_creation(trans_id)
1447
def test_create_from_tree(self):
1448
tree1 = self.make_branch_and_tree('tree1')
1449
self.build_tree_contents([('tree1/foo/',), ('tree1/bar', 'baz')])
1450
tree1.add(['foo', 'bar'], ['foo-id', 'bar-id'])
1451
tree2 = self.make_branch_and_tree('tree2')
1452
tt = TreeTransform(tree2)
1453
foo_trans_id = tt.create_path('foo', tt.root)
1454
create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1455
bar_trans_id = tt.create_path('bar', tt.root)
1456
create_from_tree(tt, bar_trans_id, tree1, 'bar-id')
1458
self.assertEqual('directory', osutils.file_kind('tree2/foo'))
1459
self.assertFileEqual('baz', 'tree2/bar')
1461
def test_create_from_tree_bytes(self):
1462
"""Provided lines are used instead of tree content."""
1463
tree1 = self.make_branch_and_tree('tree1')
1464
self.build_tree_contents([('tree1/foo', 'bar'),])
1465
tree1.add('foo', 'foo-id')
1466
tree2 = self.make_branch_and_tree('tree2')
1467
tt = TreeTransform(tree2)
1468
foo_trans_id = tt.create_path('foo', tt.root)
1469
create_from_tree(tt, foo_trans_id, tree1, 'foo-id', bytes='qux')
1471
self.assertFileEqual('qux', 'tree2/foo')
1473
def test_create_from_tree_symlink(self):
1474
self.requireFeature(SymlinkFeature)
1475
tree1 = self.make_branch_and_tree('tree1')
1476
os.symlink('bar', 'tree1/foo')
1477
tree1.add('foo', 'foo-id')
1478
tt = TreeTransform(self.make_branch_and_tree('tree2'))
1479
foo_trans_id = tt.create_path('foo', tt.root)
1480
create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1482
self.assertEqual('bar', os.readlink('tree2/foo'))
494
1485
class TransformGroup(object):
495
def __init__(self, dirname):
1487
def __init__(self, dirname, root_id):
496
1488
self.name = dirname
497
1489
os.mkdir(dirname)
498
1490
self.wt = BzrDir.create_standalone_workingtree(dirname)
1491
self.wt.set_root_id(root_id)
499
1492
self.b = self.wt.branch
500
1493
self.tt = TreeTransform(self.wt)
501
1494
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
503
1497
def conflict_text(tree, merge):
504
1498
template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
505
1499
return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
508
1502
class TestTransformMerge(TestCaseInTempDir):
509
1504
def test_text_merge(self):
510
base = TransformGroup("base")
1505
root_id = generate_ids.gen_root_id()
1506
base = TransformGroup("base", root_id)
511
1507
base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
512
1508
base.tt.new_file('b', base.root, 'b1', 'b')
513
1509
base.tt.new_file('c', base.root, 'c', 'c')
698
1698
a.add(['foo', 'foo/bar', 'foo/baz'])
699
1699
a.commit('initial commit')
700
1700
b = BzrDir.create_standalone_workingtree('b')
701
build_tree(a.basis_tree(), b)
1701
basis = a.basis_tree()
1703
self.addCleanup(basis.unlock)
1704
build_tree(basis, b)
702
1705
self.assertIs(os.path.isdir('b/foo'), True)
703
1706
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
704
1707
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1709
def test_build_with_references(self):
1710
tree = self.make_branch_and_tree('source',
1711
format='dirstate-with-subtree')
1712
subtree = self.make_branch_and_tree('source/subtree',
1713
format='dirstate-with-subtree')
1714
tree.add_reference(subtree)
1715
tree.commit('a revision')
1716
tree.branch.create_checkout('target')
1717
self.failUnlessExists('target')
1718
self.failUnlessExists('target/subtree')
1720
def test_file_conflict_handling(self):
1721
"""Ensure that when building trees, conflict handling is done"""
1722
source = self.make_branch_and_tree('source')
1723
target = self.make_branch_and_tree('target')
1724
self.build_tree(['source/file', 'target/file'])
1725
source.add('file', 'new-file')
1726
source.commit('added file')
1727
build_tree(source.basis_tree(), target)
1728
self.assertEqual([DuplicateEntry('Moved existing file to',
1729
'file.moved', 'file', None, 'new-file')],
1731
target2 = self.make_branch_and_tree('target2')
1732
target_file = file('target2/file', 'wb')
1734
source_file = file('source/file', 'rb')
1736
target_file.write(source_file.read())
1741
build_tree(source.basis_tree(), target2)
1742
self.assertEqual([], target2.conflicts())
1744
def test_symlink_conflict_handling(self):
1745
"""Ensure that when building trees, conflict handling is done"""
1746
self.requireFeature(SymlinkFeature)
1747
source = self.make_branch_and_tree('source')
1748
os.symlink('foo', 'source/symlink')
1749
source.add('symlink', 'new-symlink')
1750
source.commit('added file')
1751
target = self.make_branch_and_tree('target')
1752
os.symlink('bar', 'target/symlink')
1753
build_tree(source.basis_tree(), target)
1754
self.assertEqual([DuplicateEntry('Moved existing file to',
1755
'symlink.moved', 'symlink', None, 'new-symlink')],
1757
target = self.make_branch_and_tree('target2')
1758
os.symlink('foo', 'target2/symlink')
1759
build_tree(source.basis_tree(), target)
1760
self.assertEqual([], target.conflicts())
1762
def test_directory_conflict_handling(self):
1763
"""Ensure that when building trees, conflict handling is done"""
1764
source = self.make_branch_and_tree('source')
1765
target = self.make_branch_and_tree('target')
1766
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1767
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1768
source.commit('added file')
1769
build_tree(source.basis_tree(), target)
1770
self.assertEqual([], target.conflicts())
1771
self.failUnlessExists('target/dir1/file')
1773
# Ensure contents are merged
1774
target = self.make_branch_and_tree('target2')
1775
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1776
build_tree(source.basis_tree(), target)
1777
self.assertEqual([], target.conflicts())
1778
self.failUnlessExists('target2/dir1/file2')
1779
self.failUnlessExists('target2/dir1/file')
1781
# Ensure new contents are suppressed for existing branches
1782
target = self.make_branch_and_tree('target3')
1783
self.make_branch('target3/dir1')
1784
self.build_tree(['target3/dir1/file2'])
1785
build_tree(source.basis_tree(), target)
1786
self.failIfExists('target3/dir1/file')
1787
self.failUnlessExists('target3/dir1/file2')
1788
self.failUnlessExists('target3/dir1.diverted/file')
1789
self.assertEqual([DuplicateEntry('Diverted to',
1790
'dir1.diverted', 'dir1', 'new-dir1', None)],
1793
target = self.make_branch_and_tree('target4')
1794
self.build_tree(['target4/dir1/'])
1795
self.make_branch('target4/dir1/file')
1796
build_tree(source.basis_tree(), target)
1797
self.failUnlessExists('target4/dir1/file')
1798
self.assertEqual('directory', file_kind('target4/dir1/file'))
1799
self.failUnlessExists('target4/dir1/file.diverted')
1800
self.assertEqual([DuplicateEntry('Diverted to',
1801
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1804
def test_mixed_conflict_handling(self):
1805
"""Ensure that when building trees, conflict handling is done"""
1806
source = self.make_branch_and_tree('source')
1807
target = self.make_branch_and_tree('target')
1808
self.build_tree(['source/name', 'target/name/'])
1809
source.add('name', 'new-name')
1810
source.commit('added file')
1811
build_tree(source.basis_tree(), target)
1812
self.assertEqual([DuplicateEntry('Moved existing file to',
1813
'name.moved', 'name', None, 'new-name')], target.conflicts())
1815
def test_raises_in_populated(self):
1816
source = self.make_branch_and_tree('source')
1817
self.build_tree(['source/name'])
1819
source.commit('added name')
1820
target = self.make_branch_and_tree('target')
1821
self.build_tree(['target/name'])
1823
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1824
build_tree, source.basis_tree(), target)
1826
def test_build_tree_rename_count(self):
1827
source = self.make_branch_and_tree('source')
1828
self.build_tree(['source/file1', 'source/dir1/'])
1829
source.add(['file1', 'dir1'])
1830
source.commit('add1')
1831
target1 = self.make_branch_and_tree('target1')
1832
transform_result = build_tree(source.basis_tree(), target1)
1833
self.assertEqual(2, transform_result.rename_count)
1835
self.build_tree(['source/dir1/file2'])
1836
source.add(['dir1/file2'])
1837
source.commit('add3')
1838
target2 = self.make_branch_and_tree('target2')
1839
transform_result = build_tree(source.basis_tree(), target2)
1840
# children of non-root directories should not be renamed
1841
self.assertEqual(2, transform_result.rename_count)
1843
def create_ab_tree(self):
1844
"""Create a committed test tree with two files"""
1845
source = self.make_branch_and_tree('source')
1846
self.build_tree_contents([('source/file1', 'A')])
1847
self.build_tree_contents([('source/file2', 'B')])
1848
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1849
source.commit('commit files')
1851
self.addCleanup(source.unlock)
1854
def test_build_tree_accelerator_tree(self):
1855
source = self.create_ab_tree()
1856
self.build_tree_contents([('source/file2', 'C')])
1858
real_source_get_file = source.get_file
1859
def get_file(file_id, path=None):
1860
calls.append(file_id)
1861
return real_source_get_file(file_id, path)
1862
source.get_file = get_file
1863
target = self.make_branch_and_tree('target')
1864
revision_tree = source.basis_tree()
1865
revision_tree.lock_read()
1866
self.addCleanup(revision_tree.unlock)
1867
build_tree(revision_tree, target, source)
1868
self.assertEqual(['file1-id'], calls)
1870
self.addCleanup(target.unlock)
1871
self.assertEqual([], list(target.iter_changes(revision_tree)))
1873
def test_build_tree_accelerator_tree_missing_file(self):
1874
source = self.create_ab_tree()
1875
os.unlink('source/file1')
1876
source.remove(['file2'])
1877
target = self.make_branch_and_tree('target')
1878
revision_tree = source.basis_tree()
1879
revision_tree.lock_read()
1880
self.addCleanup(revision_tree.unlock)
1881
build_tree(revision_tree, target, source)
1883
self.addCleanup(target.unlock)
1884
self.assertEqual([], list(target.iter_changes(revision_tree)))
1886
def test_build_tree_accelerator_wrong_kind(self):
1887
self.requireFeature(SymlinkFeature)
1888
source = self.make_branch_and_tree('source')
1889
self.build_tree_contents([('source/file1', '')])
1890
self.build_tree_contents([('source/file2', '')])
1891
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1892
source.commit('commit files')
1893
os.unlink('source/file2')
1894
self.build_tree_contents([('source/file2/', 'C')])
1895
os.unlink('source/file1')
1896
os.symlink('file2', 'source/file1')
1898
real_source_get_file = source.get_file
1899
def get_file(file_id, path=None):
1900
calls.append(file_id)
1901
return real_source_get_file(file_id, path)
1902
source.get_file = get_file
1903
target = self.make_branch_and_tree('target')
1904
revision_tree = source.basis_tree()
1905
revision_tree.lock_read()
1906
self.addCleanup(revision_tree.unlock)
1907
build_tree(revision_tree, target, source)
1908
self.assertEqual([], calls)
1910
self.addCleanup(target.unlock)
1911
self.assertEqual([], list(target.iter_changes(revision_tree)))
1913
def test_build_tree_hardlink(self):
1914
self.requireFeature(HardlinkFeature)
1915
source = self.create_ab_tree()
1916
target = self.make_branch_and_tree('target')
1917
revision_tree = source.basis_tree()
1918
revision_tree.lock_read()
1919
self.addCleanup(revision_tree.unlock)
1920
build_tree(revision_tree, target, source, hardlink=True)
1922
self.addCleanup(target.unlock)
1923
self.assertEqual([], list(target.iter_changes(revision_tree)))
1924
source_stat = os.stat('source/file1')
1925
target_stat = os.stat('target/file1')
1926
self.assertEqual(source_stat, target_stat)
1928
# Explicitly disallowing hardlinks should prevent them.
1929
target2 = self.make_branch_and_tree('target2')
1930
build_tree(revision_tree, target2, source, hardlink=False)
1932
self.addCleanup(target2.unlock)
1933
self.assertEqual([], list(target2.iter_changes(revision_tree)))
1934
source_stat = os.stat('source/file1')
1935
target2_stat = os.stat('target2/file1')
1936
self.assertNotEqual(source_stat, target2_stat)
1938
def test_build_tree_accelerator_tree_moved(self):
1939
source = self.make_branch_and_tree('source')
1940
self.build_tree_contents([('source/file1', 'A')])
1941
source.add(['file1'], ['file1-id'])
1942
source.commit('commit files')
1943
source.rename_one('file1', 'file2')
1945
self.addCleanup(source.unlock)
1946
target = self.make_branch_and_tree('target')
1947
revision_tree = source.basis_tree()
1948
revision_tree.lock_read()
1949
self.addCleanup(revision_tree.unlock)
1950
build_tree(revision_tree, target, source)
1952
self.addCleanup(target.unlock)
1953
self.assertEqual([], list(target.iter_changes(revision_tree)))
1955
def test_build_tree_hardlinks_preserve_execute(self):
1956
self.requireFeature(HardlinkFeature)
1957
source = self.create_ab_tree()
1958
tt = TreeTransform(source)
1959
trans_id = tt.trans_id_tree_file_id('file1-id')
1960
tt.set_executability(True, trans_id)
1962
self.assertTrue(source.is_executable('file1-id'))
1963
target = self.make_branch_and_tree('target')
1964
revision_tree = source.basis_tree()
1965
revision_tree.lock_read()
1966
self.addCleanup(revision_tree.unlock)
1967
build_tree(revision_tree, target, source, hardlink=True)
1969
self.addCleanup(target.unlock)
1970
self.assertEqual([], list(target.iter_changes(revision_tree)))
1971
self.assertTrue(source.is_executable('file1-id'))
1973
def install_rot13_content_filter(self, pattern):
1975
# self.addCleanup(filters._reset_registry, filters._reset_registry())
1976
# below, but that looks a bit... hard to read even if it's exactly
1978
original_registry = filters._reset_registry()
1979
def restore_registry():
1980
filters._reset_registry(original_registry)
1981
self.addCleanup(restore_registry)
1982
def rot13(chunks, context=None):
1983
return [''.join(chunks).encode('rot13')]
1984
rot13filter = filters.ContentFilter(rot13, rot13)
1985
filters.register_filter_stack_map('rot13', {'yes': [rot13filter]}.get)
1986
os.mkdir(self.test_home_dir + '/.bazaar')
1987
rules_filename = self.test_home_dir + '/.bazaar/rules'
1988
f = open(rules_filename, 'wb')
1989
f.write('[name %s]\nrot13=yes\n' % (pattern,))
1991
def uninstall_rules():
1992
os.remove(rules_filename)
1994
self.addCleanup(uninstall_rules)
1997
def test_build_tree_content_filtered_files_are_not_hardlinked(self):
1998
"""build_tree will not hardlink files that have content filtering rules
1999
applied to them (but will still hardlink other files from the same tree
2002
self.requireFeature(HardlinkFeature)
2003
self.install_rot13_content_filter('file1')
2004
source = self.create_ab_tree()
2005
target = self.make_branch_and_tree('target')
2006
revision_tree = source.basis_tree()
2007
revision_tree.lock_read()
2008
self.addCleanup(revision_tree.unlock)
2009
build_tree(revision_tree, target, source, hardlink=True)
2011
self.addCleanup(target.unlock)
2012
self.assertEqual([], list(target.iter_changes(revision_tree)))
2013
source_stat = os.stat('source/file1')
2014
target_stat = os.stat('target/file1')
2015
self.assertNotEqual(source_stat, target_stat)
2016
source_stat = os.stat('source/file2')
2017
target_stat = os.stat('target/file2')
2018
self.assertEqualStat(source_stat, target_stat)
2020
def test_case_insensitive_build_tree_inventory(self):
2021
if (tests.CaseInsensitiveFilesystemFeature.available()
2022
or tests.CaseInsCasePresFilenameFeature.available()):
2023
raise tests.UnavailableFeature('Fully case sensitive filesystem')
2024
source = self.make_branch_and_tree('source')
2025
self.build_tree(['source/file', 'source/FILE'])
2026
source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
2027
source.commit('added files')
2028
# Don't try this at home, kids!
2029
# Force the tree to report that it is case insensitive
2030
target = self.make_branch_and_tree('target')
2031
target.case_sensitive = False
2032
build_tree(source.basis_tree(), target, source, delta_from_tree=True)
2033
self.assertEqual('file.moved', target.id2path('lower-id'))
2034
self.assertEqual('FILE', target.id2path('upper-id'))
2037
class TestCommitTransform(tests.TestCaseWithTransport):
2039
def get_branch(self):
2040
tree = self.make_branch_and_tree('tree')
2042
self.addCleanup(tree.unlock)
2043
tree.commit('empty commit')
2046
def get_branch_and_transform(self):
2047
branch = self.get_branch()
2048
tt = TransformPreview(branch.basis_tree())
2049
self.addCleanup(tt.finalize)
2052
def test_commit_wrong_basis(self):
2053
branch = self.get_branch()
2054
basis = branch.repository.revision_tree(
2055
_mod_revision.NULL_REVISION)
2056
tt = TransformPreview(basis)
2057
self.addCleanup(tt.finalize)
2058
e = self.assertRaises(ValueError, tt.commit, branch, '')
2059
self.assertEqual('TreeTransform not based on branch basis: null:',
2062
def test_empy_commit(self):
2063
branch, tt = self.get_branch_and_transform()
2064
rev = tt.commit(branch, 'my message')
2065
self.assertEqual(2, branch.revno())
2066
repo = branch.repository
2067
self.assertEqual('my message', repo.get_revision(rev).message)
2069
def test_merge_parents(self):
2070
branch, tt = self.get_branch_and_transform()
2071
rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
2072
self.assertEqual(['rev1b', 'rev1c'],
2073
branch.basis_tree().get_parent_ids()[1:])
2075
def test_first_commit(self):
2076
branch = self.make_branch('branch')
2078
self.addCleanup(branch.unlock)
2079
tt = TransformPreview(branch.basis_tree())
2080
self.addCleanup(tt.finalize)
2081
tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
2082
rev = tt.commit(branch, 'my message')
2083
self.assertEqual([], branch.basis_tree().get_parent_ids())
2084
self.assertNotEqual(_mod_revision.NULL_REVISION,
2085
branch.last_revision())
2087
def test_first_commit_with_merge_parents(self):
2088
branch = self.make_branch('branch')
2090
self.addCleanup(branch.unlock)
2091
tt = TransformPreview(branch.basis_tree())
2092
self.addCleanup(tt.finalize)
2093
e = self.assertRaises(ValueError, tt.commit, branch,
2094
'my message', ['rev1b-id'])
2095
self.assertEqual('Cannot supply merge parents for first commit.',
2097
self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
2099
def test_add_files(self):
2100
branch, tt = self.get_branch_and_transform()
2101
tt.new_file('file', tt.root, 'contents', 'file-id')
2102
trans_id = tt.new_directory('dir', tt.root, 'dir-id')
2103
if SymlinkFeature.available():
2104
tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
2105
rev = tt.commit(branch, 'message')
2106
tree = branch.basis_tree()
2107
self.assertEqual('file', tree.id2path('file-id'))
2108
self.assertEqual('contents', tree.get_file_text('file-id'))
2109
self.assertEqual('dir', tree.id2path('dir-id'))
2110
if SymlinkFeature.available():
2111
self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
2112
self.assertEqual('target', tree.get_symlink_target('symlink-id'))
2114
def test_add_unversioned(self):
2115
branch, tt = self.get_branch_and_transform()
2116
tt.new_file('file', tt.root, 'contents')
2117
self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
2118
'message', strict=True)
2120
def test_modify_strict(self):
2121
branch, tt = self.get_branch_and_transform()
2122
tt.new_file('file', tt.root, 'contents', 'file-id')
2123
tt.commit(branch, 'message', strict=True)
2124
tt = TransformPreview(branch.basis_tree())
2125
self.addCleanup(tt.finalize)
2126
trans_id = tt.trans_id_file_id('file-id')
2127
tt.delete_contents(trans_id)
2128
tt.create_file('contents', trans_id)
2129
tt.commit(branch, 'message', strict=True)
2131
def test_commit_malformed(self):
2132
"""Committing a malformed transform should raise an exception.
2134
In this case, we are adding a file without adding its parent.
2136
branch, tt = self.get_branch_and_transform()
2137
parent_id = tt.trans_id_file_id('parent-id')
2138
tt.new_file('file', parent_id, 'contents', 'file-id')
2139
self.assertRaises(errors.MalformedTransform, tt.commit, branch,
2142
def test_commit_rich_revision_data(self):
2143
branch, tt = self.get_branch_and_transform()
2144
rev_id = tt.commit(branch, 'message', timestamp=1, timezone=43201,
2145
committer='me <me@example.com>',
2146
revprops={'foo': 'bar'}, revision_id='revid-1',
2147
authors=['Author1 <author1@example.com>',
2148
'Author2 <author2@example.com>',
2150
self.assertEqual('revid-1', rev_id)
2151
revision = branch.repository.get_revision(rev_id)
2152
self.assertEqual(1, revision.timestamp)
2153
self.assertEqual(43201, revision.timezone)
2154
self.assertEqual('me <me@example.com>', revision.committer)
2155
self.assertEqual(['Author1 <author1@example.com>',
2156
'Author2 <author2@example.com>'],
2157
revision.get_apparent_authors())
2158
del revision.properties['authors']
2159
self.assertEqual({'foo': 'bar',
2160
'branch-nick': 'tree'},
2161
revision.properties)
2163
def test_no_explicit_revprops(self):
2164
branch, tt = self.get_branch_and_transform()
2165
rev_id = tt.commit(branch, 'message', authors=[
2166
'Author1 <author1@example.com>',
2167
'Author2 <author2@example.com>', ])
2168
revision = branch.repository.get_revision(rev_id)
2169
self.assertEqual(['Author1 <author1@example.com>',
2170
'Author2 <author2@example.com>'],
2171
revision.get_apparent_authors())
2172
self.assertEqual('tree', revision.properties['branch-nick'])
706
2175
class MockTransform(object):
708
2177
def has_named_child(self, by_parent, parent_id, name):
732
2203
self.assertEqual(name, 'name.~1~')
733
2204
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
734
2205
self.assertEqual(name, 'name.~4~')
2208
class TestFileMover(tests.TestCaseWithTransport):
2210
def test_file_mover(self):
2211
self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
2212
mover = _FileMover()
2213
mover.rename('a', 'q')
2214
self.failUnlessExists('q')
2215
self.failIfExists('a')
2216
self.failUnlessExists('q/b')
2217
self.failUnlessExists('c')
2218
self.failUnlessExists('c/d')
2220
def test_pre_delete_rollback(self):
2221
self.build_tree(['a/'])
2222
mover = _FileMover()
2223
mover.pre_delete('a', 'q')
2224
self.failUnlessExists('q')
2225
self.failIfExists('a')
2227
self.failIfExists('q')
2228
self.failUnlessExists('a')
2230
def test_apply_deletions(self):
2231
self.build_tree(['a/', 'b/'])
2232
mover = _FileMover()
2233
mover.pre_delete('a', 'q')
2234
mover.pre_delete('b', 'r')
2235
self.failUnlessExists('q')
2236
self.failUnlessExists('r')
2237
self.failIfExists('a')
2238
self.failIfExists('b')
2239
mover.apply_deletions()
2240
self.failIfExists('q')
2241
self.failIfExists('r')
2242
self.failIfExists('a')
2243
self.failIfExists('b')
2245
def test_file_mover_rollback(self):
2246
self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
2247
mover = _FileMover()
2248
mover.rename('c/d', 'c/f')
2249
mover.rename('c/e', 'c/d')
2251
mover.rename('a', 'c')
2252
except errors.FileExists, e:
2254
self.failUnlessExists('a')
2255
self.failUnlessExists('c/d')
2258
class Bogus(Exception):
2262
class TestTransformRollback(tests.TestCaseWithTransport):
2264
class ExceptionFileMover(_FileMover):
2266
def __init__(self, bad_source=None, bad_target=None):
2267
_FileMover.__init__(self)
2268
self.bad_source = bad_source
2269
self.bad_target = bad_target
2271
def rename(self, source, target):
2272
if (self.bad_source is not None and
2273
source.endswith(self.bad_source)):
2275
elif (self.bad_target is not None and
2276
target.endswith(self.bad_target)):
2279
_FileMover.rename(self, source, target)
2281
def test_rollback_rename(self):
2282
tree = self.make_branch_and_tree('.')
2283
self.build_tree(['a/', 'a/b'])
2284
tt = TreeTransform(tree)
2285
self.addCleanup(tt.finalize)
2286
a_id = tt.trans_id_tree_path('a')
2287
tt.adjust_path('c', tt.root, a_id)
2288
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2289
self.assertRaises(Bogus, tt.apply,
2290
_mover=self.ExceptionFileMover(bad_source='a'))
2291
self.failUnlessExists('a')
2292
self.failUnlessExists('a/b')
2294
self.failUnlessExists('c')
2295
self.failUnlessExists('c/d')
2297
def test_rollback_rename_into_place(self):
2298
tree = self.make_branch_and_tree('.')
2299
self.build_tree(['a/', 'a/b'])
2300
tt = TreeTransform(tree)
2301
self.addCleanup(tt.finalize)
2302
a_id = tt.trans_id_tree_path('a')
2303
tt.adjust_path('c', tt.root, a_id)
2304
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2305
self.assertRaises(Bogus, tt.apply,
2306
_mover=self.ExceptionFileMover(bad_target='c/d'))
2307
self.failUnlessExists('a')
2308
self.failUnlessExists('a/b')
2310
self.failUnlessExists('c')
2311
self.failUnlessExists('c/d')
2313
def test_rollback_deletion(self):
2314
tree = self.make_branch_and_tree('.')
2315
self.build_tree(['a/', 'a/b'])
2316
tt = TreeTransform(tree)
2317
self.addCleanup(tt.finalize)
2318
a_id = tt.trans_id_tree_path('a')
2319
tt.delete_contents(a_id)
2320
tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
2321
self.assertRaises(Bogus, tt.apply,
2322
_mover=self.ExceptionFileMover(bad_target='d'))
2323
self.failUnlessExists('a')
2324
self.failUnlessExists('a/b')
2326
def test_resolve_no_parent(self):
2327
wt = self.make_branch_and_tree('.')
2328
tt = TreeTransform(wt)
2329
self.addCleanup(tt.finalize)
2330
parent = tt.trans_id_file_id('parent-id')
2331
tt.new_file('file', parent, 'Contents')
2332
resolve_conflicts(tt)
2335
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
2336
('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
2338
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
2339
('', ''), ('directory', 'directory'), (False, None))
2342
class TestTransformPreview(tests.TestCaseWithTransport):
2344
def create_tree(self):
2345
tree = self.make_branch_and_tree('.')
2346
self.build_tree_contents([('a', 'content 1')])
2347
tree.set_root_id('TREE_ROOT')
2348
tree.add('a', 'a-id')
2349
tree.commit('rev1', rev_id='rev1')
2350
return tree.branch.repository.revision_tree('rev1')
2352
def get_empty_preview(self):
2353
repository = self.make_repository('repo')
2354
tree = repository.revision_tree(_mod_revision.NULL_REVISION)
2355
preview = TransformPreview(tree)
2356
self.addCleanup(preview.finalize)
2359
def test_transform_preview(self):
2360
revision_tree = self.create_tree()
2361
preview = TransformPreview(revision_tree)
2362
self.addCleanup(preview.finalize)
2364
def test_transform_preview_tree(self):
2365
revision_tree = self.create_tree()
2366
preview = TransformPreview(revision_tree)
2367
self.addCleanup(preview.finalize)
2368
preview.get_preview_tree()
2370
def test_transform_new_file(self):
2371
revision_tree = self.create_tree()
2372
preview = TransformPreview(revision_tree)
2373
self.addCleanup(preview.finalize)
2374
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2375
preview_tree = preview.get_preview_tree()
2376
self.assertEqual(preview_tree.kind('file2-id'), 'file')
2378
preview_tree.get_file('file2-id').read(), 'content B\n')
2380
def test_diff_preview_tree(self):
2381
revision_tree = self.create_tree()
2382
preview = TransformPreview(revision_tree)
2383
self.addCleanup(preview.finalize)
2384
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2385
preview_tree = preview.get_preview_tree()
2387
show_diff_trees(revision_tree, preview_tree, out)
2388
lines = out.getvalue().splitlines()
2389
self.assertEqual(lines[0], "=== added file 'file2'")
2390
# 3 lines of diff administrivia
2391
self.assertEqual(lines[4], "+content B")
2393
def test_transform_conflicts(self):
2394
revision_tree = self.create_tree()
2395
preview = TransformPreview(revision_tree)
2396
self.addCleanup(preview.finalize)
2397
preview.new_file('a', preview.root, 'content 2')
2398
resolve_conflicts(preview)
2399
trans_id = preview.trans_id_file_id('a-id')
2400
self.assertEqual('a.moved', preview.final_name(trans_id))
2402
def get_tree_and_preview_tree(self):
2403
revision_tree = self.create_tree()
2404
preview = TransformPreview(revision_tree)
2405
self.addCleanup(preview.finalize)
2406
a_trans_id = preview.trans_id_file_id('a-id')
2407
preview.delete_contents(a_trans_id)
2408
preview.create_file('b content', a_trans_id)
2409
preview_tree = preview.get_preview_tree()
2410
return revision_tree, preview_tree
2412
def test_iter_changes(self):
2413
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2414
root = revision_tree.inventory.root.file_id
2415
self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
2416
(root, root), ('a', 'a'), ('file', 'file'),
2418
list(preview_tree.iter_changes(revision_tree)))
2420
def test_include_unchanged_succeeds(self):
2421
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2422
changes = preview_tree.iter_changes(revision_tree,
2423
include_unchanged=True)
2424
root = revision_tree.inventory.root.file_id
2426
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2428
def test_specific_files(self):
2429
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2430
changes = preview_tree.iter_changes(revision_tree,
2431
specific_files=[''])
2432
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2434
def test_want_unversioned(self):
2435
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2436
changes = preview_tree.iter_changes(revision_tree,
2437
want_unversioned=True)
2438
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2440
def test_ignore_extra_trees_no_specific_files(self):
2441
# extra_trees is harmless without specific_files, so we'll silently
2442
# accept it, even though we won't use it.
2443
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2444
preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
2446
def test_ignore_require_versioned_no_specific_files(self):
2447
# require_versioned is meaningless without specific_files.
2448
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2449
preview_tree.iter_changes(revision_tree, require_versioned=False)
2451
def test_ignore_pb(self):
2452
# pb could be supported, but TT.iter_changes doesn't support it.
2453
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2454
preview_tree.iter_changes(revision_tree)
2456
def test_kind(self):
2457
revision_tree = self.create_tree()
2458
preview = TransformPreview(revision_tree)
2459
self.addCleanup(preview.finalize)
2460
preview.new_file('file', preview.root, 'contents', 'file-id')
2461
preview.new_directory('directory', preview.root, 'dir-id')
2462
preview_tree = preview.get_preview_tree()
2463
self.assertEqual('file', preview_tree.kind('file-id'))
2464
self.assertEqual('directory', preview_tree.kind('dir-id'))
2466
def test_get_file_mtime(self):
2467
preview = self.get_empty_preview()
2468
file_trans_id = preview.new_file('file', preview.root, 'contents',
2470
limbo_path = preview._limbo_name(file_trans_id)
2471
preview_tree = preview.get_preview_tree()
2472
self.assertEqual(os.stat(limbo_path).st_mtime,
2473
preview_tree.get_file_mtime('file-id'))
2475
def test_get_file_mtime_renamed(self):
2476
work_tree = self.make_branch_and_tree('tree')
2477
self.build_tree(['tree/file'])
2478
work_tree.add('file', 'file-id')
2479
preview = TransformPreview(work_tree)
2480
self.addCleanup(preview.finalize)
2481
file_trans_id = preview.trans_id_tree_file_id('file-id')
2482
preview.adjust_path('renamed', preview.root, file_trans_id)
2483
preview_tree = preview.get_preview_tree()
2484
preview_mtime = preview_tree.get_file_mtime('file-id', 'renamed')
2485
work_mtime = work_tree.get_file_mtime('file-id', 'file')
2487
def test_get_file(self):
2488
preview = self.get_empty_preview()
2489
preview.new_file('file', preview.root, 'contents', 'file-id')
2490
preview_tree = preview.get_preview_tree()
2491
tree_file = preview_tree.get_file('file-id')
2493
self.assertEqual('contents', tree_file.read())
2497
def test_get_symlink_target(self):
2498
self.requireFeature(SymlinkFeature)
2499
preview = self.get_empty_preview()
2500
preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
2501
preview_tree = preview.get_preview_tree()
2502
self.assertEqual('target',
2503
preview_tree.get_symlink_target('symlink-id'))
2505
def test_all_file_ids(self):
2506
tree = self.make_branch_and_tree('tree')
2507
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
2508
tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
2509
preview = TransformPreview(tree)
2510
self.addCleanup(preview.finalize)
2511
preview.unversion_file(preview.trans_id_file_id('b-id'))
2512
c_trans_id = preview.trans_id_file_id('c-id')
2513
preview.unversion_file(c_trans_id)
2514
preview.version_file('c-id', c_trans_id)
2515
preview_tree = preview.get_preview_tree()
2516
self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
2517
preview_tree.all_file_ids())
2519
def test_path2id_deleted_unchanged(self):
2520
tree = self.make_branch_and_tree('tree')
2521
self.build_tree(['tree/unchanged', 'tree/deleted'])
2522
tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
2523
preview = TransformPreview(tree)
2524
self.addCleanup(preview.finalize)
2525
preview.unversion_file(preview.trans_id_file_id('deleted-id'))
2526
preview_tree = preview.get_preview_tree()
2527
self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
2528
self.assertIs(None, preview_tree.path2id('deleted'))
2530
def test_path2id_created(self):
2531
tree = self.make_branch_and_tree('tree')
2532
self.build_tree(['tree/unchanged'])
2533
tree.add(['unchanged'], ['unchanged-id'])
2534
preview = TransformPreview(tree)
2535
self.addCleanup(preview.finalize)
2536
preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
2537
'contents', 'new-id')
2538
preview_tree = preview.get_preview_tree()
2539
self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
2541
def test_path2id_moved(self):
2542
tree = self.make_branch_and_tree('tree')
2543
self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
2544
tree.add(['old_parent', 'old_parent/child'],
2545
['old_parent-id', 'child-id'])
2546
preview = TransformPreview(tree)
2547
self.addCleanup(preview.finalize)
2548
new_parent = preview.new_directory('new_parent', preview.root,
2550
preview.adjust_path('child', new_parent,
2551
preview.trans_id_file_id('child-id'))
2552
preview_tree = preview.get_preview_tree()
2553
self.assertIs(None, preview_tree.path2id('old_parent/child'))
2554
self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
2556
def test_path2id_renamed_parent(self):
2557
tree = self.make_branch_and_tree('tree')
2558
self.build_tree(['tree/old_name/', 'tree/old_name/child'])
2559
tree.add(['old_name', 'old_name/child'],
2560
['parent-id', 'child-id'])
2561
preview = TransformPreview(tree)
2562
self.addCleanup(preview.finalize)
2563
preview.adjust_path('new_name', preview.root,
2564
preview.trans_id_file_id('parent-id'))
2565
preview_tree = preview.get_preview_tree()
2566
self.assertIs(None, preview_tree.path2id('old_name/child'))
2567
self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
2569
def assertMatchingIterEntries(self, tt, specific_file_ids=None):
2570
preview_tree = tt.get_preview_tree()
2571
preview_result = list(preview_tree.iter_entries_by_dir(
2575
actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
2576
self.assertEqual(actual_result, preview_result)
2578
def test_iter_entries_by_dir_new(self):
2579
tree = self.make_branch_and_tree('tree')
2580
tt = TreeTransform(tree)
2581
tt.new_file('new', tt.root, 'contents', 'new-id')
2582
self.assertMatchingIterEntries(tt)
2584
def test_iter_entries_by_dir_deleted(self):
2585
tree = self.make_branch_and_tree('tree')
2586
self.build_tree(['tree/deleted'])
2587
tree.add('deleted', 'deleted-id')
2588
tt = TreeTransform(tree)
2589
tt.delete_contents(tt.trans_id_file_id('deleted-id'))
2590
self.assertMatchingIterEntries(tt)
2592
def test_iter_entries_by_dir_unversioned(self):
2593
tree = self.make_branch_and_tree('tree')
2594
self.build_tree(['tree/removed'])
2595
tree.add('removed', 'removed-id')
2596
tt = TreeTransform(tree)
2597
tt.unversion_file(tt.trans_id_file_id('removed-id'))
2598
self.assertMatchingIterEntries(tt)
2600
def test_iter_entries_by_dir_moved(self):
2601
tree = self.make_branch_and_tree('tree')
2602
self.build_tree(['tree/moved', 'tree/new_parent/'])
2603
tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
2604
tt = TreeTransform(tree)
2605
tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
2606
tt.trans_id_file_id('moved-id'))
2607
self.assertMatchingIterEntries(tt)
2609
def test_iter_entries_by_dir_specific_file_ids(self):
2610
tree = self.make_branch_and_tree('tree')
2611
tree.set_root_id('tree-root-id')
2612
self.build_tree(['tree/parent/', 'tree/parent/child'])
2613
tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
2614
tt = TreeTransform(tree)
2615
self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
2617
def test_symlink_content_summary(self):
2618
self.requireFeature(SymlinkFeature)
2619
preview = self.get_empty_preview()
2620
preview.new_symlink('path', preview.root, 'target', 'path-id')
2621
summary = preview.get_preview_tree().path_content_summary('path')
2622
self.assertEqual(('symlink', None, None, 'target'), summary)
2624
def test_missing_content_summary(self):
2625
preview = self.get_empty_preview()
2626
summary = preview.get_preview_tree().path_content_summary('path')
2627
self.assertEqual(('missing', None, None, None), summary)
2629
def test_deleted_content_summary(self):
2630
tree = self.make_branch_and_tree('tree')
2631
self.build_tree(['tree/path/'])
2633
preview = TransformPreview(tree)
2634
self.addCleanup(preview.finalize)
2635
preview.delete_contents(preview.trans_id_tree_path('path'))
2636
summary = preview.get_preview_tree().path_content_summary('path')
2637
self.assertEqual(('missing', None, None, None), summary)
2639
def test_file_content_summary_executable(self):
2640
preview = self.get_empty_preview()
2641
path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2642
preview.set_executability(True, path_id)
2643
summary = preview.get_preview_tree().path_content_summary('path')
2644
self.assertEqual(4, len(summary))
2645
self.assertEqual('file', summary[0])
2646
# size must be known
2647
self.assertEqual(len('contents'), summary[1])
2649
self.assertEqual(True, summary[2])
2650
# will not have hash (not cheap to determine)
2651
self.assertIs(None, summary[3])
2653
def test_change_executability(self):
2654
tree = self.make_branch_and_tree('tree')
2655
self.build_tree(['tree/path'])
2657
preview = TransformPreview(tree)
2658
self.addCleanup(preview.finalize)
2659
path_id = preview.trans_id_tree_path('path')
2660
preview.set_executability(True, path_id)
2661
summary = preview.get_preview_tree().path_content_summary('path')
2662
self.assertEqual(True, summary[2])
2664
def test_file_content_summary_non_exec(self):
2665
preview = self.get_empty_preview()
2666
preview.new_file('path', preview.root, 'contents', 'path-id')
2667
summary = preview.get_preview_tree().path_content_summary('path')
2668
self.assertEqual(4, len(summary))
2669
self.assertEqual('file', summary[0])
2670
# size must be known
2671
self.assertEqual(len('contents'), summary[1])
2673
self.assertEqual(False, summary[2])
2674
# will not have hash (not cheap to determine)
2675
self.assertIs(None, summary[3])
2677
def test_dir_content_summary(self):
2678
preview = self.get_empty_preview()
2679
preview.new_directory('path', preview.root, 'path-id')
2680
summary = preview.get_preview_tree().path_content_summary('path')
2681
self.assertEqual(('directory', None, None, None), summary)
2683
def test_tree_content_summary(self):
2684
preview = self.get_empty_preview()
2685
path = preview.new_directory('path', preview.root, 'path-id')
2686
preview.set_tree_reference('rev-1', path)
2687
summary = preview.get_preview_tree().path_content_summary('path')
2688
self.assertEqual(4, len(summary))
2689
self.assertEqual('tree-reference', summary[0])
2691
def test_annotate(self):
2692
tree = self.make_branch_and_tree('tree')
2693
self.build_tree_contents([('tree/file', 'a\n')])
2694
tree.add('file', 'file-id')
2695
tree.commit('a', rev_id='one')
2696
self.build_tree_contents([('tree/file', 'a\nb\n')])
2697
preview = TransformPreview(tree)
2698
self.addCleanup(preview.finalize)
2699
file_trans_id = preview.trans_id_file_id('file-id')
2700
preview.delete_contents(file_trans_id)
2701
preview.create_file('a\nb\nc\n', file_trans_id)
2702
preview_tree = preview.get_preview_tree()
2708
annotation = preview_tree.annotate_iter('file-id', 'me:')
2709
self.assertEqual(expected, annotation)
2711
def test_annotate_missing(self):
2712
preview = self.get_empty_preview()
2713
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2714
preview_tree = preview.get_preview_tree()
2720
annotation = preview_tree.annotate_iter('file-id', 'me:')
2721
self.assertEqual(expected, annotation)
2723
def test_annotate_rename(self):
2724
tree = self.make_branch_and_tree('tree')
2725
self.build_tree_contents([('tree/file', 'a\n')])
2726
tree.add('file', 'file-id')
2727
tree.commit('a', rev_id='one')
2728
preview = TransformPreview(tree)
2729
self.addCleanup(preview.finalize)
2730
file_trans_id = preview.trans_id_file_id('file-id')
2731
preview.adjust_path('newname', preview.root, file_trans_id)
2732
preview_tree = preview.get_preview_tree()
2736
annotation = preview_tree.annotate_iter('file-id', 'me:')
2737
self.assertEqual(expected, annotation)
2739
def test_annotate_deleted(self):
2740
tree = self.make_branch_and_tree('tree')
2741
self.build_tree_contents([('tree/file', 'a\n')])
2742
tree.add('file', 'file-id')
2743
tree.commit('a', rev_id='one')
2744
self.build_tree_contents([('tree/file', 'a\nb\n')])
2745
preview = TransformPreview(tree)
2746
self.addCleanup(preview.finalize)
2747
file_trans_id = preview.trans_id_file_id('file-id')
2748
preview.delete_contents(file_trans_id)
2749
preview_tree = preview.get_preview_tree()
2750
annotation = preview_tree.annotate_iter('file-id', 'me:')
2751
self.assertIs(None, annotation)
2753
def test_stored_kind(self):
2754
preview = self.get_empty_preview()
2755
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2756
preview_tree = preview.get_preview_tree()
2757
self.assertEqual('file', preview_tree.stored_kind('file-id'))
2759
def test_is_executable(self):
2760
preview = self.get_empty_preview()
2761
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2762
preview.set_executability(True, preview.trans_id_file_id('file-id'))
2763
preview_tree = preview.get_preview_tree()
2764
self.assertEqual(True, preview_tree.is_executable('file-id'))
2766
def test_get_set_parent_ids(self):
2767
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2768
self.assertEqual([], preview_tree.get_parent_ids())
2769
preview_tree.set_parent_ids(['rev-1'])
2770
self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
2772
def test_plan_file_merge(self):
2773
work_a = self.make_branch_and_tree('wta')
2774
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2775
work_a.add('file', 'file-id')
2776
base_id = work_a.commit('base version')
2777
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2778
preview = TransformPreview(work_a)
2779
self.addCleanup(preview.finalize)
2780
trans_id = preview.trans_id_file_id('file-id')
2781
preview.delete_contents(trans_id)
2782
preview.create_file('b\nc\nd\ne\n', trans_id)
2783
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2784
tree_a = preview.get_preview_tree()
2785
tree_a.set_parent_ids([base_id])
2787
('killed-a', 'a\n'),
2788
('killed-b', 'b\n'),
2789
('unchanged', 'c\n'),
2790
('unchanged', 'd\n'),
2793
], list(tree_a.plan_file_merge('file-id', tree_b)))
2795
def test_plan_file_merge_revision_tree(self):
2796
work_a = self.make_branch_and_tree('wta')
2797
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2798
work_a.add('file', 'file-id')
2799
base_id = work_a.commit('base version')
2800
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2801
preview = TransformPreview(work_a.basis_tree())
2802
self.addCleanup(preview.finalize)
2803
trans_id = preview.trans_id_file_id('file-id')
2804
preview.delete_contents(trans_id)
2805
preview.create_file('b\nc\nd\ne\n', trans_id)
2806
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2807
tree_a = preview.get_preview_tree()
2808
tree_a.set_parent_ids([base_id])
2810
('killed-a', 'a\n'),
2811
('killed-b', 'b\n'),
2812
('unchanged', 'c\n'),
2813
('unchanged', 'd\n'),
2816
], list(tree_a.plan_file_merge('file-id', tree_b)))
2818
def test_walkdirs(self):
2819
preview = self.get_empty_preview()
2820
root = preview.new_directory('', ROOT_PARENT, 'tree-root')
2821
# FIXME: new_directory should mark root.
2822
preview.fixup_new_roots()
2823
preview_tree = preview.get_preview_tree()
2824
file_trans_id = preview.new_file('a', preview.root, 'contents',
2826
expected = [(('', 'tree-root'),
2827
[('a', 'a', 'file', None, 'a-id', 'file')])]
2828
self.assertEqual(expected, list(preview_tree.walkdirs()))
2830
def test_extras(self):
2831
work_tree = self.make_branch_and_tree('tree')
2832
self.build_tree(['tree/removed-file', 'tree/existing-file',
2833
'tree/not-removed-file'])
2834
work_tree.add(['removed-file', 'not-removed-file'])
2835
preview = TransformPreview(work_tree)
2836
self.addCleanup(preview.finalize)
2837
preview.new_file('new-file', preview.root, 'contents')
2838
preview.new_file('new-versioned-file', preview.root, 'contents',
2840
tree = preview.get_preview_tree()
2841
preview.unversion_file(preview.trans_id_tree_path('removed-file'))
2842
self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
2845
def test_merge_into_preview(self):
2846
work_tree = self.make_branch_and_tree('tree')
2847
self.build_tree_contents([('tree/file','b\n')])
2848
work_tree.add('file', 'file-id')
2849
work_tree.commit('first commit')
2850
child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
2851
self.build_tree_contents([('child/file','b\nc\n')])
2852
child_tree.commit('child commit')
2853
child_tree.lock_write()
2854
self.addCleanup(child_tree.unlock)
2855
work_tree.lock_write()
2856
self.addCleanup(work_tree.unlock)
2857
preview = TransformPreview(work_tree)
2858
self.addCleanup(preview.finalize)
2859
file_trans_id = preview.trans_id_file_id('file-id')
2860
preview.delete_contents(file_trans_id)
2861
preview.create_file('a\nb\n', file_trans_id)
2862
preview_tree = preview.get_preview_tree()
2863
merger = Merger.from_revision_ids(None, preview_tree,
2864
child_tree.branch.last_revision(),
2865
other_branch=child_tree.branch,
2866
tree_branch=work_tree.branch)
2867
merger.merge_type = Merge3Merger
2868
tt = merger.make_merger().make_preview_transform()
2869
self.addCleanup(tt.finalize)
2870
final_tree = tt.get_preview_tree()
2871
self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
2873
def test_merge_preview_into_workingtree(self):
2874
tree = self.make_branch_and_tree('tree')
2875
tree.set_root_id('TREE_ROOT')
2876
tt = TransformPreview(tree)
2877
self.addCleanup(tt.finalize)
2878
tt.new_file('name', tt.root, 'content', 'file-id')
2879
tree2 = self.make_branch_and_tree('tree2')
2880
tree2.set_root_id('TREE_ROOT')
2881
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2882
None, tree.basis_tree())
2883
merger.merge_type = Merge3Merger
2886
def test_merge_preview_into_workingtree_handles_conflicts(self):
2887
tree = self.make_branch_and_tree('tree')
2888
self.build_tree_contents([('tree/foo', 'bar')])
2889
tree.add('foo', 'foo-id')
2891
tt = TransformPreview(tree)
2892
self.addCleanup(tt.finalize)
2893
trans_id = tt.trans_id_file_id('foo-id')
2894
tt.delete_contents(trans_id)
2895
tt.create_file('baz', trans_id)
2896
tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2897
self.build_tree_contents([('tree2/foo', 'qux')])
2899
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2900
pb, tree.basis_tree())
2901
merger.merge_type = Merge3Merger
2904
def test_is_executable(self):
2905
tree = self.make_branch_and_tree('tree')
2906
preview = TransformPreview(tree)
2907
self.addCleanup(preview.finalize)
2908
preview.new_file('foo', preview.root, 'bar', 'baz-id')
2909
preview_tree = preview.get_preview_tree()
2910
self.assertEqual(False, preview_tree.is_executable('baz-id',
2912
self.assertEqual(False, preview_tree.is_executable('baz-id'))
2914
def test_commit_preview_tree(self):
2915
tree = self.make_branch_and_tree('tree')
2916
rev_id = tree.commit('rev1')
2917
tree.branch.lock_write()
2918
self.addCleanup(tree.branch.unlock)
2919
tt = TransformPreview(tree)
2920
tt.new_file('file', tt.root, 'contents', 'file_id')
2921
self.addCleanup(tt.finalize)
2922
preview = tt.get_preview_tree()
2923
preview.set_parent_ids([rev_id])
2924
builder = tree.branch.get_commit_builder([rev_id])
2925
list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
2926
builder.finish_inventory()
2927
rev2_id = builder.commit('rev2')
2928
rev2_tree = tree.branch.repository.revision_tree(rev2_id)
2929
self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
2931
def test_ascii_limbo_paths(self):
2932
self.requireFeature(tests.UnicodeFilenameFeature)
2933
branch = self.make_branch('any')
2934
tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
2935
tt = TransformPreview(tree)
2936
self.addCleanup(tt.finalize)
2937
foo_id = tt.new_directory('', ROOT_PARENT)
2938
bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
2939
limbo_path = tt._limbo_name(bar_id)
2940
self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
2943
class FakeSerializer(object):
2944
"""Serializer implementation that simply returns the input.
2946
The input is returned in the order used by pack.ContainerPushParser.
2949
def bytes_record(bytes, names):
2953
class TestSerializeTransform(tests.TestCaseWithTransport):
2955
_test_needs_features = [tests.UnicodeFilenameFeature]
2957
def get_preview(self, tree=None):
2959
tree = self.make_branch_and_tree('tree')
2960
tt = TransformPreview(tree)
2961
self.addCleanup(tt.finalize)
2964
def assertSerializesTo(self, expected, tt):
2965
records = list(tt.serialize(FakeSerializer()))
2966
self.assertEqual(expected, records)
2969
def default_attribs():
2974
'_new_executability': {},
2976
'_tree_path_ids': {'': 'new-0'},
2978
'_removed_contents': [],
2979
'_non_present_ids': {},
2982
def make_records(self, attribs, contents):
2984
(((('attribs'),),), bencode.bencode(attribs))]
2985
records.extend([(((n, k),), c) for n, k, c in contents])
2988
def creation_records(self):
2989
attribs = self.default_attribs()
2990
attribs['_id_number'] = 3
2991
attribs['_new_name'] = {
2992
'new-1': u'foo\u1234'.encode('utf-8'), 'new-2': 'qux'}
2993
attribs['_new_id'] = {'new-1': 'baz', 'new-2': 'quxx'}
2994
attribs['_new_parent'] = {'new-1': 'new-0', 'new-2': 'new-0'}
2995
attribs['_new_executability'] = {'new-1': 1}
2997
('new-1', 'file', 'i 1\nbar\n'),
2998
('new-2', 'directory', ''),
3000
return self.make_records(attribs, contents)
3002
def test_serialize_creation(self):
3003
tt = self.get_preview()
3004
tt.new_file(u'foo\u1234', tt.root, 'bar', 'baz', True)
3005
tt.new_directory('qux', tt.root, 'quxx')
3006
self.assertSerializesTo(self.creation_records(), tt)
3008
def test_deserialize_creation(self):
3009
tt = self.get_preview()
3010
tt.deserialize(iter(self.creation_records()))
3011
self.assertEqual(3, tt._id_number)
3012
self.assertEqual({'new-1': u'foo\u1234',
3013
'new-2': 'qux'}, tt._new_name)
3014
self.assertEqual({'new-1': 'baz', 'new-2': 'quxx'}, tt._new_id)
3015
self.assertEqual({'new-1': tt.root, 'new-2': tt.root}, tt._new_parent)
3016
self.assertEqual({'baz': 'new-1', 'quxx': 'new-2'}, tt._r_new_id)
3017
self.assertEqual({'new-1': True}, tt._new_executability)
3018
self.assertEqual({'new-1': 'file',
3019
'new-2': 'directory'}, tt._new_contents)
3020
foo_limbo = open(tt._limbo_name('new-1'), 'rb')
3022
foo_content = foo_limbo.read()
3025
self.assertEqual('bar', foo_content)
3027
def symlink_creation_records(self):
3028
attribs = self.default_attribs()
3029
attribs['_id_number'] = 2
3030
attribs['_new_name'] = {'new-1': u'foo\u1234'.encode('utf-8')}
3031
attribs['_new_parent'] = {'new-1': 'new-0'}
3032
contents = [('new-1', 'symlink', u'bar\u1234'.encode('utf-8'))]
3033
return self.make_records(attribs, contents)
3035
def test_serialize_symlink_creation(self):
3036
self.requireFeature(tests.SymlinkFeature)
3037
tt = self.get_preview()
3038
tt.new_symlink(u'foo\u1234', tt.root, u'bar\u1234')
3039
self.assertSerializesTo(self.symlink_creation_records(), tt)
3041
def test_deserialize_symlink_creation(self):
3042
self.requireFeature(tests.SymlinkFeature)
3043
tt = self.get_preview()
3044
tt.deserialize(iter(self.symlink_creation_records()))
3045
abspath = tt._limbo_name('new-1')
3046
foo_content = osutils.readlink(abspath)
3047
self.assertEqual(u'bar\u1234', foo_content)
3049
def make_destruction_preview(self):
3050
tree = self.make_branch_and_tree('.')
3051
self.build_tree([u'foo\u1234', 'bar'])
3052
tree.add([u'foo\u1234', 'bar'], ['foo-id', 'bar-id'])
3053
return self.get_preview(tree)
3055
def destruction_records(self):
3056
attribs = self.default_attribs()
3057
attribs['_id_number'] = 3
3058
attribs['_removed_id'] = ['new-1']
3059
attribs['_removed_contents'] = ['new-2']
3060
attribs['_tree_path_ids'] = {
3062
u'foo\u1234'.encode('utf-8'): 'new-1',
3065
return self.make_records(attribs, [])
3067
def test_serialize_destruction(self):
3068
tt = self.make_destruction_preview()
3069
foo_trans_id = tt.trans_id_tree_file_id('foo-id')
3070
tt.unversion_file(foo_trans_id)
3071
bar_trans_id = tt.trans_id_tree_file_id('bar-id')
3072
tt.delete_contents(bar_trans_id)
3073
self.assertSerializesTo(self.destruction_records(), tt)
3075
def test_deserialize_destruction(self):
3076
tt = self.make_destruction_preview()
3077
tt.deserialize(iter(self.destruction_records()))
3078
self.assertEqual({u'foo\u1234': 'new-1',
3080
'': tt.root}, tt._tree_path_ids)
3081
self.assertEqual({'new-1': u'foo\u1234',
3083
tt.root: ''}, tt._tree_id_paths)
3084
self.assertEqual(set(['new-1']), tt._removed_id)
3085
self.assertEqual(set(['new-2']), tt._removed_contents)
3087
def missing_records(self):
3088
attribs = self.default_attribs()
3089
attribs['_id_number'] = 2
3090
attribs['_non_present_ids'] = {
3092
return self.make_records(attribs, [])
3094
def test_serialize_missing(self):
3095
tt = self.get_preview()
3096
boo_trans_id = tt.trans_id_file_id('boo')
3097
self.assertSerializesTo(self.missing_records(), tt)
3099
def test_deserialize_missing(self):
3100
tt = self.get_preview()
3101
tt.deserialize(iter(self.missing_records()))
3102
self.assertEqual({'boo': 'new-1'}, tt._non_present_ids)
3104
def make_modification_preview(self):
3105
LINES_ONE = 'aa\nbb\ncc\ndd\n'
3106
LINES_TWO = 'z\nbb\nx\ndd\n'
3107
tree = self.make_branch_and_tree('tree')
3108
self.build_tree_contents([('tree/file', LINES_ONE)])
3109
tree.add('file', 'file-id')
3110
return self.get_preview(tree), LINES_TWO
3112
def modification_records(self):
3113
attribs = self.default_attribs()
3114
attribs['_id_number'] = 2
3115
attribs['_tree_path_ids'] = {
3118
attribs['_removed_contents'] = ['new-1']
3119
contents = [('new-1', 'file',
3120
'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
3121
return self.make_records(attribs, contents)
3123
def test_serialize_modification(self):
3124
tt, LINES = self.make_modification_preview()
3125
trans_id = tt.trans_id_file_id('file-id')
3126
tt.delete_contents(trans_id)
3127
tt.create_file(LINES, trans_id)
3128
self.assertSerializesTo(self.modification_records(), tt)
3130
def test_deserialize_modification(self):
3131
tt, LINES = self.make_modification_preview()
3132
tt.deserialize(iter(self.modification_records()))
3133
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
3135
def make_kind_change_preview(self):
3136
LINES = 'a\nb\nc\nd\n'
3137
tree = self.make_branch_and_tree('tree')
3138
self.build_tree(['tree/foo/'])
3139
tree.add('foo', 'foo-id')
3140
return self.get_preview(tree), LINES
3142
def kind_change_records(self):
3143
attribs = self.default_attribs()
3144
attribs['_id_number'] = 2
3145
attribs['_tree_path_ids'] = {
3148
attribs['_removed_contents'] = ['new-1']
3149
contents = [('new-1', 'file',
3150
'i 4\na\nb\nc\nd\n\n')]
3151
return self.make_records(attribs, contents)
3153
def test_serialize_kind_change(self):
3154
tt, LINES = self.make_kind_change_preview()
3155
trans_id = tt.trans_id_file_id('foo-id')
3156
tt.delete_contents(trans_id)
3157
tt.create_file(LINES, trans_id)
3158
self.assertSerializesTo(self.kind_change_records(), tt)
3160
def test_deserialize_kind_change(self):
3161
tt, LINES = self.make_kind_change_preview()
3162
tt.deserialize(iter(self.kind_change_records()))
3163
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
3165
def make_add_contents_preview(self):
3166
LINES = 'a\nb\nc\nd\n'
3167
tree = self.make_branch_and_tree('tree')
3168
self.build_tree(['tree/foo'])
3170
os.unlink('tree/foo')
3171
return self.get_preview(tree), LINES
3173
def add_contents_records(self):
3174
attribs = self.default_attribs()
3175
attribs['_id_number'] = 2
3176
attribs['_tree_path_ids'] = {
3179
contents = [('new-1', 'file',
3180
'i 4\na\nb\nc\nd\n\n')]
3181
return self.make_records(attribs, contents)
3183
def test_serialize_add_contents(self):
3184
tt, LINES = self.make_add_contents_preview()
3185
trans_id = tt.trans_id_tree_path('foo')
3186
tt.create_file(LINES, trans_id)
3187
self.assertSerializesTo(self.add_contents_records(), tt)
3189
def test_deserialize_add_contents(self):
3190
tt, LINES = self.make_add_contents_preview()
3191
tt.deserialize(iter(self.add_contents_records()))
3192
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
3194
def test_get_parents_lines(self):
3195
LINES_ONE = 'aa\nbb\ncc\ndd\n'
3196
LINES_TWO = 'z\nbb\nx\ndd\n'
3197
tree = self.make_branch_and_tree('tree')
3198
self.build_tree_contents([('tree/file', LINES_ONE)])
3199
tree.add('file', 'file-id')
3200
tt = self.get_preview(tree)
3201
trans_id = tt.trans_id_tree_path('file')
3202
self.assertEqual((['aa\n', 'bb\n', 'cc\n', 'dd\n'],),
3203
tt._get_parents_lines(trans_id))
3205
def test_get_parents_texts(self):
3206
LINES_ONE = 'aa\nbb\ncc\ndd\n'
3207
LINES_TWO = 'z\nbb\nx\ndd\n'
3208
tree = self.make_branch_and_tree('tree')
3209
self.build_tree_contents([('tree/file', LINES_ONE)])
3210
tree.add('file', 'file-id')
3211
tt = self.get_preview(tree)
3212
trans_id = tt.trans_id_tree_path('file')
3213
self.assertEqual((LINES_ONE,),
3214
tt._get_parents_texts(trans_id))