479
940
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')
943
def test_rename_fails(self):
944
self.requireFeature(features.not_running_as_root)
945
# see https://bugs.launchpad.net/bzr/+bug/491763
946
create, root_id = self.get_transform()
947
first_dir = create.new_directory('first-dir', root_id, 'first-id')
948
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,
951
if os.name == "posix" and sys.platform != "cygwin":
952
# posix filesystems fail on renaming if the readonly bit is set
953
osutils.make_readonly(self.wt.abspath('first-dir'))
954
elif os.name == "nt":
955
# windows filesystems fail on renaming open files
956
self.addCleanup(file(self.wt.abspath('myfile')).close)
958
self.skip("Don't know how to force a permissions error on rename")
959
# now transform to rename
960
rename_transform, root_id = self.get_transform()
961
file_trans_id = rename_transform.trans_id_file_id('myfile-id')
962
dir_id = rename_transform.trans_id_file_id('first-id')
963
rename_transform.adjust_path('newname', dir_id, file_trans_id)
964
e = self.assertRaises(errors.TransformRenameFailed,
965
rename_transform.apply)
967
# "Failed to rename .../work/.bzr/checkout/limbo/new-1
968
# to .../first-dir/newname: [Errno 13] Permission denied"
969
# On windows looks like:
970
# "Failed to rename .../work/myfile to
971
# .../work/.bzr/checkout/limbo/new-1: [Errno 13] Permission denied"
972
# This test isn't concerned with exactly what the error looks like,
973
# and the strerror will vary across OS and locales, but the assert
974
# that the exeception attributes are what we expect
975
self.assertEqual(e.errno, errno.EACCES)
976
if os.name == "posix":
977
self.assertEndsWith(e.to_path, "/first-dir/newname")
979
self.assertEqual(os.path.basename(e.from_path), "myfile")
981
def test_set_executability_order(self):
982
"""Ensure that executability behaves the same, no matter what order.
984
- create file and set executability simultaneously
985
- create file and set executability afterward
986
- unsetting the executability of a file whose executability has not been
987
declared should throw an exception (this may happen when a
988
merge attempts to create a file with a duplicate ID)
990
transform, root = self.get_transform()
993
self.addCleanup(wt.unlock)
994
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
996
sac = transform.new_file('set_after_creation', root,
997
'Set after creation', 'sac')
998
transform.set_executability(True, sac)
999
uws = transform.new_file('unset_without_set', root, 'Unset badly',
1001
self.assertRaises(KeyError, transform.set_executability, None, uws)
1003
self.assertTrue(wt.is_executable('soc'))
1004
self.assertTrue(wt.is_executable('sac'))
1006
def test_preserve_mode(self):
1007
"""File mode is preserved when replacing content"""
1008
if sys.platform == 'win32':
1009
raise TestSkipped('chmod has no effect on win32')
1010
transform, root = self.get_transform()
1011
transform.new_file('file1', root, 'contents', 'file1-id', True)
1013
self.wt.lock_write()
1014
self.addCleanup(self.wt.unlock)
1015
self.assertTrue(self.wt.is_executable('file1-id'))
1016
transform, root = self.get_transform()
1017
file1_id = transform.trans_id_tree_file_id('file1-id')
1018
transform.delete_contents(file1_id)
1019
transform.create_file('contents2', file1_id)
1021
self.assertTrue(self.wt.is_executable('file1-id'))
1023
def test__set_mode_stats_correctly(self):
1024
"""_set_mode stats to determine file mode."""
1025
if sys.platform == 'win32':
1026
raise TestSkipped('chmod has no effect on win32')
1030
def instrumented_stat(path):
1031
stat_paths.append(path)
1032
return real_stat(path)
1034
transform, root = self.get_transform()
1036
bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
1037
file_id='bar-id-1', executable=False)
1040
transform, root = self.get_transform()
1041
bar1_id = transform.trans_id_tree_path('bar')
1042
bar2_id = transform.trans_id_tree_path('bar2')
1044
os.stat = instrumented_stat
1045
transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
1048
transform.finalize()
1050
bar1_abspath = self.wt.abspath('bar')
1051
self.assertEqual([bar1_abspath], stat_paths)
1053
def test_iter_changes(self):
1054
self.wt.set_root_id('eert_toor')
1055
transform, root = self.get_transform()
1056
transform.new_file('old', root, 'blah', 'id-1', True)
1058
transform, root = self.get_transform()
1060
self.assertEqual([], list(transform.iter_changes()))
1061
old = transform.trans_id_tree_file_id('id-1')
1062
transform.unversion_file(old)
1063
self.assertEqual([('id-1', ('old', None), False, (True, False),
1064
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1065
(True, True))], list(transform.iter_changes()))
1066
transform.new_directory('new', root, 'id-1')
1067
self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
1068
('eert_toor', 'eert_toor'), ('old', 'new'),
1069
('file', 'directory'),
1070
(True, False))], list(transform.iter_changes()))
1072
transform.finalize()
1074
def test_iter_changes_new(self):
1075
self.wt.set_root_id('eert_toor')
1076
transform, root = self.get_transform()
1077
transform.new_file('old', root, 'blah')
1079
transform, root = self.get_transform()
1081
old = transform.trans_id_tree_path('old')
1082
transform.version_file('id-1', old)
1083
self.assertEqual([('id-1', (None, 'old'), False, (False, True),
1084
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1085
(False, False))], list(transform.iter_changes()))
1087
transform.finalize()
1089
def test_iter_changes_modifications(self):
1090
self.wt.set_root_id('eert_toor')
1091
transform, root = self.get_transform()
1092
transform.new_file('old', root, 'blah', 'id-1')
1093
transform.new_file('new', root, 'blah')
1094
transform.new_directory('subdir', root, 'subdir-id')
1096
transform, root = self.get_transform()
1098
old = transform.trans_id_tree_path('old')
1099
subdir = transform.trans_id_tree_file_id('subdir-id')
1100
new = transform.trans_id_tree_path('new')
1101
self.assertEqual([], list(transform.iter_changes()))
1104
transform.delete_contents(old)
1105
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
1106
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
1107
(False, False))], list(transform.iter_changes()))
1110
transform.create_file('blah', old)
1111
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
1112
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1113
(False, False))], list(transform.iter_changes()))
1114
transform.cancel_deletion(old)
1115
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
1116
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1117
(False, False))], list(transform.iter_changes()))
1118
transform.cancel_creation(old)
1120
# move file_id to a different file
1121
self.assertEqual([], list(transform.iter_changes()))
1122
transform.unversion_file(old)
1123
transform.version_file('id-1', new)
1124
transform.adjust_path('old', root, new)
1125
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
1126
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1127
(False, False))], list(transform.iter_changes()))
1128
transform.cancel_versioning(new)
1129
transform._removed_id = set()
1132
self.assertEqual([], list(transform.iter_changes()))
1133
transform.set_executability(True, old)
1134
self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
1135
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
1136
(False, True))], list(transform.iter_changes()))
1137
transform.set_executability(None, old)
1140
self.assertEqual([], list(transform.iter_changes()))
1141
transform.adjust_path('new', root, old)
1142
transform._new_parent = {}
1143
self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
1144
('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
1145
(False, False))], list(transform.iter_changes()))
1146
transform._new_name = {}
1149
self.assertEqual([], list(transform.iter_changes()))
1150
transform.adjust_path('new', subdir, old)
1151
transform._new_name = {}
1152
self.assertEqual([('id-1', ('old', 'subdir/old'), False,
1153
(True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
1154
('file', 'file'), (False, False))],
1155
list(transform.iter_changes()))
1156
transform._new_path = {}
1159
transform.finalize()
1161
def test_iter_changes_modified_bleed(self):
1162
self.wt.set_root_id('eert_toor')
1163
"""Modified flag should not bleed from one change to another"""
1164
# unfortunately, we have no guarantee that file1 (which is modified)
1165
# will be applied before file2. And if it's applied after file2, it
1166
# obviously can't bleed into file2's change output. But for now, it
1168
transform, root = self.get_transform()
1169
transform.new_file('file1', root, 'blah', 'id-1')
1170
transform.new_file('file2', root, 'blah', 'id-2')
1172
transform, root = self.get_transform()
1174
transform.delete_contents(transform.trans_id_file_id('id-1'))
1175
transform.set_executability(True,
1176
transform.trans_id_file_id('id-2'))
1177
self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
1178
('eert_toor', 'eert_toor'), ('file1', u'file1'),
1179
('file', None), (False, False)),
1180
('id-2', (u'file2', u'file2'), False, (True, True),
1181
('eert_toor', 'eert_toor'), ('file2', u'file2'),
1182
('file', 'file'), (False, True))],
1183
list(transform.iter_changes()))
1185
transform.finalize()
1187
def test_iter_changes_move_missing(self):
1188
"""Test moving ids with no files around"""
1189
self.wt.set_root_id('toor_eert')
1190
# Need two steps because versioning a non-existant file is a conflict.
1191
transform, root = self.get_transform()
1192
transform.new_directory('floater', root, 'floater-id')
1194
transform, root = self.get_transform()
1195
transform.delete_contents(transform.trans_id_tree_path('floater'))
1197
transform, root = self.get_transform()
1198
floater = transform.trans_id_tree_path('floater')
1200
transform.adjust_path('flitter', root, floater)
1201
self.assertEqual([('floater-id', ('floater', 'flitter'), False,
1202
(True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
1203
(None, None), (False, False))], list(transform.iter_changes()))
1205
transform.finalize()
1207
def test_iter_changes_pointless(self):
1208
"""Ensure that no-ops are not treated as modifications"""
1209
self.wt.set_root_id('eert_toor')
1210
transform, root = self.get_transform()
1211
transform.new_file('old', root, 'blah', 'id-1')
1212
transform.new_directory('subdir', root, 'subdir-id')
1214
transform, root = self.get_transform()
1216
old = transform.trans_id_tree_path('old')
1217
subdir = transform.trans_id_tree_file_id('subdir-id')
1218
self.assertEqual([], list(transform.iter_changes()))
1219
transform.delete_contents(subdir)
1220
transform.create_directory(subdir)
1221
transform.set_executability(False, old)
1222
transform.unversion_file(old)
1223
transform.version_file('id-1', old)
1224
transform.adjust_path('old', root, old)
1225
self.assertEqual([], list(transform.iter_changes()))
1227
transform.finalize()
1229
def test_rename_count(self):
1230
transform, root = self.get_transform()
1231
transform.new_file('name1', root, 'contents')
1232
self.assertEqual(transform.rename_count, 0)
1234
self.assertEqual(transform.rename_count, 1)
1235
transform2, root = self.get_transform()
1236
transform2.adjust_path('name2', root,
1237
transform2.trans_id_tree_path('name1'))
1238
self.assertEqual(transform2.rename_count, 0)
1240
self.assertEqual(transform2.rename_count, 2)
1242
def test_change_parent(self):
1243
"""Ensure that after we change a parent, the results are still right.
1245
Renames and parent changes on pending transforms can happen as part
1246
of conflict resolution, and are explicitly permitted by the
1249
This test ensures they work correctly with the rename-avoidance
1252
transform, root = self.get_transform()
1253
parent1 = transform.new_directory('parent1', root)
1254
child1 = transform.new_file('child1', parent1, 'contents')
1255
parent2 = transform.new_directory('parent2', root)
1256
transform.adjust_path('child1', parent2, child1)
1258
self.assertPathDoesNotExist(self.wt.abspath('parent1/child1'))
1259
self.assertPathExists(self.wt.abspath('parent2/child1'))
1260
# rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1261
# no rename for child1 (counting only renames during apply)
1262
self.assertEqual(2, transform.rename_count)
1264
def test_cancel_parent(self):
1265
"""Cancelling a parent doesn't cause deletion of a non-empty directory
1267
This is like the test_change_parent, except that we cancel the parent
1268
before adjusting the path. The transform must detect that the
1269
directory is non-empty, and move children to safe locations.
1271
transform, root = self.get_transform()
1272
parent1 = transform.new_directory('parent1', root)
1273
child1 = transform.new_file('child1', parent1, 'contents')
1274
child2 = transform.new_file('child2', parent1, 'contents')
1276
transform.cancel_creation(parent1)
1278
self.fail('Failed to move child1 before deleting parent1')
1279
transform.cancel_creation(child2)
1280
transform.create_directory(parent1)
1282
transform.cancel_creation(parent1)
1283
# If the transform incorrectly believes that child2 is still in
1284
# parent1's limbo directory, it will try to rename it and fail
1285
# because was already moved by the first cancel_creation.
1287
self.fail('Transform still thinks child2 is a child of parent1')
1288
parent2 = transform.new_directory('parent2', root)
1289
transform.adjust_path('child1', parent2, child1)
1291
self.assertPathDoesNotExist(self.wt.abspath('parent1'))
1292
self.assertPathExists(self.wt.abspath('parent2/child1'))
1293
# rename limbo/new-3 => parent2, rename limbo/new-2 => child1
1294
self.assertEqual(2, transform.rename_count)
1296
def test_adjust_and_cancel(self):
1297
"""Make sure adjust_path keeps track of limbo children properly"""
1298
transform, root = self.get_transform()
1299
parent1 = transform.new_directory('parent1', root)
1300
child1 = transform.new_file('child1', parent1, 'contents')
1301
parent2 = transform.new_directory('parent2', root)
1302
transform.adjust_path('child1', parent2, child1)
1303
transform.cancel_creation(child1)
1305
transform.cancel_creation(parent1)
1306
# if the transform thinks child1 is still in parent1's limbo
1307
# directory, it will attempt to move it and fail.
1309
self.fail('Transform still thinks child1 is a child of parent1')
1310
transform.finalize()
1312
def test_noname_contents(self):
1313
"""TreeTransform should permit deferring naming files."""
1314
transform, root = self.get_transform()
1315
parent = transform.trans_id_file_id('parent-id')
1317
transform.create_directory(parent)
1319
self.fail("Can't handle contents with no name")
1320
transform.finalize()
1322
def test_noname_contents_nested(self):
1323
"""TreeTransform should permit deferring naming files."""
1324
transform, root = self.get_transform()
1325
parent = transform.trans_id_file_id('parent-id')
1327
transform.create_directory(parent)
1329
self.fail("Can't handle contents with no name")
1330
child = transform.new_directory('child', parent)
1331
transform.adjust_path('parent', root, parent)
1333
self.assertPathExists(self.wt.abspath('parent/child'))
1334
self.assertEqual(1, transform.rename_count)
1336
def test_reuse_name(self):
1337
"""Avoid reusing the same limbo name for different files"""
1338
transform, root = self.get_transform()
1339
parent = transform.new_directory('parent', root)
1340
child1 = transform.new_directory('child', parent)
1342
child2 = transform.new_directory('child', parent)
1344
self.fail('Tranform tried to use the same limbo name twice')
1345
transform.adjust_path('child2', parent, child2)
1347
# limbo/new-1 => parent, limbo/new-3 => parent/child2
1348
# child2 is put into top-level limbo because child1 has already
1349
# claimed the direct limbo path when child2 is created. There is no
1350
# advantage in renaming files once they're in top-level limbo, except
1352
self.assertEqual(2, transform.rename_count)
1354
def test_reuse_when_first_moved(self):
1355
"""Don't avoid direct paths when it is safe to use them"""
1356
transform, root = self.get_transform()
1357
parent = transform.new_directory('parent', root)
1358
child1 = transform.new_directory('child', parent)
1359
transform.adjust_path('child1', parent, child1)
1360
child2 = transform.new_directory('child', parent)
1362
# limbo/new-1 => parent
1363
self.assertEqual(1, transform.rename_count)
1365
def test_reuse_after_cancel(self):
1366
"""Don't avoid direct paths when it is safe to use them"""
1367
transform, root = self.get_transform()
1368
parent2 = transform.new_directory('parent2', root)
1369
child1 = transform.new_directory('child1', parent2)
1370
transform.cancel_creation(parent2)
1371
transform.create_directory(parent2)
1372
child2 = transform.new_directory('child1', parent2)
1373
transform.adjust_path('child2', parent2, child1)
1375
# limbo/new-1 => parent2, limbo/new-2 => parent2/child1
1376
self.assertEqual(2, transform.rename_count)
1378
def test_finalize_order(self):
1379
"""Finalize must be done in child-to-parent order"""
1380
transform, root = self.get_transform()
1381
parent = transform.new_directory('parent', root)
1382
child = transform.new_directory('child', parent)
1384
transform.finalize()
1386
self.fail('Tried to remove parent before child1')
1388
def test_cancel_with_cancelled_child_should_succeed(self):
1389
transform, root = self.get_transform()
1390
parent = transform.new_directory('parent', root)
1391
child = transform.new_directory('child', parent)
1392
transform.cancel_creation(child)
1393
transform.cancel_creation(parent)
1394
transform.finalize()
1396
def test_rollback_on_directory_clash(self):
1398
wt = self.make_branch_and_tree('.')
1399
tt = TreeTransform(wt) # TreeTransform obtains write lock
1401
foo = tt.new_directory('foo', tt.root)
1402
tt.new_file('bar', foo, 'foobar')
1403
baz = tt.new_directory('baz', tt.root)
1404
tt.new_file('qux', baz, 'quux')
1405
# Ask for a rename 'foo' -> 'baz'
1406
tt.adjust_path('baz', tt.root, foo)
1407
# Lie to tt that we've already resolved all conflicts.
1408
tt.apply(no_conflicts=True)
1412
# The rename will fail because the target directory is not empty (but
1413
# raises FileExists anyway).
1414
err = self.assertRaises(errors.FileExists, tt_helper)
1415
self.assertContainsRe(str(err),
1416
"^File exists: .+/baz")
1418
def test_two_directories_clash(self):
1420
wt = self.make_branch_and_tree('.')
1421
tt = TreeTransform(wt) # TreeTransform obtains write lock
1423
foo_1 = tt.new_directory('foo', tt.root)
1424
tt.new_directory('bar', foo_1)
1425
# Adding the same directory with a different content
1426
foo_2 = tt.new_directory('foo', tt.root)
1427
tt.new_directory('baz', foo_2)
1428
# Lie to tt that we've already resolved all conflicts.
1429
tt.apply(no_conflicts=True)
1433
err = self.assertRaises(errors.FileExists, tt_helper)
1434
self.assertContainsRe(str(err),
1435
"^File exists: .+/foo")
1437
def test_two_directories_clash_finalize(self):
1439
wt = self.make_branch_and_tree('.')
1440
tt = TreeTransform(wt) # TreeTransform obtains write lock
1442
foo_1 = tt.new_directory('foo', tt.root)
1443
tt.new_directory('bar', foo_1)
1444
# Adding the same directory with a different content
1445
foo_2 = tt.new_directory('foo', tt.root)
1446
tt.new_directory('baz', foo_2)
1447
# Lie to tt that we've already resolved all conflicts.
1448
tt.apply(no_conflicts=True)
1452
err = self.assertRaises(errors.FileExists, tt_helper)
1453
self.assertContainsRe(str(err),
1454
"^File exists: .+/foo")
1456
def test_file_to_directory(self):
1457
wt = self.make_branch_and_tree('.')
1458
self.build_tree(['foo'])
1461
tt = TreeTransform(wt)
1462
self.addCleanup(tt.finalize)
1463
foo_trans_id = tt.trans_id_tree_path("foo")
1464
tt.delete_contents(foo_trans_id)
1465
tt.create_directory(foo_trans_id)
1466
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1467
tt.create_file(["aa\n"], bar_trans_id)
1468
tt.version_file("bar-1", bar_trans_id)
1470
self.assertPathExists("foo/bar")
1473
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1478
changes = wt.changes_from(wt.basis_tree())
1479
self.assertFalse(changes.has_changed(), changes)
1481
def test_file_to_symlink(self):
1482
self.requireFeature(SymlinkFeature)
1483
wt = self.make_branch_and_tree('.')
1484
self.build_tree(['foo'])
1487
tt = TreeTransform(wt)
1488
self.addCleanup(tt.finalize)
1489
foo_trans_id = tt.trans_id_tree_path("foo")
1490
tt.delete_contents(foo_trans_id)
1491
tt.create_symlink("bar", foo_trans_id)
1493
self.assertPathExists("foo")
1495
self.addCleanup(wt.unlock)
1496
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1499
def test_dir_to_file(self):
1500
wt = self.make_branch_and_tree('.')
1501
self.build_tree(['foo/', 'foo/bar'])
1502
wt.add(['foo', 'foo/bar'])
1504
tt = TreeTransform(wt)
1505
self.addCleanup(tt.finalize)
1506
foo_trans_id = tt.trans_id_tree_path("foo")
1507
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1508
tt.delete_contents(foo_trans_id)
1509
tt.delete_versioned(bar_trans_id)
1510
tt.create_file(["aa\n"], foo_trans_id)
1512
self.assertPathExists("foo")
1514
self.addCleanup(wt.unlock)
1515
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1518
def test_dir_to_hardlink(self):
1519
self.requireFeature(HardlinkFeature)
1520
wt = self.make_branch_and_tree('.')
1521
self.build_tree(['foo/', 'foo/bar'])
1522
wt.add(['foo', 'foo/bar'])
1524
tt = TreeTransform(wt)
1525
self.addCleanup(tt.finalize)
1526
foo_trans_id = tt.trans_id_tree_path("foo")
1527
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1528
tt.delete_contents(foo_trans_id)
1529
tt.delete_versioned(bar_trans_id)
1530
self.build_tree(['baz'])
1531
tt.create_hardlink("baz", foo_trans_id)
1533
self.assertPathExists("foo")
1534
self.assertPathExists("baz")
1536
self.addCleanup(wt.unlock)
1537
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1540
def test_no_final_path(self):
1541
transform, root = self.get_transform()
1542
trans_id = transform.trans_id_file_id('foo')
1543
transform.create_file('bar', trans_id)
1544
transform.cancel_creation(trans_id)
1547
def test_create_from_tree(self):
1548
tree1 = self.make_branch_and_tree('tree1')
1549
self.build_tree_contents([('tree1/foo/',), ('tree1/bar', 'baz')])
1550
tree1.add(['foo', 'bar'], ['foo-id', 'bar-id'])
1551
tree2 = self.make_branch_and_tree('tree2')
1552
tt = TreeTransform(tree2)
1553
foo_trans_id = tt.create_path('foo', tt.root)
1554
create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1555
bar_trans_id = tt.create_path('bar', tt.root)
1556
create_from_tree(tt, bar_trans_id, tree1, 'bar-id')
1558
self.assertEqual('directory', osutils.file_kind('tree2/foo'))
1559
self.assertFileEqual('baz', 'tree2/bar')
1561
def test_create_from_tree_bytes(self):
1562
"""Provided lines are used instead of tree content."""
1563
tree1 = self.make_branch_and_tree('tree1')
1564
self.build_tree_contents([('tree1/foo', 'bar'),])
1565
tree1.add('foo', 'foo-id')
1566
tree2 = self.make_branch_and_tree('tree2')
1567
tt = TreeTransform(tree2)
1568
foo_trans_id = tt.create_path('foo', tt.root)
1569
create_from_tree(tt, foo_trans_id, tree1, 'foo-id', bytes='qux')
1571
self.assertFileEqual('qux', 'tree2/foo')
1573
def test_create_from_tree_symlink(self):
1574
self.requireFeature(SymlinkFeature)
1575
tree1 = self.make_branch_and_tree('tree1')
1576
os.symlink('bar', 'tree1/foo')
1577
tree1.add('foo', 'foo-id')
1578
tt = TreeTransform(self.make_branch_and_tree('tree2'))
1579
foo_trans_id = tt.create_path('foo', tt.root)
1580
create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1582
self.assertEqual('bar', os.readlink('tree2/foo'))
494
1585
class TransformGroup(object):
495
def __init__(self, dirname):
1587
def __init__(self, dirname, root_id):
496
1588
self.name = dirname
497
1589
os.mkdir(dirname)
498
1590
self.wt = BzrDir.create_standalone_workingtree(dirname)
1591
self.wt.set_root_id(root_id)
499
1592
self.b = self.wt.branch
500
1593
self.tt = TreeTransform(self.wt)
501
1594
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
503
1597
def conflict_text(tree, merge):
504
1598
template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
505
1599
return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
508
1602
class TestTransformMerge(TestCaseInTempDir):
509
1604
def test_text_merge(self):
510
base = TransformGroup("base")
1605
root_id = generate_ids.gen_root_id()
1606
base = TransformGroup("base", root_id)
511
1607
base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
512
1608
base.tt.new_file('b', base.root, 'b1', 'b')
513
1609
base.tt.new_file('c', base.root, 'c', 'c')
698
1798
a.add(['foo', 'foo/bar', 'foo/baz'])
699
1799
a.commit('initial commit')
700
1800
b = BzrDir.create_standalone_workingtree('b')
701
build_tree(a.basis_tree(), b)
1801
basis = a.basis_tree()
1803
self.addCleanup(basis.unlock)
1804
build_tree(basis, b)
702
1805
self.assertIs(os.path.isdir('b/foo'), True)
703
1806
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
704
1807
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
706
class MockTransform(object):
708
def has_named_child(self, by_parent, parent_id, name):
709
for child_id in by_parent[parent_id]:
713
elif name == "name.~%s~" % child_id:
717
class MockEntry(object):
719
object.__init__(self)
722
class TestGetBackupName(TestCase):
723
def test_get_backup_name(self):
1809
def test_build_with_references(self):
1810
tree = self.make_branch_and_tree('source',
1811
format='dirstate-with-subtree')
1812
subtree = self.make_branch_and_tree('source/subtree',
1813
format='dirstate-with-subtree')
1814
tree.add_reference(subtree)
1815
tree.commit('a revision')
1816
tree.branch.create_checkout('target')
1817
self.assertPathExists('target')
1818
self.assertPathExists('target/subtree')
1820
def test_file_conflict_handling(self):
1821
"""Ensure that when building trees, conflict handling is done"""
1822
source = self.make_branch_and_tree('source')
1823
target = self.make_branch_and_tree('target')
1824
self.build_tree(['source/file', 'target/file'])
1825
source.add('file', 'new-file')
1826
source.commit('added file')
1827
build_tree(source.basis_tree(), target)
1828
self.assertEqual([DuplicateEntry('Moved existing file to',
1829
'file.moved', 'file', None, 'new-file')],
1831
target2 = self.make_branch_and_tree('target2')
1832
target_file = file('target2/file', 'wb')
1834
source_file = file('source/file', 'rb')
1836
target_file.write(source_file.read())
1841
build_tree(source.basis_tree(), target2)
1842
self.assertEqual([], target2.conflicts())
1844
def test_symlink_conflict_handling(self):
1845
"""Ensure that when building trees, conflict handling is done"""
1846
self.requireFeature(SymlinkFeature)
1847
source = self.make_branch_and_tree('source')
1848
os.symlink('foo', 'source/symlink')
1849
source.add('symlink', 'new-symlink')
1850
source.commit('added file')
1851
target = self.make_branch_and_tree('target')
1852
os.symlink('bar', 'target/symlink')
1853
build_tree(source.basis_tree(), target)
1854
self.assertEqual([DuplicateEntry('Moved existing file to',
1855
'symlink.moved', 'symlink', None, 'new-symlink')],
1857
target = self.make_branch_and_tree('target2')
1858
os.symlink('foo', 'target2/symlink')
1859
build_tree(source.basis_tree(), target)
1860
self.assertEqual([], target.conflicts())
1862
def test_directory_conflict_handling(self):
1863
"""Ensure that when building trees, conflict handling is done"""
1864
source = self.make_branch_and_tree('source')
1865
target = self.make_branch_and_tree('target')
1866
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1867
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1868
source.commit('added file')
1869
build_tree(source.basis_tree(), target)
1870
self.assertEqual([], target.conflicts())
1871
self.assertPathExists('target/dir1/file')
1873
# Ensure contents are merged
1874
target = self.make_branch_and_tree('target2')
1875
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1876
build_tree(source.basis_tree(), target)
1877
self.assertEqual([], target.conflicts())
1878
self.assertPathExists('target2/dir1/file2')
1879
self.assertPathExists('target2/dir1/file')
1881
# Ensure new contents are suppressed for existing branches
1882
target = self.make_branch_and_tree('target3')
1883
self.make_branch('target3/dir1')
1884
self.build_tree(['target3/dir1/file2'])
1885
build_tree(source.basis_tree(), target)
1886
self.assertPathDoesNotExist('target3/dir1/file')
1887
self.assertPathExists('target3/dir1/file2')
1888
self.assertPathExists('target3/dir1.diverted/file')
1889
self.assertEqual([DuplicateEntry('Diverted to',
1890
'dir1.diverted', 'dir1', 'new-dir1', None)],
1893
target = self.make_branch_and_tree('target4')
1894
self.build_tree(['target4/dir1/'])
1895
self.make_branch('target4/dir1/file')
1896
build_tree(source.basis_tree(), target)
1897
self.assertPathExists('target4/dir1/file')
1898
self.assertEqual('directory', file_kind('target4/dir1/file'))
1899
self.assertPathExists('target4/dir1/file.diverted')
1900
self.assertEqual([DuplicateEntry('Diverted to',
1901
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1904
def test_mixed_conflict_handling(self):
1905
"""Ensure that when building trees, conflict handling is done"""
1906
source = self.make_branch_and_tree('source')
1907
target = self.make_branch_and_tree('target')
1908
self.build_tree(['source/name', 'target/name/'])
1909
source.add('name', 'new-name')
1910
source.commit('added file')
1911
build_tree(source.basis_tree(), target)
1912
self.assertEqual([DuplicateEntry('Moved existing file to',
1913
'name.moved', 'name', None, 'new-name')], target.conflicts())
1915
def test_raises_in_populated(self):
1916
source = self.make_branch_and_tree('source')
1917
self.build_tree(['source/name'])
1919
source.commit('added name')
1920
target = self.make_branch_and_tree('target')
1921
self.build_tree(['target/name'])
1923
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1924
build_tree, source.basis_tree(), target)
1926
def test_build_tree_rename_count(self):
1927
source = self.make_branch_and_tree('source')
1928
self.build_tree(['source/file1', 'source/dir1/'])
1929
source.add(['file1', 'dir1'])
1930
source.commit('add1')
1931
target1 = self.make_branch_and_tree('target1')
1932
transform_result = build_tree(source.basis_tree(), target1)
1933
self.assertEqual(2, transform_result.rename_count)
1935
self.build_tree(['source/dir1/file2'])
1936
source.add(['dir1/file2'])
1937
source.commit('add3')
1938
target2 = self.make_branch_and_tree('target2')
1939
transform_result = build_tree(source.basis_tree(), target2)
1940
# children of non-root directories should not be renamed
1941
self.assertEqual(2, transform_result.rename_count)
1943
def create_ab_tree(self):
1944
"""Create a committed test tree with two files"""
1945
source = self.make_branch_and_tree('source')
1946
self.build_tree_contents([('source/file1', 'A')])
1947
self.build_tree_contents([('source/file2', 'B')])
1948
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1949
source.commit('commit files')
1951
self.addCleanup(source.unlock)
1954
def test_build_tree_accelerator_tree(self):
1955
source = self.create_ab_tree()
1956
self.build_tree_contents([('source/file2', 'C')])
1958
real_source_get_file = source.get_file
1959
def get_file(file_id, path=None):
1960
calls.append(file_id)
1961
return real_source_get_file(file_id, path)
1962
source.get_file = get_file
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)
1968
self.assertEqual(['file1-id'], calls)
1970
self.addCleanup(target.unlock)
1971
self.assertEqual([], list(target.iter_changes(revision_tree)))
1973
def test_build_tree_accelerator_tree_observes_sha1(self):
1974
source = self.create_ab_tree()
1975
sha1 = osutils.sha_string('A')
1976
target = self.make_branch_and_tree('target')
1978
self.addCleanup(target.unlock)
1979
state = target.current_dirstate()
1980
state._cutoff_time = time.time() + 60
1981
build_tree(source.basis_tree(), target, source)
1982
entry = state._get_entry(0, path_utf8='file1')
1983
self.assertEqual(sha1, entry[1][0][1])
1985
def test_build_tree_accelerator_tree_missing_file(self):
1986
source = self.create_ab_tree()
1987
os.unlink('source/file1')
1988
source.remove(['file2'])
1989
target = self.make_branch_and_tree('target')
1990
revision_tree = source.basis_tree()
1991
revision_tree.lock_read()
1992
self.addCleanup(revision_tree.unlock)
1993
build_tree(revision_tree, target, source)
1995
self.addCleanup(target.unlock)
1996
self.assertEqual([], list(target.iter_changes(revision_tree)))
1998
def test_build_tree_accelerator_wrong_kind(self):
1999
self.requireFeature(SymlinkFeature)
2000
source = self.make_branch_and_tree('source')
2001
self.build_tree_contents([('source/file1', '')])
2002
self.build_tree_contents([('source/file2', '')])
2003
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
2004
source.commit('commit files')
2005
os.unlink('source/file2')
2006
self.build_tree_contents([('source/file2/', 'C')])
2007
os.unlink('source/file1')
2008
os.symlink('file2', 'source/file1')
2010
real_source_get_file = source.get_file
2011
def get_file(file_id, path=None):
2012
calls.append(file_id)
2013
return real_source_get_file(file_id, path)
2014
source.get_file = get_file
2015
target = self.make_branch_and_tree('target')
2016
revision_tree = source.basis_tree()
2017
revision_tree.lock_read()
2018
self.addCleanup(revision_tree.unlock)
2019
build_tree(revision_tree, target, source)
2020
self.assertEqual([], calls)
2022
self.addCleanup(target.unlock)
2023
self.assertEqual([], list(target.iter_changes(revision_tree)))
2025
def test_build_tree_hardlink(self):
2026
self.requireFeature(HardlinkFeature)
2027
source = self.create_ab_tree()
2028
target = self.make_branch_and_tree('target')
2029
revision_tree = source.basis_tree()
2030
revision_tree.lock_read()
2031
self.addCleanup(revision_tree.unlock)
2032
build_tree(revision_tree, target, source, hardlink=True)
2034
self.addCleanup(target.unlock)
2035
self.assertEqual([], list(target.iter_changes(revision_tree)))
2036
source_stat = os.stat('source/file1')
2037
target_stat = os.stat('target/file1')
2038
self.assertEqual(source_stat, target_stat)
2040
# Explicitly disallowing hardlinks should prevent them.
2041
target2 = self.make_branch_and_tree('target2')
2042
build_tree(revision_tree, target2, source, hardlink=False)
2044
self.addCleanup(target2.unlock)
2045
self.assertEqual([], list(target2.iter_changes(revision_tree)))
2046
source_stat = os.stat('source/file1')
2047
target2_stat = os.stat('target2/file1')
2048
self.assertNotEqual(source_stat, target2_stat)
2050
def test_build_tree_accelerator_tree_moved(self):
2051
source = self.make_branch_and_tree('source')
2052
self.build_tree_contents([('source/file1', 'A')])
2053
source.add(['file1'], ['file1-id'])
2054
source.commit('commit files')
2055
source.rename_one('file1', 'file2')
2057
self.addCleanup(source.unlock)
2058
target = self.make_branch_and_tree('target')
2059
revision_tree = source.basis_tree()
2060
revision_tree.lock_read()
2061
self.addCleanup(revision_tree.unlock)
2062
build_tree(revision_tree, target, source)
2064
self.addCleanup(target.unlock)
2065
self.assertEqual([], list(target.iter_changes(revision_tree)))
2067
def test_build_tree_hardlinks_preserve_execute(self):
2068
self.requireFeature(HardlinkFeature)
2069
source = self.create_ab_tree()
2070
tt = TreeTransform(source)
2071
trans_id = tt.trans_id_tree_file_id('file1-id')
2072
tt.set_executability(True, trans_id)
2074
self.assertTrue(source.is_executable('file1-id'))
2075
target = self.make_branch_and_tree('target')
2076
revision_tree = source.basis_tree()
2077
revision_tree.lock_read()
2078
self.addCleanup(revision_tree.unlock)
2079
build_tree(revision_tree, target, source, hardlink=True)
2081
self.addCleanup(target.unlock)
2082
self.assertEqual([], list(target.iter_changes(revision_tree)))
2083
self.assertTrue(source.is_executable('file1-id'))
2085
def install_rot13_content_filter(self, pattern):
2087
# self.addCleanup(filters._reset_registry, filters._reset_registry())
2088
# below, but that looks a bit... hard to read even if it's exactly
2090
original_registry = filters._reset_registry()
2091
def restore_registry():
2092
filters._reset_registry(original_registry)
2093
self.addCleanup(restore_registry)
2094
def rot13(chunks, context=None):
2095
return [''.join(chunks).encode('rot13')]
2096
rot13filter = filters.ContentFilter(rot13, rot13)
2097
filters.register_filter_stack_map('rot13', {'yes': [rot13filter]}.get)
2098
os.mkdir(self.test_home_dir + '/.bazaar')
2099
rules_filename = self.test_home_dir + '/.bazaar/rules'
2100
f = open(rules_filename, 'wb')
2101
f.write('[name %s]\nrot13=yes\n' % (pattern,))
2103
def uninstall_rules():
2104
os.remove(rules_filename)
2106
self.addCleanup(uninstall_rules)
2109
def test_build_tree_content_filtered_files_are_not_hardlinked(self):
2110
"""build_tree will not hardlink files that have content filtering rules
2111
applied to them (but will still hardlink other files from the same tree
2114
self.requireFeature(HardlinkFeature)
2115
self.install_rot13_content_filter('file1')
2116
source = self.create_ab_tree()
2117
target = self.make_branch_and_tree('target')
2118
revision_tree = source.basis_tree()
2119
revision_tree.lock_read()
2120
self.addCleanup(revision_tree.unlock)
2121
build_tree(revision_tree, target, source, hardlink=True)
2123
self.addCleanup(target.unlock)
2124
self.assertEqual([], list(target.iter_changes(revision_tree)))
2125
source_stat = os.stat('source/file1')
2126
target_stat = os.stat('target/file1')
2127
self.assertNotEqual(source_stat, target_stat)
2128
source_stat = os.stat('source/file2')
2129
target_stat = os.stat('target/file2')
2130
self.assertEqualStat(source_stat, target_stat)
2132
def test_case_insensitive_build_tree_inventory(self):
2133
if (tests.CaseInsensitiveFilesystemFeature.available()
2134
or tests.CaseInsCasePresFilenameFeature.available()):
2135
raise tests.UnavailableFeature('Fully case sensitive filesystem')
2136
source = self.make_branch_and_tree('source')
2137
self.build_tree(['source/file', 'source/FILE'])
2138
source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
2139
source.commit('added files')
2140
# Don't try this at home, kids!
2141
# Force the tree to report that it is case insensitive
2142
target = self.make_branch_and_tree('target')
2143
target.case_sensitive = False
2144
build_tree(source.basis_tree(), target, source, delta_from_tree=True)
2145
self.assertEqual('file.moved', target.id2path('lower-id'))
2146
self.assertEqual('FILE', target.id2path('upper-id'))
2148
def test_build_tree_observes_sha(self):
2149
source = self.make_branch_and_tree('source')
2150
self.build_tree(['source/file1', 'source/dir/', 'source/dir/file2'])
2151
source.add(['file1', 'dir', 'dir/file2'],
2152
['file1-id', 'dir-id', 'file2-id'])
2153
source.commit('new files')
2154
target = self.make_branch_and_tree('target')
2156
self.addCleanup(target.unlock)
2157
# We make use of the fact that DirState caches its cutoff time. So we
2158
# set the 'safe' time to one minute in the future.
2159
state = target.current_dirstate()
2160
state._cutoff_time = time.time() + 60
2161
build_tree(source.basis_tree(), target)
2162
entry1_sha = osutils.sha_file_by_name('source/file1')
2163
entry2_sha = osutils.sha_file_by_name('source/dir/file2')
2164
# entry[1] is the state information, entry[1][0] is the state of the
2165
# working tree, entry[1][0][1] is the sha value for the current working
2167
entry1 = state._get_entry(0, path_utf8='file1')
2168
self.assertEqual(entry1_sha, entry1[1][0][1])
2169
# The 'size' field must also be set.
2170
self.assertEqual(25, entry1[1][0][2])
2171
entry1_state = entry1[1][0]
2172
entry2 = state._get_entry(0, path_utf8='dir/file2')
2173
self.assertEqual(entry2_sha, entry2[1][0][1])
2174
self.assertEqual(29, entry2[1][0][2])
2175
entry2_state = entry2[1][0]
2176
# Now, make sure that we don't have to re-read the content. The
2177
# packed_stat should match exactly.
2178
self.assertEqual(entry1_sha, target.get_file_sha1('file1-id', 'file1'))
2179
self.assertEqual(entry2_sha,
2180
target.get_file_sha1('file2-id', 'dir/file2'))
2181
self.assertEqual(entry1_state, entry1[1][0])
2182
self.assertEqual(entry2_state, entry2[1][0])
2185
class TestCommitTransform(tests.TestCaseWithTransport):
2187
def get_branch(self):
2188
tree = self.make_branch_and_tree('tree')
2190
self.addCleanup(tree.unlock)
2191
tree.commit('empty commit')
2194
def get_branch_and_transform(self):
2195
branch = self.get_branch()
2196
tt = TransformPreview(branch.basis_tree())
2197
self.addCleanup(tt.finalize)
2200
def test_commit_wrong_basis(self):
2201
branch = self.get_branch()
2202
basis = branch.repository.revision_tree(
2203
_mod_revision.NULL_REVISION)
2204
tt = TransformPreview(basis)
2205
self.addCleanup(tt.finalize)
2206
e = self.assertRaises(ValueError, tt.commit, branch, '')
2207
self.assertEqual('TreeTransform not based on branch basis: null:',
2210
def test_empy_commit(self):
2211
branch, tt = self.get_branch_and_transform()
2212
rev = tt.commit(branch, 'my message')
2213
self.assertEqual(2, branch.revno())
2214
repo = branch.repository
2215
self.assertEqual('my message', repo.get_revision(rev).message)
2217
def test_merge_parents(self):
2218
branch, tt = self.get_branch_and_transform()
2219
rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
2220
self.assertEqual(['rev1b', 'rev1c'],
2221
branch.basis_tree().get_parent_ids()[1:])
2223
def test_first_commit(self):
2224
branch = self.make_branch('branch')
2226
self.addCleanup(branch.unlock)
2227
tt = TransformPreview(branch.basis_tree())
2228
self.addCleanup(tt.finalize)
2229
tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
2230
rev = tt.commit(branch, 'my message')
2231
self.assertEqual([], branch.basis_tree().get_parent_ids())
2232
self.assertNotEqual(_mod_revision.NULL_REVISION,
2233
branch.last_revision())
2235
def test_first_commit_with_merge_parents(self):
2236
branch = self.make_branch('branch')
2238
self.addCleanup(branch.unlock)
2239
tt = TransformPreview(branch.basis_tree())
2240
self.addCleanup(tt.finalize)
2241
e = self.assertRaises(ValueError, tt.commit, branch,
2242
'my message', ['rev1b-id'])
2243
self.assertEqual('Cannot supply merge parents for first commit.',
2245
self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
2247
def test_add_files(self):
2248
branch, tt = self.get_branch_and_transform()
2249
tt.new_file('file', tt.root, 'contents', 'file-id')
2250
trans_id = tt.new_directory('dir', tt.root, 'dir-id')
2251
if SymlinkFeature.available():
2252
tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
2253
rev = tt.commit(branch, 'message')
2254
tree = branch.basis_tree()
2255
self.assertEqual('file', tree.id2path('file-id'))
2256
self.assertEqual('contents', tree.get_file_text('file-id'))
2257
self.assertEqual('dir', tree.id2path('dir-id'))
2258
if SymlinkFeature.available():
2259
self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
2260
self.assertEqual('target', tree.get_symlink_target('symlink-id'))
2262
def test_add_unversioned(self):
2263
branch, tt = self.get_branch_and_transform()
2264
tt.new_file('file', tt.root, 'contents')
2265
self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
2266
'message', strict=True)
2268
def test_modify_strict(self):
2269
branch, tt = self.get_branch_and_transform()
2270
tt.new_file('file', tt.root, 'contents', 'file-id')
2271
tt.commit(branch, 'message', strict=True)
2272
tt = TransformPreview(branch.basis_tree())
2273
self.addCleanup(tt.finalize)
2274
trans_id = tt.trans_id_file_id('file-id')
2275
tt.delete_contents(trans_id)
2276
tt.create_file('contents', trans_id)
2277
tt.commit(branch, 'message', strict=True)
2279
def test_commit_malformed(self):
2280
"""Committing a malformed transform should raise an exception.
2282
In this case, we are adding a file without adding its parent.
2284
branch, tt = self.get_branch_and_transform()
2285
parent_id = tt.trans_id_file_id('parent-id')
2286
tt.new_file('file', parent_id, 'contents', 'file-id')
2287
self.assertRaises(errors.MalformedTransform, tt.commit, branch,
2290
def test_commit_rich_revision_data(self):
2291
branch, tt = self.get_branch_and_transform()
2292
rev_id = tt.commit(branch, 'message', timestamp=1, timezone=43201,
2293
committer='me <me@example.com>',
2294
revprops={'foo': 'bar'}, revision_id='revid-1',
2295
authors=['Author1 <author1@example.com>',
2296
'Author2 <author2@example.com>',
2298
self.assertEqual('revid-1', rev_id)
2299
revision = branch.repository.get_revision(rev_id)
2300
self.assertEqual(1, revision.timestamp)
2301
self.assertEqual(43201, revision.timezone)
2302
self.assertEqual('me <me@example.com>', revision.committer)
2303
self.assertEqual(['Author1 <author1@example.com>',
2304
'Author2 <author2@example.com>'],
2305
revision.get_apparent_authors())
2306
del revision.properties['authors']
2307
self.assertEqual({'foo': 'bar',
2308
'branch-nick': 'tree'},
2309
revision.properties)
2311
def test_no_explicit_revprops(self):
2312
branch, tt = self.get_branch_and_transform()
2313
rev_id = tt.commit(branch, 'message', authors=[
2314
'Author1 <author1@example.com>',
2315
'Author2 <author2@example.com>', ])
2316
revision = branch.repository.get_revision(rev_id)
2317
self.assertEqual(['Author1 <author1@example.com>',
2318
'Author2 <author2@example.com>'],
2319
revision.get_apparent_authors())
2320
self.assertEqual('tree', revision.properties['branch-nick'])
2323
class TestBackupName(tests.TestCase):
2325
def test_deprecations(self):
2326
class MockTransform(object):
2328
def has_named_child(self, by_parent, parent_id, name):
2329
return name in by_parent.get(parent_id, [])
2331
class MockEntry(object):
2334
object.__init__(self)
724
2337
tt = MockTransform()
725
name = get_backup_name(MockEntry(), {'a':[]}, 'a', tt)
726
self.assertEqual(name, 'name.~1~')
727
name = get_backup_name(MockEntry(), {'a':['1']}, 'a', tt)
728
self.assertEqual(name, 'name.~2~')
729
name = get_backup_name(MockEntry(), {'a':['2']}, 'a', tt)
730
self.assertEqual(name, 'name.~1~')
731
name = get_backup_name(MockEntry(), {'a':['2'], 'b':[]}, 'b', tt)
732
self.assertEqual(name, 'name.~1~')
733
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
734
self.assertEqual(name, 'name.~4~')
2338
name1 = self.applyDeprecated(
2339
symbol_versioning.deprecated_in((2, 3, 0)),
2340
transform.get_backup_name, MockEntry(), {'a':[]}, 'a', tt)
2341
self.assertEqual('name.~1~', name1)
2342
name2 = self.applyDeprecated(
2343
symbol_versioning.deprecated_in((2, 3, 0)),
2344
transform._get_backup_name, 'name', {'a':['name.~1~']}, 'a', tt)
2345
self.assertEqual('name.~2~', name2)
2348
class TestFileMover(tests.TestCaseWithTransport):
2350
def test_file_mover(self):
2351
self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
2352
mover = _FileMover()
2353
mover.rename('a', 'q')
2354
self.assertPathExists('q')
2355
self.assertPathDoesNotExist('a')
2356
self.assertPathExists('q/b')
2357
self.assertPathExists('c')
2358
self.assertPathExists('c/d')
2360
def test_pre_delete_rollback(self):
2361
self.build_tree(['a/'])
2362
mover = _FileMover()
2363
mover.pre_delete('a', 'q')
2364
self.assertPathExists('q')
2365
self.assertPathDoesNotExist('a')
2367
self.assertPathDoesNotExist('q')
2368
self.assertPathExists('a')
2370
def test_apply_deletions(self):
2371
self.build_tree(['a/', 'b/'])
2372
mover = _FileMover()
2373
mover.pre_delete('a', 'q')
2374
mover.pre_delete('b', 'r')
2375
self.assertPathExists('q')
2376
self.assertPathExists('r')
2377
self.assertPathDoesNotExist('a')
2378
self.assertPathDoesNotExist('b')
2379
mover.apply_deletions()
2380
self.assertPathDoesNotExist('q')
2381
self.assertPathDoesNotExist('r')
2382
self.assertPathDoesNotExist('a')
2383
self.assertPathDoesNotExist('b')
2385
def test_file_mover_rollback(self):
2386
self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
2387
mover = _FileMover()
2388
mover.rename('c/d', 'c/f')
2389
mover.rename('c/e', 'c/d')
2391
mover.rename('a', 'c')
2392
except errors.FileExists, e:
2394
self.assertPathExists('a')
2395
self.assertPathExists('c/d')
2398
class Bogus(Exception):
2402
class TestTransformRollback(tests.TestCaseWithTransport):
2404
class ExceptionFileMover(_FileMover):
2406
def __init__(self, bad_source=None, bad_target=None):
2407
_FileMover.__init__(self)
2408
self.bad_source = bad_source
2409
self.bad_target = bad_target
2411
def rename(self, source, target):
2412
if (self.bad_source is not None and
2413
source.endswith(self.bad_source)):
2415
elif (self.bad_target is not None and
2416
target.endswith(self.bad_target)):
2419
_FileMover.rename(self, source, target)
2421
def test_rollback_rename(self):
2422
tree = self.make_branch_and_tree('.')
2423
self.build_tree(['a/', 'a/b'])
2424
tt = TreeTransform(tree)
2425
self.addCleanup(tt.finalize)
2426
a_id = tt.trans_id_tree_path('a')
2427
tt.adjust_path('c', tt.root, a_id)
2428
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2429
self.assertRaises(Bogus, tt.apply,
2430
_mover=self.ExceptionFileMover(bad_source='a'))
2431
self.assertPathExists('a')
2432
self.assertPathExists('a/b')
2434
self.assertPathExists('c')
2435
self.assertPathExists('c/d')
2437
def test_rollback_rename_into_place(self):
2438
tree = self.make_branch_and_tree('.')
2439
self.build_tree(['a/', 'a/b'])
2440
tt = TreeTransform(tree)
2441
self.addCleanup(tt.finalize)
2442
a_id = tt.trans_id_tree_path('a')
2443
tt.adjust_path('c', tt.root, a_id)
2444
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2445
self.assertRaises(Bogus, tt.apply,
2446
_mover=self.ExceptionFileMover(bad_target='c/d'))
2447
self.assertPathExists('a')
2448
self.assertPathExists('a/b')
2450
self.assertPathExists('c')
2451
self.assertPathExists('c/d')
2453
def test_rollback_deletion(self):
2454
tree = self.make_branch_and_tree('.')
2455
self.build_tree(['a/', 'a/b'])
2456
tt = TreeTransform(tree)
2457
self.addCleanup(tt.finalize)
2458
a_id = tt.trans_id_tree_path('a')
2459
tt.delete_contents(a_id)
2460
tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
2461
self.assertRaises(Bogus, tt.apply,
2462
_mover=self.ExceptionFileMover(bad_target='d'))
2463
self.assertPathExists('a')
2464
self.assertPathExists('a/b')
2467
class TestTransformMissingParent(tests.TestCaseWithTransport):
2469
def make_tt_with_versioned_dir(self):
2470
wt = self.make_branch_and_tree('.')
2471
self.build_tree(['dir/',])
2472
wt.add(['dir'], ['dir-id'])
2473
wt.commit('Create dir')
2474
tt = TreeTransform(wt)
2475
self.addCleanup(tt.finalize)
2478
def test_resolve_create_parent_for_versioned_file(self):
2479
wt, tt = self.make_tt_with_versioned_dir()
2480
dir_tid = tt.trans_id_tree_file_id('dir-id')
2481
file_tid = tt.new_file('file', dir_tid, 'Contents', file_id='file-id')
2482
tt.delete_contents(dir_tid)
2483
tt.unversion_file(dir_tid)
2484
conflicts = resolve_conflicts(tt)
2485
# one conflict for the missing directory, one for the unversioned
2487
self.assertLength(2, conflicts)
2489
def test_non_versioned_file_create_conflict(self):
2490
wt, tt = self.make_tt_with_versioned_dir()
2491
dir_tid = tt.trans_id_tree_file_id('dir-id')
2492
tt.new_file('file', dir_tid, 'Contents')
2493
tt.delete_contents(dir_tid)
2494
tt.unversion_file(dir_tid)
2495
conflicts = resolve_conflicts(tt)
2496
# no conflicts or rather: orphaning 'file' resolve the 'dir' conflict
2497
self.assertLength(1, conflicts)
2498
self.assertEqual(('deleting parent', 'Not deleting', 'new-1'),
2502
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
2503
('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
2505
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
2506
('', ''), ('directory', 'directory'), (False, False))
2509
class TestTransformPreview(tests.TestCaseWithTransport):
2511
def create_tree(self):
2512
tree = self.make_branch_and_tree('.')
2513
self.build_tree_contents([('a', 'content 1')])
2514
tree.set_root_id('TREE_ROOT')
2515
tree.add('a', 'a-id')
2516
tree.commit('rev1', rev_id='rev1')
2517
return tree.branch.repository.revision_tree('rev1')
2519
def get_empty_preview(self):
2520
repository = self.make_repository('repo')
2521
tree = repository.revision_tree(_mod_revision.NULL_REVISION)
2522
preview = TransformPreview(tree)
2523
self.addCleanup(preview.finalize)
2526
def test_transform_preview(self):
2527
revision_tree = self.create_tree()
2528
preview = TransformPreview(revision_tree)
2529
self.addCleanup(preview.finalize)
2531
def test_transform_preview_tree(self):
2532
revision_tree = self.create_tree()
2533
preview = TransformPreview(revision_tree)
2534
self.addCleanup(preview.finalize)
2535
preview.get_preview_tree()
2537
def test_transform_new_file(self):
2538
revision_tree = self.create_tree()
2539
preview = TransformPreview(revision_tree)
2540
self.addCleanup(preview.finalize)
2541
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2542
preview_tree = preview.get_preview_tree()
2543
self.assertEqual(preview_tree.kind('file2-id'), 'file')
2545
preview_tree.get_file('file2-id').read(), 'content B\n')
2547
def test_diff_preview_tree(self):
2548
revision_tree = self.create_tree()
2549
preview = TransformPreview(revision_tree)
2550
self.addCleanup(preview.finalize)
2551
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2552
preview_tree = preview.get_preview_tree()
2554
show_diff_trees(revision_tree, preview_tree, out)
2555
lines = out.getvalue().splitlines()
2556
self.assertEqual(lines[0], "=== added file 'file2'")
2557
# 3 lines of diff administrivia
2558
self.assertEqual(lines[4], "+content B")
2560
def test_transform_conflicts(self):
2561
revision_tree = self.create_tree()
2562
preview = TransformPreview(revision_tree)
2563
self.addCleanup(preview.finalize)
2564
preview.new_file('a', preview.root, 'content 2')
2565
resolve_conflicts(preview)
2566
trans_id = preview.trans_id_file_id('a-id')
2567
self.assertEqual('a.moved', preview.final_name(trans_id))
2569
def get_tree_and_preview_tree(self):
2570
revision_tree = self.create_tree()
2571
preview = TransformPreview(revision_tree)
2572
self.addCleanup(preview.finalize)
2573
a_trans_id = preview.trans_id_file_id('a-id')
2574
preview.delete_contents(a_trans_id)
2575
preview.create_file('b content', a_trans_id)
2576
preview_tree = preview.get_preview_tree()
2577
return revision_tree, preview_tree
2579
def test_iter_changes(self):
2580
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2581
root = revision_tree.inventory.root.file_id
2582
self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
2583
(root, root), ('a', 'a'), ('file', 'file'),
2585
list(preview_tree.iter_changes(revision_tree)))
2587
def test_include_unchanged_succeeds(self):
2588
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2589
changes = preview_tree.iter_changes(revision_tree,
2590
include_unchanged=True)
2591
root = revision_tree.inventory.root.file_id
2593
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2595
def test_specific_files(self):
2596
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2597
changes = preview_tree.iter_changes(revision_tree,
2598
specific_files=[''])
2599
self.assertEqual([A_ENTRY], list(changes))
2601
def test_want_unversioned(self):
2602
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2603
changes = preview_tree.iter_changes(revision_tree,
2604
want_unversioned=True)
2605
self.assertEqual([A_ENTRY], list(changes))
2607
def test_ignore_extra_trees_no_specific_files(self):
2608
# extra_trees is harmless without specific_files, so we'll silently
2609
# accept it, even though we won't use it.
2610
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2611
preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
2613
def test_ignore_require_versioned_no_specific_files(self):
2614
# require_versioned is meaningless without specific_files.
2615
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2616
preview_tree.iter_changes(revision_tree, require_versioned=False)
2618
def test_ignore_pb(self):
2619
# pb could be supported, but TT.iter_changes doesn't support it.
2620
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2621
preview_tree.iter_changes(revision_tree)
2623
def test_kind(self):
2624
revision_tree = self.create_tree()
2625
preview = TransformPreview(revision_tree)
2626
self.addCleanup(preview.finalize)
2627
preview.new_file('file', preview.root, 'contents', 'file-id')
2628
preview.new_directory('directory', preview.root, 'dir-id')
2629
preview_tree = preview.get_preview_tree()
2630
self.assertEqual('file', preview_tree.kind('file-id'))
2631
self.assertEqual('directory', preview_tree.kind('dir-id'))
2633
def test_get_file_mtime(self):
2634
preview = self.get_empty_preview()
2635
file_trans_id = preview.new_file('file', preview.root, 'contents',
2637
limbo_path = preview._limbo_name(file_trans_id)
2638
preview_tree = preview.get_preview_tree()
2639
self.assertEqual(os.stat(limbo_path).st_mtime,
2640
preview_tree.get_file_mtime('file-id'))
2642
def test_get_file_mtime_renamed(self):
2643
work_tree = self.make_branch_and_tree('tree')
2644
self.build_tree(['tree/file'])
2645
work_tree.add('file', 'file-id')
2646
preview = TransformPreview(work_tree)
2647
self.addCleanup(preview.finalize)
2648
file_trans_id = preview.trans_id_tree_file_id('file-id')
2649
preview.adjust_path('renamed', preview.root, file_trans_id)
2650
preview_tree = preview.get_preview_tree()
2651
preview_mtime = preview_tree.get_file_mtime('file-id', 'renamed')
2652
work_mtime = work_tree.get_file_mtime('file-id', 'file')
2654
def test_get_file_size(self):
2655
work_tree = self.make_branch_and_tree('tree')
2656
self.build_tree_contents([('tree/old', 'old')])
2657
work_tree.add('old', 'old-id')
2658
preview = TransformPreview(work_tree)
2659
self.addCleanup(preview.finalize)
2660
new_id = preview.new_file('name', preview.root, 'contents', 'new-id',
2662
tree = preview.get_preview_tree()
2663
self.assertEqual(len('old'), tree.get_file_size('old-id'))
2664
self.assertEqual(len('contents'), tree.get_file_size('new-id'))
2666
def test_get_file(self):
2667
preview = self.get_empty_preview()
2668
preview.new_file('file', preview.root, 'contents', 'file-id')
2669
preview_tree = preview.get_preview_tree()
2670
tree_file = preview_tree.get_file('file-id')
2672
self.assertEqual('contents', tree_file.read())
2676
def test_get_symlink_target(self):
2677
self.requireFeature(SymlinkFeature)
2678
preview = self.get_empty_preview()
2679
preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
2680
preview_tree = preview.get_preview_tree()
2681
self.assertEqual('target',
2682
preview_tree.get_symlink_target('symlink-id'))
2684
def test_all_file_ids(self):
2685
tree = self.make_branch_and_tree('tree')
2686
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
2687
tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
2688
preview = TransformPreview(tree)
2689
self.addCleanup(preview.finalize)
2690
preview.unversion_file(preview.trans_id_file_id('b-id'))
2691
c_trans_id = preview.trans_id_file_id('c-id')
2692
preview.unversion_file(c_trans_id)
2693
preview.version_file('c-id', c_trans_id)
2694
preview_tree = preview.get_preview_tree()
2695
self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
2696
preview_tree.all_file_ids())
2698
def test_path2id_deleted_unchanged(self):
2699
tree = self.make_branch_and_tree('tree')
2700
self.build_tree(['tree/unchanged', 'tree/deleted'])
2701
tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
2702
preview = TransformPreview(tree)
2703
self.addCleanup(preview.finalize)
2704
preview.unversion_file(preview.trans_id_file_id('deleted-id'))
2705
preview_tree = preview.get_preview_tree()
2706
self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
2707
self.assertIs(None, preview_tree.path2id('deleted'))
2709
def test_path2id_created(self):
2710
tree = self.make_branch_and_tree('tree')
2711
self.build_tree(['tree/unchanged'])
2712
tree.add(['unchanged'], ['unchanged-id'])
2713
preview = TransformPreview(tree)
2714
self.addCleanup(preview.finalize)
2715
preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
2716
'contents', 'new-id')
2717
preview_tree = preview.get_preview_tree()
2718
self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
2720
def test_path2id_moved(self):
2721
tree = self.make_branch_and_tree('tree')
2722
self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
2723
tree.add(['old_parent', 'old_parent/child'],
2724
['old_parent-id', 'child-id'])
2725
preview = TransformPreview(tree)
2726
self.addCleanup(preview.finalize)
2727
new_parent = preview.new_directory('new_parent', preview.root,
2729
preview.adjust_path('child', new_parent,
2730
preview.trans_id_file_id('child-id'))
2731
preview_tree = preview.get_preview_tree()
2732
self.assertIs(None, preview_tree.path2id('old_parent/child'))
2733
self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
2735
def test_path2id_renamed_parent(self):
2736
tree = self.make_branch_and_tree('tree')
2737
self.build_tree(['tree/old_name/', 'tree/old_name/child'])
2738
tree.add(['old_name', 'old_name/child'],
2739
['parent-id', 'child-id'])
2740
preview = TransformPreview(tree)
2741
self.addCleanup(preview.finalize)
2742
preview.adjust_path('new_name', preview.root,
2743
preview.trans_id_file_id('parent-id'))
2744
preview_tree = preview.get_preview_tree()
2745
self.assertIs(None, preview_tree.path2id('old_name/child'))
2746
self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
2748
def assertMatchingIterEntries(self, tt, specific_file_ids=None):
2749
preview_tree = tt.get_preview_tree()
2750
preview_result = list(preview_tree.iter_entries_by_dir(
2754
actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
2755
self.assertEqual(actual_result, preview_result)
2757
def test_iter_entries_by_dir_new(self):
2758
tree = self.make_branch_and_tree('tree')
2759
tt = TreeTransform(tree)
2760
tt.new_file('new', tt.root, 'contents', 'new-id')
2761
self.assertMatchingIterEntries(tt)
2763
def test_iter_entries_by_dir_deleted(self):
2764
tree = self.make_branch_and_tree('tree')
2765
self.build_tree(['tree/deleted'])
2766
tree.add('deleted', 'deleted-id')
2767
tt = TreeTransform(tree)
2768
tt.delete_contents(tt.trans_id_file_id('deleted-id'))
2769
self.assertMatchingIterEntries(tt)
2771
def test_iter_entries_by_dir_unversioned(self):
2772
tree = self.make_branch_and_tree('tree')
2773
self.build_tree(['tree/removed'])
2774
tree.add('removed', 'removed-id')
2775
tt = TreeTransform(tree)
2776
tt.unversion_file(tt.trans_id_file_id('removed-id'))
2777
self.assertMatchingIterEntries(tt)
2779
def test_iter_entries_by_dir_moved(self):
2780
tree = self.make_branch_and_tree('tree')
2781
self.build_tree(['tree/moved', 'tree/new_parent/'])
2782
tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
2783
tt = TreeTransform(tree)
2784
tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
2785
tt.trans_id_file_id('moved-id'))
2786
self.assertMatchingIterEntries(tt)
2788
def test_iter_entries_by_dir_specific_file_ids(self):
2789
tree = self.make_branch_and_tree('tree')
2790
tree.set_root_id('tree-root-id')
2791
self.build_tree(['tree/parent/', 'tree/parent/child'])
2792
tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
2793
tt = TreeTransform(tree)
2794
self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
2796
def test_symlink_content_summary(self):
2797
self.requireFeature(SymlinkFeature)
2798
preview = self.get_empty_preview()
2799
preview.new_symlink('path', preview.root, 'target', 'path-id')
2800
summary = preview.get_preview_tree().path_content_summary('path')
2801
self.assertEqual(('symlink', None, None, 'target'), summary)
2803
def test_missing_content_summary(self):
2804
preview = self.get_empty_preview()
2805
summary = preview.get_preview_tree().path_content_summary('path')
2806
self.assertEqual(('missing', None, None, None), summary)
2808
def test_deleted_content_summary(self):
2809
tree = self.make_branch_and_tree('tree')
2810
self.build_tree(['tree/path/'])
2812
preview = TransformPreview(tree)
2813
self.addCleanup(preview.finalize)
2814
preview.delete_contents(preview.trans_id_tree_path('path'))
2815
summary = preview.get_preview_tree().path_content_summary('path')
2816
self.assertEqual(('missing', None, None, None), summary)
2818
def test_file_content_summary_executable(self):
2819
preview = self.get_empty_preview()
2820
path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2821
preview.set_executability(True, path_id)
2822
summary = preview.get_preview_tree().path_content_summary('path')
2823
self.assertEqual(4, len(summary))
2824
self.assertEqual('file', summary[0])
2825
# size must be known
2826
self.assertEqual(len('contents'), summary[1])
2828
self.assertEqual(True, summary[2])
2829
# will not have hash (not cheap to determine)
2830
self.assertIs(None, summary[3])
2832
def test_change_executability(self):
2833
tree = self.make_branch_and_tree('tree')
2834
self.build_tree(['tree/path'])
2836
preview = TransformPreview(tree)
2837
self.addCleanup(preview.finalize)
2838
path_id = preview.trans_id_tree_path('path')
2839
preview.set_executability(True, path_id)
2840
summary = preview.get_preview_tree().path_content_summary('path')
2841
self.assertEqual(True, summary[2])
2843
def test_file_content_summary_non_exec(self):
2844
preview = self.get_empty_preview()
2845
preview.new_file('path', preview.root, 'contents', 'path-id')
2846
summary = preview.get_preview_tree().path_content_summary('path')
2847
self.assertEqual(4, len(summary))
2848
self.assertEqual('file', summary[0])
2849
# size must be known
2850
self.assertEqual(len('contents'), summary[1])
2852
self.assertEqual(False, summary[2])
2853
# will not have hash (not cheap to determine)
2854
self.assertIs(None, summary[3])
2856
def test_dir_content_summary(self):
2857
preview = self.get_empty_preview()
2858
preview.new_directory('path', preview.root, 'path-id')
2859
summary = preview.get_preview_tree().path_content_summary('path')
2860
self.assertEqual(('directory', None, None, None), summary)
2862
def test_tree_content_summary(self):
2863
preview = self.get_empty_preview()
2864
path = preview.new_directory('path', preview.root, 'path-id')
2865
preview.set_tree_reference('rev-1', path)
2866
summary = preview.get_preview_tree().path_content_summary('path')
2867
self.assertEqual(4, len(summary))
2868
self.assertEqual('tree-reference', summary[0])
2870
def test_annotate(self):
2871
tree = self.make_branch_and_tree('tree')
2872
self.build_tree_contents([('tree/file', 'a\n')])
2873
tree.add('file', 'file-id')
2874
tree.commit('a', rev_id='one')
2875
self.build_tree_contents([('tree/file', 'a\nb\n')])
2876
preview = TransformPreview(tree)
2877
self.addCleanup(preview.finalize)
2878
file_trans_id = preview.trans_id_file_id('file-id')
2879
preview.delete_contents(file_trans_id)
2880
preview.create_file('a\nb\nc\n', file_trans_id)
2881
preview_tree = preview.get_preview_tree()
2887
annotation = preview_tree.annotate_iter('file-id', 'me:')
2888
self.assertEqual(expected, annotation)
2890
def test_annotate_missing(self):
2891
preview = self.get_empty_preview()
2892
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2893
preview_tree = preview.get_preview_tree()
2899
annotation = preview_tree.annotate_iter('file-id', 'me:')
2900
self.assertEqual(expected, annotation)
2902
def test_annotate_rename(self):
2903
tree = self.make_branch_and_tree('tree')
2904
self.build_tree_contents([('tree/file', 'a\n')])
2905
tree.add('file', 'file-id')
2906
tree.commit('a', rev_id='one')
2907
preview = TransformPreview(tree)
2908
self.addCleanup(preview.finalize)
2909
file_trans_id = preview.trans_id_file_id('file-id')
2910
preview.adjust_path('newname', preview.root, file_trans_id)
2911
preview_tree = preview.get_preview_tree()
2915
annotation = preview_tree.annotate_iter('file-id', 'me:')
2916
self.assertEqual(expected, annotation)
2918
def test_annotate_deleted(self):
2919
tree = self.make_branch_and_tree('tree')
2920
self.build_tree_contents([('tree/file', 'a\n')])
2921
tree.add('file', 'file-id')
2922
tree.commit('a', rev_id='one')
2923
self.build_tree_contents([('tree/file', 'a\nb\n')])
2924
preview = TransformPreview(tree)
2925
self.addCleanup(preview.finalize)
2926
file_trans_id = preview.trans_id_file_id('file-id')
2927
preview.delete_contents(file_trans_id)
2928
preview_tree = preview.get_preview_tree()
2929
annotation = preview_tree.annotate_iter('file-id', 'me:')
2930
self.assertIs(None, annotation)
2932
def test_stored_kind(self):
2933
preview = self.get_empty_preview()
2934
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2935
preview_tree = preview.get_preview_tree()
2936
self.assertEqual('file', preview_tree.stored_kind('file-id'))
2938
def test_is_executable(self):
2939
preview = self.get_empty_preview()
2940
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2941
preview.set_executability(True, preview.trans_id_file_id('file-id'))
2942
preview_tree = preview.get_preview_tree()
2943
self.assertEqual(True, preview_tree.is_executable('file-id'))
2945
def test_get_set_parent_ids(self):
2946
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2947
self.assertEqual([], preview_tree.get_parent_ids())
2948
preview_tree.set_parent_ids(['rev-1'])
2949
self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
2951
def test_plan_file_merge(self):
2952
work_a = self.make_branch_and_tree('wta')
2953
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2954
work_a.add('file', 'file-id')
2955
base_id = work_a.commit('base version')
2956
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2957
preview = TransformPreview(work_a)
2958
self.addCleanup(preview.finalize)
2959
trans_id = preview.trans_id_file_id('file-id')
2960
preview.delete_contents(trans_id)
2961
preview.create_file('b\nc\nd\ne\n', trans_id)
2962
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2963
tree_a = preview.get_preview_tree()
2964
tree_a.set_parent_ids([base_id])
2966
('killed-a', 'a\n'),
2967
('killed-b', 'b\n'),
2968
('unchanged', 'c\n'),
2969
('unchanged', 'd\n'),
2972
], list(tree_a.plan_file_merge('file-id', tree_b)))
2974
def test_plan_file_merge_revision_tree(self):
2975
work_a = self.make_branch_and_tree('wta')
2976
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2977
work_a.add('file', 'file-id')
2978
base_id = work_a.commit('base version')
2979
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2980
preview = TransformPreview(work_a.basis_tree())
2981
self.addCleanup(preview.finalize)
2982
trans_id = preview.trans_id_file_id('file-id')
2983
preview.delete_contents(trans_id)
2984
preview.create_file('b\nc\nd\ne\n', trans_id)
2985
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2986
tree_a = preview.get_preview_tree()
2987
tree_a.set_parent_ids([base_id])
2989
('killed-a', 'a\n'),
2990
('killed-b', 'b\n'),
2991
('unchanged', 'c\n'),
2992
('unchanged', 'd\n'),
2995
], list(tree_a.plan_file_merge('file-id', tree_b)))
2997
def test_walkdirs(self):
2998
preview = self.get_empty_preview()
2999
root = preview.new_directory('', ROOT_PARENT, 'tree-root')
3000
# FIXME: new_directory should mark root.
3001
preview.fixup_new_roots()
3002
preview_tree = preview.get_preview_tree()
3003
file_trans_id = preview.new_file('a', preview.root, 'contents',
3005
expected = [(('', 'tree-root'),
3006
[('a', 'a', 'file', None, 'a-id', 'file')])]
3007
self.assertEqual(expected, list(preview_tree.walkdirs()))
3009
def test_extras(self):
3010
work_tree = self.make_branch_and_tree('tree')
3011
self.build_tree(['tree/removed-file', 'tree/existing-file',
3012
'tree/not-removed-file'])
3013
work_tree.add(['removed-file', 'not-removed-file'])
3014
preview = TransformPreview(work_tree)
3015
self.addCleanup(preview.finalize)
3016
preview.new_file('new-file', preview.root, 'contents')
3017
preview.new_file('new-versioned-file', preview.root, 'contents',
3019
tree = preview.get_preview_tree()
3020
preview.unversion_file(preview.trans_id_tree_path('removed-file'))
3021
self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
3024
def test_merge_into_preview(self):
3025
work_tree = self.make_branch_and_tree('tree')
3026
self.build_tree_contents([('tree/file','b\n')])
3027
work_tree.add('file', 'file-id')
3028
work_tree.commit('first commit')
3029
child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
3030
self.build_tree_contents([('child/file','b\nc\n')])
3031
child_tree.commit('child commit')
3032
child_tree.lock_write()
3033
self.addCleanup(child_tree.unlock)
3034
work_tree.lock_write()
3035
self.addCleanup(work_tree.unlock)
3036
preview = TransformPreview(work_tree)
3037
self.addCleanup(preview.finalize)
3038
file_trans_id = preview.trans_id_file_id('file-id')
3039
preview.delete_contents(file_trans_id)
3040
preview.create_file('a\nb\n', file_trans_id)
3041
preview_tree = preview.get_preview_tree()
3042
merger = Merger.from_revision_ids(None, preview_tree,
3043
child_tree.branch.last_revision(),
3044
other_branch=child_tree.branch,
3045
tree_branch=work_tree.branch)
3046
merger.merge_type = Merge3Merger
3047
tt = merger.make_merger().make_preview_transform()
3048
self.addCleanup(tt.finalize)
3049
final_tree = tt.get_preview_tree()
3050
self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
3052
def test_merge_preview_into_workingtree(self):
3053
tree = self.make_branch_and_tree('tree')
3054
tree.set_root_id('TREE_ROOT')
3055
tt = TransformPreview(tree)
3056
self.addCleanup(tt.finalize)
3057
tt.new_file('name', tt.root, 'content', 'file-id')
3058
tree2 = self.make_branch_and_tree('tree2')
3059
tree2.set_root_id('TREE_ROOT')
3060
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
3061
None, tree.basis_tree())
3062
merger.merge_type = Merge3Merger
3065
def test_merge_preview_into_workingtree_handles_conflicts(self):
3066
tree = self.make_branch_and_tree('tree')
3067
self.build_tree_contents([('tree/foo', 'bar')])
3068
tree.add('foo', 'foo-id')
3070
tt = TransformPreview(tree)
3071
self.addCleanup(tt.finalize)
3072
trans_id = tt.trans_id_file_id('foo-id')
3073
tt.delete_contents(trans_id)
3074
tt.create_file('baz', trans_id)
3075
tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
3076
self.build_tree_contents([('tree2/foo', 'qux')])
3078
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
3079
pb, tree.basis_tree())
3080
merger.merge_type = Merge3Merger
3083
def test_has_filename(self):
3084
wt = self.make_branch_and_tree('tree')
3085
self.build_tree(['tree/unmodified', 'tree/removed', 'tree/modified'])
3086
tt = TransformPreview(wt)
3087
removed_id = tt.trans_id_tree_path('removed')
3088
tt.delete_contents(removed_id)
3089
tt.new_file('new', tt.root, 'contents')
3090
modified_id = tt.trans_id_tree_path('modified')
3091
tt.delete_contents(modified_id)
3092
tt.create_file('modified-contents', modified_id)
3093
self.addCleanup(tt.finalize)
3094
tree = tt.get_preview_tree()
3095
self.assertTrue(tree.has_filename('unmodified'))
3096
self.assertFalse(tree.has_filename('not-present'))
3097
self.assertFalse(tree.has_filename('removed'))
3098
self.assertTrue(tree.has_filename('new'))
3099
self.assertTrue(tree.has_filename('modified'))
3101
def test_is_executable(self):
3102
tree = self.make_branch_and_tree('tree')
3103
preview = TransformPreview(tree)
3104
self.addCleanup(preview.finalize)
3105
preview.new_file('foo', preview.root, 'bar', 'baz-id')
3106
preview_tree = preview.get_preview_tree()
3107
self.assertEqual(False, preview_tree.is_executable('baz-id',
3109
self.assertEqual(False, preview_tree.is_executable('baz-id'))
3111
def test_commit_preview_tree(self):
3112
tree = self.make_branch_and_tree('tree')
3113
rev_id = tree.commit('rev1')
3114
tree.branch.lock_write()
3115
self.addCleanup(tree.branch.unlock)
3116
tt = TransformPreview(tree)
3117
tt.new_file('file', tt.root, 'contents', 'file_id')
3118
self.addCleanup(tt.finalize)
3119
preview = tt.get_preview_tree()
3120
preview.set_parent_ids([rev_id])
3121
builder = tree.branch.get_commit_builder([rev_id])
3122
list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
3123
builder.finish_inventory()
3124
rev2_id = builder.commit('rev2')
3125
rev2_tree = tree.branch.repository.revision_tree(rev2_id)
3126
self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
3128
def test_ascii_limbo_paths(self):
3129
self.requireFeature(tests.UnicodeFilenameFeature)
3130
branch = self.make_branch('any')
3131
tree = branch.repository.revision_tree(_mod_revision.NULL_REVISION)
3132
tt = TransformPreview(tree)
3133
self.addCleanup(tt.finalize)
3134
foo_id = tt.new_directory('', ROOT_PARENT)
3135
bar_id = tt.new_file(u'\u1234bar', foo_id, 'contents')
3136
limbo_path = tt._limbo_name(bar_id)
3137
self.assertEqual(limbo_path.encode('ascii', 'replace'), limbo_path)
3140
class FakeSerializer(object):
3141
"""Serializer implementation that simply returns the input.
3143
The input is returned in the order used by pack.ContainerPushParser.
3146
def bytes_record(bytes, names):
3150
class TestSerializeTransform(tests.TestCaseWithTransport):
3152
_test_needs_features = [tests.UnicodeFilenameFeature]
3154
def get_preview(self, tree=None):
3156
tree = self.make_branch_and_tree('tree')
3157
tt = TransformPreview(tree)
3158
self.addCleanup(tt.finalize)
3161
def assertSerializesTo(self, expected, tt):
3162
records = list(tt.serialize(FakeSerializer()))
3163
self.assertEqual(expected, records)
3166
def default_attribs():
3171
'_new_executability': {},
3173
'_tree_path_ids': {'': 'new-0'},
3175
'_removed_contents': [],
3176
'_non_present_ids': {},
3179
def make_records(self, attribs, contents):
3181
(((('attribs'),),), bencode.bencode(attribs))]
3182
records.extend([(((n, k),), c) for n, k, c in contents])
3185
def creation_records(self):
3186
attribs = self.default_attribs()
3187
attribs['_id_number'] = 3
3188
attribs['_new_name'] = {
3189
'new-1': u'foo\u1234'.encode('utf-8'), 'new-2': 'qux'}
3190
attribs['_new_id'] = {'new-1': 'baz', 'new-2': 'quxx'}
3191
attribs['_new_parent'] = {'new-1': 'new-0', 'new-2': 'new-0'}
3192
attribs['_new_executability'] = {'new-1': 1}
3194
('new-1', 'file', 'i 1\nbar\n'),
3195
('new-2', 'directory', ''),
3197
return self.make_records(attribs, contents)
3199
def test_serialize_creation(self):
3200
tt = self.get_preview()
3201
tt.new_file(u'foo\u1234', tt.root, 'bar', 'baz', True)
3202
tt.new_directory('qux', tt.root, 'quxx')
3203
self.assertSerializesTo(self.creation_records(), tt)
3205
def test_deserialize_creation(self):
3206
tt = self.get_preview()
3207
tt.deserialize(iter(self.creation_records()))
3208
self.assertEqual(3, tt._id_number)
3209
self.assertEqual({'new-1': u'foo\u1234',
3210
'new-2': 'qux'}, tt._new_name)
3211
self.assertEqual({'new-1': 'baz', 'new-2': 'quxx'}, tt._new_id)
3212
self.assertEqual({'new-1': tt.root, 'new-2': tt.root}, tt._new_parent)
3213
self.assertEqual({'baz': 'new-1', 'quxx': 'new-2'}, tt._r_new_id)
3214
self.assertEqual({'new-1': True}, tt._new_executability)
3215
self.assertEqual({'new-1': 'file',
3216
'new-2': 'directory'}, tt._new_contents)
3217
foo_limbo = open(tt._limbo_name('new-1'), 'rb')
3219
foo_content = foo_limbo.read()
3222
self.assertEqual('bar', foo_content)
3224
def symlink_creation_records(self):
3225
attribs = self.default_attribs()
3226
attribs['_id_number'] = 2
3227
attribs['_new_name'] = {'new-1': u'foo\u1234'.encode('utf-8')}
3228
attribs['_new_parent'] = {'new-1': 'new-0'}
3229
contents = [('new-1', 'symlink', u'bar\u1234'.encode('utf-8'))]
3230
return self.make_records(attribs, contents)
3232
def test_serialize_symlink_creation(self):
3233
self.requireFeature(tests.SymlinkFeature)
3234
tt = self.get_preview()
3235
tt.new_symlink(u'foo\u1234', tt.root, u'bar\u1234')
3236
self.assertSerializesTo(self.symlink_creation_records(), tt)
3238
def test_deserialize_symlink_creation(self):
3239
self.requireFeature(tests.SymlinkFeature)
3240
tt = self.get_preview()
3241
tt.deserialize(iter(self.symlink_creation_records()))
3242
abspath = tt._limbo_name('new-1')
3243
foo_content = osutils.readlink(abspath)
3244
self.assertEqual(u'bar\u1234', foo_content)
3246
def make_destruction_preview(self):
3247
tree = self.make_branch_and_tree('.')
3248
self.build_tree([u'foo\u1234', 'bar'])
3249
tree.add([u'foo\u1234', 'bar'], ['foo-id', 'bar-id'])
3250
return self.get_preview(tree)
3252
def destruction_records(self):
3253
attribs = self.default_attribs()
3254
attribs['_id_number'] = 3
3255
attribs['_removed_id'] = ['new-1']
3256
attribs['_removed_contents'] = ['new-2']
3257
attribs['_tree_path_ids'] = {
3259
u'foo\u1234'.encode('utf-8'): 'new-1',
3262
return self.make_records(attribs, [])
3264
def test_serialize_destruction(self):
3265
tt = self.make_destruction_preview()
3266
foo_trans_id = tt.trans_id_tree_file_id('foo-id')
3267
tt.unversion_file(foo_trans_id)
3268
bar_trans_id = tt.trans_id_tree_file_id('bar-id')
3269
tt.delete_contents(bar_trans_id)
3270
self.assertSerializesTo(self.destruction_records(), tt)
3272
def test_deserialize_destruction(self):
3273
tt = self.make_destruction_preview()
3274
tt.deserialize(iter(self.destruction_records()))
3275
self.assertEqual({u'foo\u1234': 'new-1',
3277
'': tt.root}, tt._tree_path_ids)
3278
self.assertEqual({'new-1': u'foo\u1234',
3280
tt.root: ''}, tt._tree_id_paths)
3281
self.assertEqual(set(['new-1']), tt._removed_id)
3282
self.assertEqual(set(['new-2']), tt._removed_contents)
3284
def missing_records(self):
3285
attribs = self.default_attribs()
3286
attribs['_id_number'] = 2
3287
attribs['_non_present_ids'] = {
3289
return self.make_records(attribs, [])
3291
def test_serialize_missing(self):
3292
tt = self.get_preview()
3293
boo_trans_id = tt.trans_id_file_id('boo')
3294
self.assertSerializesTo(self.missing_records(), tt)
3296
def test_deserialize_missing(self):
3297
tt = self.get_preview()
3298
tt.deserialize(iter(self.missing_records()))
3299
self.assertEqual({'boo': 'new-1'}, tt._non_present_ids)
3301
def make_modification_preview(self):
3302
LINES_ONE = 'aa\nbb\ncc\ndd\n'
3303
LINES_TWO = 'z\nbb\nx\ndd\n'
3304
tree = self.make_branch_and_tree('tree')
3305
self.build_tree_contents([('tree/file', LINES_ONE)])
3306
tree.add('file', 'file-id')
3307
return self.get_preview(tree), LINES_TWO
3309
def modification_records(self):
3310
attribs = self.default_attribs()
3311
attribs['_id_number'] = 2
3312
attribs['_tree_path_ids'] = {
3315
attribs['_removed_contents'] = ['new-1']
3316
contents = [('new-1', 'file',
3317
'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
3318
return self.make_records(attribs, contents)
3320
def test_serialize_modification(self):
3321
tt, LINES = self.make_modification_preview()
3322
trans_id = tt.trans_id_file_id('file-id')
3323
tt.delete_contents(trans_id)
3324
tt.create_file(LINES, trans_id)
3325
self.assertSerializesTo(self.modification_records(), tt)
3327
def test_deserialize_modification(self):
3328
tt, LINES = self.make_modification_preview()
3329
tt.deserialize(iter(self.modification_records()))
3330
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
3332
def make_kind_change_preview(self):
3333
LINES = 'a\nb\nc\nd\n'
3334
tree = self.make_branch_and_tree('tree')
3335
self.build_tree(['tree/foo/'])
3336
tree.add('foo', 'foo-id')
3337
return self.get_preview(tree), LINES
3339
def kind_change_records(self):
3340
attribs = self.default_attribs()
3341
attribs['_id_number'] = 2
3342
attribs['_tree_path_ids'] = {
3345
attribs['_removed_contents'] = ['new-1']
3346
contents = [('new-1', 'file',
3347
'i 4\na\nb\nc\nd\n\n')]
3348
return self.make_records(attribs, contents)
3350
def test_serialize_kind_change(self):
3351
tt, LINES = self.make_kind_change_preview()
3352
trans_id = tt.trans_id_file_id('foo-id')
3353
tt.delete_contents(trans_id)
3354
tt.create_file(LINES, trans_id)
3355
self.assertSerializesTo(self.kind_change_records(), tt)
3357
def test_deserialize_kind_change(self):
3358
tt, LINES = self.make_kind_change_preview()
3359
tt.deserialize(iter(self.kind_change_records()))
3360
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
3362
def make_add_contents_preview(self):
3363
LINES = 'a\nb\nc\nd\n'
3364
tree = self.make_branch_and_tree('tree')
3365
self.build_tree(['tree/foo'])
3367
os.unlink('tree/foo')
3368
return self.get_preview(tree), LINES
3370
def add_contents_records(self):
3371
attribs = self.default_attribs()
3372
attribs['_id_number'] = 2
3373
attribs['_tree_path_ids'] = {
3376
contents = [('new-1', 'file',
3377
'i 4\na\nb\nc\nd\n\n')]
3378
return self.make_records(attribs, contents)
3380
def test_serialize_add_contents(self):
3381
tt, LINES = self.make_add_contents_preview()
3382
trans_id = tt.trans_id_tree_path('foo')
3383
tt.create_file(LINES, trans_id)
3384
self.assertSerializesTo(self.add_contents_records(), tt)
3386
def test_deserialize_add_contents(self):
3387
tt, LINES = self.make_add_contents_preview()
3388
tt.deserialize(iter(self.add_contents_records()))
3389
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
3391
def test_get_parents_lines(self):
3392
LINES_ONE = 'aa\nbb\ncc\ndd\n'
3393
LINES_TWO = 'z\nbb\nx\ndd\n'
3394
tree = self.make_branch_and_tree('tree')
3395
self.build_tree_contents([('tree/file', LINES_ONE)])
3396
tree.add('file', 'file-id')
3397
tt = self.get_preview(tree)
3398
trans_id = tt.trans_id_tree_path('file')
3399
self.assertEqual((['aa\n', 'bb\n', 'cc\n', 'dd\n'],),
3400
tt._get_parents_lines(trans_id))
3402
def test_get_parents_texts(self):
3403
LINES_ONE = 'aa\nbb\ncc\ndd\n'
3404
LINES_TWO = 'z\nbb\nx\ndd\n'
3405
tree = self.make_branch_and_tree('tree')
3406
self.build_tree_contents([('tree/file', LINES_ONE)])
3407
tree.add('file', 'file-id')
3408
tt = self.get_preview(tree)
3409
trans_id = tt.trans_id_tree_path('file')
3410
self.assertEqual((LINES_ONE,),
3411
tt._get_parents_texts(trans_id))
3414
class TestOrphan(tests.TestCaseWithTransport):
3416
def test_no_orphan_for_transform_preview(self):
3417
tree = self.make_branch_and_tree('tree')
3418
tt = transform.TransformPreview(tree)
3419
self.addCleanup(tt.finalize)
3420
self.assertRaises(NotImplementedError, tt.new_orphan, 'foo', 'bar')
3422
def _set_orphan_policy(self, wt, policy):
3423
wt.branch.get_config().set_user_option('bzr.transform.orphan_policy',
3426
def _prepare_orphan(self, wt):
3427
self.build_tree(['dir/', 'dir/file', 'dir/foo'])
3428
wt.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
3429
wt.commit('add dir and file ignoring foo')
3430
tt = transform.TreeTransform(wt)
3431
self.addCleanup(tt.finalize)
3432
# dir and bar are deleted
3433
dir_tid = tt.trans_id_tree_path('dir')
3434
file_tid = tt.trans_id_tree_path('dir/file')
3435
orphan_tid = tt.trans_id_tree_path('dir/foo')
3436
tt.delete_contents(file_tid)
3437
tt.unversion_file(file_tid)
3438
tt.delete_contents(dir_tid)
3439
tt.unversion_file(dir_tid)
3440
# There should be a conflict because dir still contain foo
3441
raw_conflicts = tt.find_conflicts()
3442
self.assertLength(1, raw_conflicts)
3443
self.assertEqual(('missing parent', 'new-1'), raw_conflicts[0])
3444
return tt, orphan_tid
3446
def test_new_orphan_created(self):
3447
wt = self.make_branch_and_tree('.')
3448
self._set_orphan_policy(wt, 'move')
3449
tt, orphan_tid = self._prepare_orphan(wt)
3452
warnings.append(args[0] % args[1:])
3453
self.overrideAttr(trace, 'warning', warning)
3454
remaining_conflicts = resolve_conflicts(tt)
3455
self.assertEquals(['dir/foo has been orphaned in bzr-orphans'],
3457
# Yeah for resolved conflicts !
3458
self.assertLength(0, remaining_conflicts)
3459
# We have a new orphan
3460
self.assertEquals('foo.~1~', tt.final_name(orphan_tid))
3461
self.assertEquals('bzr-orphans',
3462
tt.final_name(tt.final_parent(orphan_tid)))
3464
def test_never_orphan(self):
3465
wt = self.make_branch_and_tree('.')
3466
self._set_orphan_policy(wt, 'conflict')
3467
tt, orphan_tid = self._prepare_orphan(wt)
3468
remaining_conflicts = resolve_conflicts(tt)
3469
self.assertLength(1, remaining_conflicts)
3470
self.assertEqual(('deleting parent', 'Not deleting', 'new-1'),
3471
remaining_conflicts.pop())
3473
def test_orphan_error(self):
3474
def bogus_orphan(tt, orphan_id, parent_id):
3475
raise transform.OrphaningError(tt.final_name(orphan_id),
3476
tt.final_name(parent_id))
3477
transform.orphaning_registry.register('bogus', bogus_orphan,
3478
'Raise an error when orphaning')
3479
wt = self.make_branch_and_tree('.')
3480
self._set_orphan_policy(wt, 'bogus')
3481
tt, orphan_tid = self._prepare_orphan(wt)
3482
remaining_conflicts = resolve_conflicts(tt)
3483
self.assertLength(1, remaining_conflicts)
3484
self.assertEqual(('deleting parent', 'Not deleting', 'new-1'),
3485
remaining_conflicts.pop())
3487
def test_unknown_orphan_policy(self):
3488
wt = self.make_branch_and_tree('.')
3489
# Set a fictional policy nobody ever implemented
3490
self._set_orphan_policy(wt, 'donttouchmypreciouuus')
3491
tt, orphan_tid = self._prepare_orphan(wt)
3494
warnings.append(args[0] % args[1:])
3495
self.overrideAttr(trace, 'warning', warning)
3496
remaining_conflicts = resolve_conflicts(tt)
3497
# We fallback to the default policy which create a conflict
3498
self.assertLength(1, remaining_conflicts)
3499
self.assertEqual(('deleting parent', 'Not deleting', 'new-1'),
3500
remaining_conflicts.pop())
3501
self.assertLength(1, warnings)
3502
self.assertStartsWith(warnings[0], 'donttouchmypreciouuus')