508
776
transform, root = self.get_transform()
509
777
wt = transform._tree
779
self.addCleanup(wt.unlock)
510
780
transform.new_file('set_on_creation', root, 'Set on creation', 'soc',
512
sac = transform.new_file('set_after_creation', root, 'Set after creation', 'sac')
782
sac = transform.new_file('set_after_creation', root,
783
'Set after creation', 'sac')
513
784
transform.set_executability(True, sac)
514
uws = transform.new_file('unset_without_set', root, 'Unset badly', 'uws')
785
uws = transform.new_file('unset_without_set', root, 'Unset badly',
515
787
self.assertRaises(KeyError, transform.set_executability, None, uws)
516
788
transform.apply()
517
789
self.assertTrue(wt.is_executable('soc'))
518
790
self.assertTrue(wt.is_executable('sac'))
792
def test_preserve_mode(self):
793
"""File mode is preserved when replacing content"""
794
if sys.platform == 'win32':
795
raise TestSkipped('chmod has no effect on win32')
796
transform, root = self.get_transform()
797
transform.new_file('file1', root, 'contents', 'file1-id', True)
800
self.addCleanup(self.wt.unlock)
801
self.assertTrue(self.wt.is_executable('file1-id'))
802
transform, root = self.get_transform()
803
file1_id = transform.trans_id_tree_file_id('file1-id')
804
transform.delete_contents(file1_id)
805
transform.create_file('contents2', file1_id)
807
self.assertTrue(self.wt.is_executable('file1-id'))
809
def test__set_mode_stats_correctly(self):
810
"""_set_mode stats to determine file mode."""
811
if sys.platform == 'win32':
812
raise TestSkipped('chmod has no effect on win32')
816
def instrumented_stat(path):
817
stat_paths.append(path)
818
return real_stat(path)
820
transform, root = self.get_transform()
822
bar1_id = transform.new_file('bar', root, 'bar contents 1\n',
823
file_id='bar-id-1', executable=False)
826
transform, root = self.get_transform()
827
bar1_id = transform.trans_id_tree_path('bar')
828
bar2_id = transform.trans_id_tree_path('bar2')
830
os.stat = instrumented_stat
831
transform.create_file('bar2 contents\n', bar2_id, mode_id=bar1_id)
836
bar1_abspath = self.wt.abspath('bar')
837
self.assertEqual([bar1_abspath], stat_paths)
839
def test_iter_changes(self):
840
self.wt.set_root_id('eert_toor')
841
transform, root = self.get_transform()
842
transform.new_file('old', root, 'blah', 'id-1', True)
844
transform, root = self.get_transform()
846
self.assertEqual([], list(transform.iter_changes()))
847
old = transform.trans_id_tree_file_id('id-1')
848
transform.unversion_file(old)
849
self.assertEqual([('id-1', ('old', None), False, (True, False),
850
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
851
(True, True))], list(transform.iter_changes()))
852
transform.new_directory('new', root, 'id-1')
853
self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
854
('eert_toor', 'eert_toor'), ('old', 'new'),
855
('file', 'directory'),
856
(True, False))], list(transform.iter_changes()))
860
def test_iter_changes_new(self):
861
self.wt.set_root_id('eert_toor')
862
transform, root = self.get_transform()
863
transform.new_file('old', root, 'blah')
865
transform, root = self.get_transform()
867
old = transform.trans_id_tree_path('old')
868
transform.version_file('id-1', old)
869
self.assertEqual([('id-1', (None, 'old'), False, (False, True),
870
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
871
(False, False))], list(transform.iter_changes()))
875
def test_iter_changes_modifications(self):
876
self.wt.set_root_id('eert_toor')
877
transform, root = self.get_transform()
878
transform.new_file('old', root, 'blah', 'id-1')
879
transform.new_file('new', root, 'blah')
880
transform.new_directory('subdir', root, 'subdir-id')
882
transform, root = self.get_transform()
884
old = transform.trans_id_tree_path('old')
885
subdir = transform.trans_id_tree_file_id('subdir-id')
886
new = transform.trans_id_tree_path('new')
887
self.assertEqual([], list(transform.iter_changes()))
890
transform.delete_contents(old)
891
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
892
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
893
(False, False))], list(transform.iter_changes()))
896
transform.create_file('blah', old)
897
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
898
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
899
(False, False))], list(transform.iter_changes()))
900
transform.cancel_deletion(old)
901
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
902
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
903
(False, False))], list(transform.iter_changes()))
904
transform.cancel_creation(old)
906
# move file_id to a different file
907
self.assertEqual([], list(transform.iter_changes()))
908
transform.unversion_file(old)
909
transform.version_file('id-1', new)
910
transform.adjust_path('old', root, new)
911
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
912
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
913
(False, False))], list(transform.iter_changes()))
914
transform.cancel_versioning(new)
915
transform._removed_id = set()
918
self.assertEqual([], list(transform.iter_changes()))
919
transform.set_executability(True, old)
920
self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
921
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
922
(False, True))], list(transform.iter_changes()))
923
transform.set_executability(None, old)
926
self.assertEqual([], list(transform.iter_changes()))
927
transform.adjust_path('new', root, old)
928
transform._new_parent = {}
929
self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
930
('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
931
(False, False))], list(transform.iter_changes()))
932
transform._new_name = {}
935
self.assertEqual([], list(transform.iter_changes()))
936
transform.adjust_path('new', subdir, old)
937
transform._new_name = {}
938
self.assertEqual([('id-1', ('old', 'subdir/old'), False,
939
(True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
940
('file', 'file'), (False, False))],
941
list(transform.iter_changes()))
942
transform._new_path = {}
947
def test_iter_changes_modified_bleed(self):
948
self.wt.set_root_id('eert_toor')
949
"""Modified flag should not bleed from one change to another"""
950
# unfortunately, we have no guarantee that file1 (which is modified)
951
# will be applied before file2. And if it's applied after file2, it
952
# obviously can't bleed into file2's change output. But for now, it
954
transform, root = self.get_transform()
955
transform.new_file('file1', root, 'blah', 'id-1')
956
transform.new_file('file2', root, 'blah', 'id-2')
958
transform, root = self.get_transform()
960
transform.delete_contents(transform.trans_id_file_id('id-1'))
961
transform.set_executability(True,
962
transform.trans_id_file_id('id-2'))
963
self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
964
('eert_toor', 'eert_toor'), ('file1', u'file1'),
965
('file', None), (False, False)),
966
('id-2', (u'file2', u'file2'), False, (True, True),
967
('eert_toor', 'eert_toor'), ('file2', u'file2'),
968
('file', 'file'), (False, True))],
969
list(transform.iter_changes()))
973
def test_iter_changes_move_missing(self):
974
"""Test moving ids with no files around"""
975
self.wt.set_root_id('toor_eert')
976
# Need two steps because versioning a non-existant file is a conflict.
977
transform, root = self.get_transform()
978
transform.new_directory('floater', root, 'floater-id')
980
transform, root = self.get_transform()
981
transform.delete_contents(transform.trans_id_tree_path('floater'))
983
transform, root = self.get_transform()
984
floater = transform.trans_id_tree_path('floater')
986
transform.adjust_path('flitter', root, floater)
987
self.assertEqual([('floater-id', ('floater', 'flitter'), False,
988
(True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
989
(None, None), (False, False))], list(transform.iter_changes()))
993
def test_iter_changes_pointless(self):
994
"""Ensure that no-ops are not treated as modifications"""
995
self.wt.set_root_id('eert_toor')
996
transform, root = self.get_transform()
997
transform.new_file('old', root, 'blah', 'id-1')
998
transform.new_directory('subdir', root, 'subdir-id')
1000
transform, root = self.get_transform()
1002
old = transform.trans_id_tree_path('old')
1003
subdir = transform.trans_id_tree_file_id('subdir-id')
1004
self.assertEqual([], list(transform.iter_changes()))
1005
transform.delete_contents(subdir)
1006
transform.create_directory(subdir)
1007
transform.set_executability(False, old)
1008
transform.unversion_file(old)
1009
transform.version_file('id-1', old)
1010
transform.adjust_path('old', root, old)
1011
self.assertEqual([], list(transform.iter_changes()))
1013
transform.finalize()
1015
def test_rename_count(self):
1016
transform, root = self.get_transform()
1017
transform.new_file('name1', root, 'contents')
1018
self.assertEqual(transform.rename_count, 0)
1020
self.assertEqual(transform.rename_count, 1)
1021
transform2, root = self.get_transform()
1022
transform2.adjust_path('name2', root,
1023
transform2.trans_id_tree_path('name1'))
1024
self.assertEqual(transform2.rename_count, 0)
1026
self.assertEqual(transform2.rename_count, 2)
1028
def test_change_parent(self):
1029
"""Ensure that after we change a parent, the results are still right.
1031
Renames and parent changes on pending transforms can happen as part
1032
of conflict resolution, and are explicitly permitted by the
1035
This test ensures they work correctly with the rename-avoidance
1038
transform, root = self.get_transform()
1039
parent1 = transform.new_directory('parent1', root)
1040
child1 = transform.new_file('child1', parent1, 'contents')
1041
parent2 = transform.new_directory('parent2', root)
1042
transform.adjust_path('child1', parent2, child1)
1044
self.failIfExists(self.wt.abspath('parent1/child1'))
1045
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1046
# rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1047
# no rename for child1 (counting only renames during apply)
1048
self.failUnlessEqual(2, transform.rename_count)
1050
def test_cancel_parent(self):
1051
"""Cancelling a parent doesn't cause deletion of a non-empty directory
1053
This is like the test_change_parent, except that we cancel the parent
1054
before adjusting the path. The transform must detect that the
1055
directory is non-empty, and move children to safe locations.
1057
transform, root = self.get_transform()
1058
parent1 = transform.new_directory('parent1', root)
1059
child1 = transform.new_file('child1', parent1, 'contents')
1060
child2 = transform.new_file('child2', parent1, 'contents')
1062
transform.cancel_creation(parent1)
1064
self.fail('Failed to move child1 before deleting parent1')
1065
transform.cancel_creation(child2)
1066
transform.create_directory(parent1)
1068
transform.cancel_creation(parent1)
1069
# If the transform incorrectly believes that child2 is still in
1070
# parent1's limbo directory, it will try to rename it and fail
1071
# because was already moved by the first cancel_creation.
1073
self.fail('Transform still thinks child2 is a child of parent1')
1074
parent2 = transform.new_directory('parent2', root)
1075
transform.adjust_path('child1', parent2, child1)
1077
self.failIfExists(self.wt.abspath('parent1'))
1078
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1079
# rename limbo/new-3 => parent2, rename limbo/new-2 => child1
1080
self.failUnlessEqual(2, transform.rename_count)
1082
def test_adjust_and_cancel(self):
1083
"""Make sure adjust_path keeps track of limbo children properly"""
1084
transform, root = self.get_transform()
1085
parent1 = transform.new_directory('parent1', root)
1086
child1 = transform.new_file('child1', parent1, 'contents')
1087
parent2 = transform.new_directory('parent2', root)
1088
transform.adjust_path('child1', parent2, child1)
1089
transform.cancel_creation(child1)
1091
transform.cancel_creation(parent1)
1092
# if the transform thinks child1 is still in parent1's limbo
1093
# directory, it will attempt to move it and fail.
1095
self.fail('Transform still thinks child1 is a child of parent1')
1096
transform.finalize()
1098
def test_noname_contents(self):
1099
"""TreeTransform should permit deferring naming files."""
1100
transform, root = self.get_transform()
1101
parent = transform.trans_id_file_id('parent-id')
1103
transform.create_directory(parent)
1105
self.fail("Can't handle contents with no name")
1106
transform.finalize()
1108
def test_noname_contents_nested(self):
1109
"""TreeTransform should permit deferring naming files."""
1110
transform, root = self.get_transform()
1111
parent = transform.trans_id_file_id('parent-id')
1113
transform.create_directory(parent)
1115
self.fail("Can't handle contents with no name")
1116
child = transform.new_directory('child', parent)
1117
transform.adjust_path('parent', root, parent)
1119
self.failUnlessExists(self.wt.abspath('parent/child'))
1120
self.assertEqual(1, transform.rename_count)
1122
def test_reuse_name(self):
1123
"""Avoid reusing the same limbo name for different files"""
1124
transform, root = self.get_transform()
1125
parent = transform.new_directory('parent', root)
1126
child1 = transform.new_directory('child', parent)
1128
child2 = transform.new_directory('child', parent)
1130
self.fail('Tranform tried to use the same limbo name twice')
1131
transform.adjust_path('child2', parent, child2)
1133
# limbo/new-1 => parent, limbo/new-3 => parent/child2
1134
# child2 is put into top-level limbo because child1 has already
1135
# claimed the direct limbo path when child2 is created. There is no
1136
# advantage in renaming files once they're in top-level limbo, except
1138
self.assertEqual(2, transform.rename_count)
1140
def test_reuse_when_first_moved(self):
1141
"""Don't avoid direct paths when it is safe to use them"""
1142
transform, root = self.get_transform()
1143
parent = transform.new_directory('parent', root)
1144
child1 = transform.new_directory('child', parent)
1145
transform.adjust_path('child1', parent, child1)
1146
child2 = transform.new_directory('child', parent)
1148
# limbo/new-1 => parent
1149
self.assertEqual(1, transform.rename_count)
1151
def test_reuse_after_cancel(self):
1152
"""Don't avoid direct paths when it is safe to use them"""
1153
transform, root = self.get_transform()
1154
parent2 = transform.new_directory('parent2', root)
1155
child1 = transform.new_directory('child1', parent2)
1156
transform.cancel_creation(parent2)
1157
transform.create_directory(parent2)
1158
child2 = transform.new_directory('child1', parent2)
1159
transform.adjust_path('child2', parent2, child1)
1161
# limbo/new-1 => parent2, limbo/new-2 => parent2/child1
1162
self.assertEqual(2, transform.rename_count)
1164
def test_finalize_order(self):
1165
"""Finalize must be done in child-to-parent order"""
1166
transform, root = self.get_transform()
1167
parent = transform.new_directory('parent', root)
1168
child = transform.new_directory('child', parent)
1170
transform.finalize()
1172
self.fail('Tried to remove parent before child1')
1174
def test_cancel_with_cancelled_child_should_succeed(self):
1175
transform, root = self.get_transform()
1176
parent = transform.new_directory('parent', root)
1177
child = transform.new_directory('child', parent)
1178
transform.cancel_creation(child)
1179
transform.cancel_creation(parent)
1180
transform.finalize()
1182
def test_rollback_on_directory_clash(self):
1184
wt = self.make_branch_and_tree('.')
1185
tt = TreeTransform(wt) # TreeTransform obtains write lock
1187
foo = tt.new_directory('foo', tt.root)
1188
tt.new_file('bar', foo, 'foobar')
1189
baz = tt.new_directory('baz', tt.root)
1190
tt.new_file('qux', baz, 'quux')
1191
# Ask for a rename 'foo' -> 'baz'
1192
tt.adjust_path('baz', tt.root, foo)
1193
# Lie to tt that we've already resolved all conflicts.
1194
tt.apply(no_conflicts=True)
1198
# The rename will fail because the target directory is not empty (but
1199
# raises FileExists anyway).
1200
err = self.assertRaises(errors.FileExists, tt_helper)
1201
self.assertContainsRe(str(err),
1202
"^File exists: .+/baz")
1204
def test_two_directories_clash(self):
1206
wt = self.make_branch_and_tree('.')
1207
tt = TreeTransform(wt) # TreeTransform obtains write lock
1209
foo_1 = tt.new_directory('foo', tt.root)
1210
tt.new_directory('bar', foo_1)
1211
# Adding the same directory with a different content
1212
foo_2 = tt.new_directory('foo', tt.root)
1213
tt.new_directory('baz', foo_2)
1214
# Lie to tt that we've already resolved all conflicts.
1215
tt.apply(no_conflicts=True)
1219
err = self.assertRaises(errors.FileExists, tt_helper)
1220
self.assertContainsRe(str(err),
1221
"^File exists: .+/foo")
1223
def test_two_directories_clash_finalize(self):
1225
wt = self.make_branch_and_tree('.')
1226
tt = TreeTransform(wt) # TreeTransform obtains write lock
1228
foo_1 = tt.new_directory('foo', tt.root)
1229
tt.new_directory('bar', foo_1)
1230
# Adding the same directory with a different content
1231
foo_2 = tt.new_directory('foo', tt.root)
1232
tt.new_directory('baz', foo_2)
1233
# Lie to tt that we've already resolved all conflicts.
1234
tt.apply(no_conflicts=True)
1238
err = self.assertRaises(errors.FileExists, tt_helper)
1239
self.assertContainsRe(str(err),
1240
"^File exists: .+/foo")
1242
def test_file_to_directory(self):
1243
wt = self.make_branch_and_tree('.')
1244
self.build_tree(['foo'])
1247
tt = TreeTransform(wt)
1248
self.addCleanup(tt.finalize)
1249
foo_trans_id = tt.trans_id_tree_path("foo")
1250
tt.delete_contents(foo_trans_id)
1251
tt.create_directory(foo_trans_id)
1252
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1253
tt.create_file(["aa\n"], bar_trans_id)
1254
tt.version_file("bar-1", bar_trans_id)
1256
self.failUnlessExists("foo/bar")
1259
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1264
changes = wt.changes_from(wt.basis_tree())
1265
self.assertFalse(changes.has_changed(), changes)
1267
def test_file_to_symlink(self):
1268
self.requireFeature(SymlinkFeature)
1269
wt = self.make_branch_and_tree('.')
1270
self.build_tree(['foo'])
1273
tt = TreeTransform(wt)
1274
self.addCleanup(tt.finalize)
1275
foo_trans_id = tt.trans_id_tree_path("foo")
1276
tt.delete_contents(foo_trans_id)
1277
tt.create_symlink("bar", foo_trans_id)
1279
self.failUnlessExists("foo")
1281
self.addCleanup(wt.unlock)
1282
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1285
def test_dir_to_file(self):
1286
wt = self.make_branch_and_tree('.')
1287
self.build_tree(['foo/', 'foo/bar'])
1288
wt.add(['foo', 'foo/bar'])
1290
tt = TreeTransform(wt)
1291
self.addCleanup(tt.finalize)
1292
foo_trans_id = tt.trans_id_tree_path("foo")
1293
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1294
tt.delete_contents(foo_trans_id)
1295
tt.delete_versioned(bar_trans_id)
1296
tt.create_file(["aa\n"], foo_trans_id)
1298
self.failUnlessExists("foo")
1300
self.addCleanup(wt.unlock)
1301
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1304
def test_dir_to_hardlink(self):
1305
self.requireFeature(HardlinkFeature)
1306
wt = self.make_branch_and_tree('.')
1307
self.build_tree(['foo/', 'foo/bar'])
1308
wt.add(['foo', 'foo/bar'])
1310
tt = TreeTransform(wt)
1311
self.addCleanup(tt.finalize)
1312
foo_trans_id = tt.trans_id_tree_path("foo")
1313
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1314
tt.delete_contents(foo_trans_id)
1315
tt.delete_versioned(bar_trans_id)
1316
self.build_tree(['baz'])
1317
tt.create_hardlink("baz", foo_trans_id)
1319
self.failUnlessExists("foo")
1320
self.failUnlessExists("baz")
1322
self.addCleanup(wt.unlock)
1323
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1326
def test_no_final_path(self):
1327
transform, root = self.get_transform()
1328
trans_id = transform.trans_id_file_id('foo')
1329
transform.create_file('bar', trans_id)
1330
transform.cancel_creation(trans_id)
1333
def test_create_from_tree(self):
1334
tree1 = self.make_branch_and_tree('tree1')
1335
self.build_tree_contents([('tree1/foo/',), ('tree1/bar', 'baz')])
1336
tree1.add(['foo', 'bar'], ['foo-id', 'bar-id'])
1337
tree2 = self.make_branch_and_tree('tree2')
1338
tt = TreeTransform(tree2)
1339
foo_trans_id = tt.create_path('foo', tt.root)
1340
create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1341
bar_trans_id = tt.create_path('bar', tt.root)
1342
create_from_tree(tt, bar_trans_id, tree1, 'bar-id')
1344
self.assertEqual('directory', osutils.file_kind('tree2/foo'))
1345
self.assertFileEqual('baz', 'tree2/bar')
1347
def test_create_from_tree_bytes(self):
1348
"""Provided lines are used instead of tree content."""
1349
tree1 = self.make_branch_and_tree('tree1')
1350
self.build_tree_contents([('tree1/foo', 'bar'),])
1351
tree1.add('foo', 'foo-id')
1352
tree2 = self.make_branch_and_tree('tree2')
1353
tt = TreeTransform(tree2)
1354
foo_trans_id = tt.create_path('foo', tt.root)
1355
create_from_tree(tt, foo_trans_id, tree1, 'foo-id', bytes='qux')
1357
self.assertFileEqual('qux', 'tree2/foo')
1359
def test_create_from_tree_symlink(self):
1360
self.requireFeature(SymlinkFeature)
1361
tree1 = self.make_branch_and_tree('tree1')
1362
os.symlink('bar', 'tree1/foo')
1363
tree1.add('foo', 'foo-id')
1364
tt = TreeTransform(self.make_branch_and_tree('tree2'))
1365
foo_trans_id = tt.create_path('foo', tt.root)
1366
create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1368
self.assertEqual('bar', os.readlink('tree2/foo'))
521
1371
class TransformGroup(object):
522
def __init__(self, dirname):
1373
def __init__(self, dirname, root_id):
523
1374
self.name = dirname
524
1375
os.mkdir(dirname)
525
1376
self.wt = BzrDir.create_standalone_workingtree(dirname)
1377
self.wt.set_root_id(root_id)
526
1378
self.b = self.wt.branch
527
1379
self.tt = TreeTransform(self.wt)
528
1380
self.root = self.tt.trans_id_tree_file_id(self.wt.get_root_id())
530
1383
def conflict_text(tree, merge):
531
1384
template = '%s TREE\n%s%s\n%s%s MERGE-SOURCE\n'
532
1385
return template % ('<' * 7, tree, '=' * 7, merge, '>' * 7)
535
1388
class TestTransformMerge(TestCaseInTempDir):
536
1390
def test_text_merge(self):
537
base = TransformGroup("base")
1391
root_id = generate_ids.gen_root_id()
1392
base = TransformGroup("base", root_id)
538
1393
base.tt.new_file('a', base.root, 'a\nb\nc\nd\be\n', 'a')
539
1394
base.tt.new_file('b', base.root, 'b1', 'b')
540
1395
base.tt.new_file('c', base.root, 'c', 'c')
725
1584
a.add(['foo', 'foo/bar', 'foo/baz'])
726
1585
a.commit('initial commit')
727
1586
b = BzrDir.create_standalone_workingtree('b')
728
build_tree(a.basis_tree(), b)
1587
basis = a.basis_tree()
1589
self.addCleanup(basis.unlock)
1590
build_tree(basis, b)
729
1591
self.assertIs(os.path.isdir('b/foo'), True)
730
1592
self.assertEqual(file('b/foo/bar', 'rb').read(), "contents")
731
1593
self.assertEqual(os.readlink('b/foo/baz'), 'a/foo/bar')
1595
def test_build_with_references(self):
1596
tree = self.make_branch_and_tree('source',
1597
format='dirstate-with-subtree')
1598
subtree = self.make_branch_and_tree('source/subtree',
1599
format='dirstate-with-subtree')
1600
tree.add_reference(subtree)
1601
tree.commit('a revision')
1602
tree.branch.create_checkout('target')
1603
self.failUnlessExists('target')
1604
self.failUnlessExists('target/subtree')
1606
def test_file_conflict_handling(self):
1607
"""Ensure that when building trees, conflict handling is done"""
1608
source = self.make_branch_and_tree('source')
1609
target = self.make_branch_and_tree('target')
1610
self.build_tree(['source/file', 'target/file'])
1611
source.add('file', 'new-file')
1612
source.commit('added file')
1613
build_tree(source.basis_tree(), target)
1614
self.assertEqual([DuplicateEntry('Moved existing file to',
1615
'file.moved', 'file', None, 'new-file')],
1617
target2 = self.make_branch_and_tree('target2')
1618
target_file = file('target2/file', 'wb')
1620
source_file = file('source/file', 'rb')
1622
target_file.write(source_file.read())
1627
build_tree(source.basis_tree(), target2)
1628
self.assertEqual([], target2.conflicts())
1630
def test_symlink_conflict_handling(self):
1631
"""Ensure that when building trees, conflict handling is done"""
1632
self.requireFeature(SymlinkFeature)
1633
source = self.make_branch_and_tree('source')
1634
os.symlink('foo', 'source/symlink')
1635
source.add('symlink', 'new-symlink')
1636
source.commit('added file')
1637
target = self.make_branch_and_tree('target')
1638
os.symlink('bar', 'target/symlink')
1639
build_tree(source.basis_tree(), target)
1640
self.assertEqual([DuplicateEntry('Moved existing file to',
1641
'symlink.moved', 'symlink', None, 'new-symlink')],
1643
target = self.make_branch_and_tree('target2')
1644
os.symlink('foo', 'target2/symlink')
1645
build_tree(source.basis_tree(), target)
1646
self.assertEqual([], target.conflicts())
1648
def test_directory_conflict_handling(self):
1649
"""Ensure that when building trees, conflict handling is done"""
1650
source = self.make_branch_and_tree('source')
1651
target = self.make_branch_and_tree('target')
1652
self.build_tree(['source/dir1/', 'source/dir1/file', 'target/dir1/'])
1653
source.add(['dir1', 'dir1/file'], ['new-dir1', 'new-file'])
1654
source.commit('added file')
1655
build_tree(source.basis_tree(), target)
1656
self.assertEqual([], target.conflicts())
1657
self.failUnlessExists('target/dir1/file')
1659
# Ensure contents are merged
1660
target = self.make_branch_and_tree('target2')
1661
self.build_tree(['target2/dir1/', 'target2/dir1/file2'])
1662
build_tree(source.basis_tree(), target)
1663
self.assertEqual([], target.conflicts())
1664
self.failUnlessExists('target2/dir1/file2')
1665
self.failUnlessExists('target2/dir1/file')
1667
# Ensure new contents are suppressed for existing branches
1668
target = self.make_branch_and_tree('target3')
1669
self.make_branch('target3/dir1')
1670
self.build_tree(['target3/dir1/file2'])
1671
build_tree(source.basis_tree(), target)
1672
self.failIfExists('target3/dir1/file')
1673
self.failUnlessExists('target3/dir1/file2')
1674
self.failUnlessExists('target3/dir1.diverted/file')
1675
self.assertEqual([DuplicateEntry('Diverted to',
1676
'dir1.diverted', 'dir1', 'new-dir1', None)],
1679
target = self.make_branch_and_tree('target4')
1680
self.build_tree(['target4/dir1/'])
1681
self.make_branch('target4/dir1/file')
1682
build_tree(source.basis_tree(), target)
1683
self.failUnlessExists('target4/dir1/file')
1684
self.assertEqual('directory', file_kind('target4/dir1/file'))
1685
self.failUnlessExists('target4/dir1/file.diverted')
1686
self.assertEqual([DuplicateEntry('Diverted to',
1687
'dir1/file.diverted', 'dir1/file', 'new-file', None)],
1690
def test_mixed_conflict_handling(self):
1691
"""Ensure that when building trees, conflict handling is done"""
1692
source = self.make_branch_and_tree('source')
1693
target = self.make_branch_and_tree('target')
1694
self.build_tree(['source/name', 'target/name/'])
1695
source.add('name', 'new-name')
1696
source.commit('added file')
1697
build_tree(source.basis_tree(), target)
1698
self.assertEqual([DuplicateEntry('Moved existing file to',
1699
'name.moved', 'name', None, 'new-name')], target.conflicts())
1701
def test_raises_in_populated(self):
1702
source = self.make_branch_and_tree('source')
1703
self.build_tree(['source/name'])
1705
source.commit('added name')
1706
target = self.make_branch_and_tree('target')
1707
self.build_tree(['target/name'])
1709
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1710
build_tree, source.basis_tree(), target)
1712
def test_build_tree_rename_count(self):
1713
source = self.make_branch_and_tree('source')
1714
self.build_tree(['source/file1', 'source/dir1/'])
1715
source.add(['file1', 'dir1'])
1716
source.commit('add1')
1717
target1 = self.make_branch_and_tree('target1')
1718
transform_result = build_tree(source.basis_tree(), target1)
1719
self.assertEqual(2, transform_result.rename_count)
1721
self.build_tree(['source/dir1/file2'])
1722
source.add(['dir1/file2'])
1723
source.commit('add3')
1724
target2 = self.make_branch_and_tree('target2')
1725
transform_result = build_tree(source.basis_tree(), target2)
1726
# children of non-root directories should not be renamed
1727
self.assertEqual(2, transform_result.rename_count)
1729
def create_ab_tree(self):
1730
"""Create a committed test tree with two files"""
1731
source = self.make_branch_and_tree('source')
1732
self.build_tree_contents([('source/file1', 'A')])
1733
self.build_tree_contents([('source/file2', 'B')])
1734
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1735
source.commit('commit files')
1737
self.addCleanup(source.unlock)
1740
def test_build_tree_accelerator_tree(self):
1741
source = self.create_ab_tree()
1742
self.build_tree_contents([('source/file2', 'C')])
1744
real_source_get_file = source.get_file
1745
def get_file(file_id, path=None):
1746
calls.append(file_id)
1747
return real_source_get_file(file_id, path)
1748
source.get_file = get_file
1749
target = self.make_branch_and_tree('target')
1750
revision_tree = source.basis_tree()
1751
revision_tree.lock_read()
1752
self.addCleanup(revision_tree.unlock)
1753
build_tree(revision_tree, target, source)
1754
self.assertEqual(['file1-id'], calls)
1756
self.addCleanup(target.unlock)
1757
self.assertEqual([], list(target.iter_changes(revision_tree)))
1759
def test_build_tree_accelerator_tree_missing_file(self):
1760
source = self.create_ab_tree()
1761
os.unlink('source/file1')
1762
source.remove(['file2'])
1763
target = self.make_branch_and_tree('target')
1764
revision_tree = source.basis_tree()
1765
revision_tree.lock_read()
1766
self.addCleanup(revision_tree.unlock)
1767
build_tree(revision_tree, target, source)
1769
self.addCleanup(target.unlock)
1770
self.assertEqual([], list(target.iter_changes(revision_tree)))
1772
def test_build_tree_accelerator_wrong_kind(self):
1773
self.requireFeature(SymlinkFeature)
1774
source = self.make_branch_and_tree('source')
1775
self.build_tree_contents([('source/file1', '')])
1776
self.build_tree_contents([('source/file2', '')])
1777
source.add(['file1', 'file2'], ['file1-id', 'file2-id'])
1778
source.commit('commit files')
1779
os.unlink('source/file2')
1780
self.build_tree_contents([('source/file2/', 'C')])
1781
os.unlink('source/file1')
1782
os.symlink('file2', 'source/file1')
1784
real_source_get_file = source.get_file
1785
def get_file(file_id, path=None):
1786
calls.append(file_id)
1787
return real_source_get_file(file_id, path)
1788
source.get_file = get_file
1789
target = self.make_branch_and_tree('target')
1790
revision_tree = source.basis_tree()
1791
revision_tree.lock_read()
1792
self.addCleanup(revision_tree.unlock)
1793
build_tree(revision_tree, target, source)
1794
self.assertEqual([], calls)
1796
self.addCleanup(target.unlock)
1797
self.assertEqual([], list(target.iter_changes(revision_tree)))
1799
def test_build_tree_hardlink(self):
1800
self.requireFeature(HardlinkFeature)
1801
source = self.create_ab_tree()
1802
target = self.make_branch_and_tree('target')
1803
revision_tree = source.basis_tree()
1804
revision_tree.lock_read()
1805
self.addCleanup(revision_tree.unlock)
1806
build_tree(revision_tree, target, source, hardlink=True)
1808
self.addCleanup(target.unlock)
1809
self.assertEqual([], list(target.iter_changes(revision_tree)))
1810
source_stat = os.stat('source/file1')
1811
target_stat = os.stat('target/file1')
1812
self.assertEqual(source_stat, target_stat)
1814
# Explicitly disallowing hardlinks should prevent them.
1815
target2 = self.make_branch_and_tree('target2')
1816
build_tree(revision_tree, target2, source, hardlink=False)
1818
self.addCleanup(target2.unlock)
1819
self.assertEqual([], list(target2.iter_changes(revision_tree)))
1820
source_stat = os.stat('source/file1')
1821
target2_stat = os.stat('target2/file1')
1822
self.assertNotEqual(source_stat, target2_stat)
1824
def test_build_tree_accelerator_tree_moved(self):
1825
source = self.make_branch_and_tree('source')
1826
self.build_tree_contents([('source/file1', 'A')])
1827
source.add(['file1'], ['file1-id'])
1828
source.commit('commit files')
1829
source.rename_one('file1', 'file2')
1831
self.addCleanup(source.unlock)
1832
target = self.make_branch_and_tree('target')
1833
revision_tree = source.basis_tree()
1834
revision_tree.lock_read()
1835
self.addCleanup(revision_tree.unlock)
1836
build_tree(revision_tree, target, source)
1838
self.addCleanup(target.unlock)
1839
self.assertEqual([], list(target.iter_changes(revision_tree)))
1841
def test_build_tree_hardlinks_preserve_execute(self):
1842
self.requireFeature(HardlinkFeature)
1843
source = self.create_ab_tree()
1844
tt = TreeTransform(source)
1845
trans_id = tt.trans_id_tree_file_id('file1-id')
1846
tt.set_executability(True, trans_id)
1848
self.assertTrue(source.is_executable('file1-id'))
1849
target = self.make_branch_and_tree('target')
1850
revision_tree = source.basis_tree()
1851
revision_tree.lock_read()
1852
self.addCleanup(revision_tree.unlock)
1853
build_tree(revision_tree, target, source, hardlink=True)
1855
self.addCleanup(target.unlock)
1856
self.assertEqual([], list(target.iter_changes(revision_tree)))
1857
self.assertTrue(source.is_executable('file1-id'))
1859
def test_case_insensitive_build_tree_inventory(self):
1860
if (tests.CaseInsensitiveFilesystemFeature.available()
1861
or tests.CaseInsCasePresFilenameFeature.available()):
1862
raise tests.UnavailableFeature('Fully case sensitive filesystem')
1863
source = self.make_branch_and_tree('source')
1864
self.build_tree(['source/file', 'source/FILE'])
1865
source.add(['file', 'FILE'], ['lower-id', 'upper-id'])
1866
source.commit('added files')
1867
# Don't try this at home, kids!
1868
# Force the tree to report that it is case insensitive
1869
target = self.make_branch_and_tree('target')
1870
target.case_sensitive = False
1871
build_tree(source.basis_tree(), target, source, delta_from_tree=True)
1872
self.assertEqual('file.moved', target.id2path('lower-id'))
1873
self.assertEqual('FILE', target.id2path('upper-id'))
733
1876
class MockTransform(object):
735
1878
def has_named_child(self, by_parent, parent_id, name):
759
1904
self.assertEqual(name, 'name.~1~')
760
1905
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
761
1906
self.assertEqual(name, 'name.~4~')
1909
class TestFileMover(tests.TestCaseWithTransport):
1911
def test_file_mover(self):
1912
self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
1913
mover = _FileMover()
1914
mover.rename('a', 'q')
1915
self.failUnlessExists('q')
1916
self.failIfExists('a')
1917
self.failUnlessExists('q/b')
1918
self.failUnlessExists('c')
1919
self.failUnlessExists('c/d')
1921
def test_pre_delete_rollback(self):
1922
self.build_tree(['a/'])
1923
mover = _FileMover()
1924
mover.pre_delete('a', 'q')
1925
self.failUnlessExists('q')
1926
self.failIfExists('a')
1928
self.failIfExists('q')
1929
self.failUnlessExists('a')
1931
def test_apply_deletions(self):
1932
self.build_tree(['a/', 'b/'])
1933
mover = _FileMover()
1934
mover.pre_delete('a', 'q')
1935
mover.pre_delete('b', 'r')
1936
self.failUnlessExists('q')
1937
self.failUnlessExists('r')
1938
self.failIfExists('a')
1939
self.failIfExists('b')
1940
mover.apply_deletions()
1941
self.failIfExists('q')
1942
self.failIfExists('r')
1943
self.failIfExists('a')
1944
self.failIfExists('b')
1946
def test_file_mover_rollback(self):
1947
self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
1948
mover = _FileMover()
1949
mover.rename('c/d', 'c/f')
1950
mover.rename('c/e', 'c/d')
1952
mover.rename('a', 'c')
1953
except errors.FileExists, e:
1955
self.failUnlessExists('a')
1956
self.failUnlessExists('c/d')
1959
class Bogus(Exception):
1963
class TestTransformRollback(tests.TestCaseWithTransport):
1965
class ExceptionFileMover(_FileMover):
1967
def __init__(self, bad_source=None, bad_target=None):
1968
_FileMover.__init__(self)
1969
self.bad_source = bad_source
1970
self.bad_target = bad_target
1972
def rename(self, source, target):
1973
if (self.bad_source is not None and
1974
source.endswith(self.bad_source)):
1976
elif (self.bad_target is not None and
1977
target.endswith(self.bad_target)):
1980
_FileMover.rename(self, source, target)
1982
def test_rollback_rename(self):
1983
tree = self.make_branch_and_tree('.')
1984
self.build_tree(['a/', 'a/b'])
1985
tt = TreeTransform(tree)
1986
self.addCleanup(tt.finalize)
1987
a_id = tt.trans_id_tree_path('a')
1988
tt.adjust_path('c', tt.root, a_id)
1989
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1990
self.assertRaises(Bogus, tt.apply,
1991
_mover=self.ExceptionFileMover(bad_source='a'))
1992
self.failUnlessExists('a')
1993
self.failUnlessExists('a/b')
1995
self.failUnlessExists('c')
1996
self.failUnlessExists('c/d')
1998
def test_rollback_rename_into_place(self):
1999
tree = self.make_branch_and_tree('.')
2000
self.build_tree(['a/', 'a/b'])
2001
tt = TreeTransform(tree)
2002
self.addCleanup(tt.finalize)
2003
a_id = tt.trans_id_tree_path('a')
2004
tt.adjust_path('c', tt.root, a_id)
2005
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2006
self.assertRaises(Bogus, tt.apply,
2007
_mover=self.ExceptionFileMover(bad_target='c/d'))
2008
self.failUnlessExists('a')
2009
self.failUnlessExists('a/b')
2011
self.failUnlessExists('c')
2012
self.failUnlessExists('c/d')
2014
def test_rollback_deletion(self):
2015
tree = self.make_branch_and_tree('.')
2016
self.build_tree(['a/', 'a/b'])
2017
tt = TreeTransform(tree)
2018
self.addCleanup(tt.finalize)
2019
a_id = tt.trans_id_tree_path('a')
2020
tt.delete_contents(a_id)
2021
tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
2022
self.assertRaises(Bogus, tt.apply,
2023
_mover=self.ExceptionFileMover(bad_target='d'))
2024
self.failUnlessExists('a')
2025
self.failUnlessExists('a/b')
2027
def test_resolve_no_parent(self):
2028
wt = self.make_branch_and_tree('.')
2029
tt = TreeTransform(wt)
2030
self.addCleanup(tt.finalize)
2031
parent = tt.trans_id_file_id('parent-id')
2032
tt.new_file('file', parent, 'Contents')
2033
resolve_conflicts(tt)
2036
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
2037
('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
2039
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
2040
('', ''), ('directory', 'directory'), (False, None))
2043
class TestTransformPreview(tests.TestCaseWithTransport):
2045
def create_tree(self):
2046
tree = self.make_branch_and_tree('.')
2047
self.build_tree_contents([('a', 'content 1')])
2048
tree.add('a', 'a-id')
2049
tree.commit('rev1', rev_id='rev1')
2050
return tree.branch.repository.revision_tree('rev1')
2052
def get_empty_preview(self):
2053
repository = self.make_repository('repo')
2054
tree = repository.revision_tree(_mod_revision.NULL_REVISION)
2055
preview = TransformPreview(tree)
2056
self.addCleanup(preview.finalize)
2059
def test_transform_preview(self):
2060
revision_tree = self.create_tree()
2061
preview = TransformPreview(revision_tree)
2062
self.addCleanup(preview.finalize)
2064
def test_transform_preview_tree(self):
2065
revision_tree = self.create_tree()
2066
preview = TransformPreview(revision_tree)
2067
self.addCleanup(preview.finalize)
2068
preview.get_preview_tree()
2070
def test_transform_new_file(self):
2071
revision_tree = self.create_tree()
2072
preview = TransformPreview(revision_tree)
2073
self.addCleanup(preview.finalize)
2074
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2075
preview_tree = preview.get_preview_tree()
2076
self.assertEqual(preview_tree.kind('file2-id'), 'file')
2078
preview_tree.get_file('file2-id').read(), 'content B\n')
2080
def test_diff_preview_tree(self):
2081
revision_tree = self.create_tree()
2082
preview = TransformPreview(revision_tree)
2083
self.addCleanup(preview.finalize)
2084
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2085
preview_tree = preview.get_preview_tree()
2087
show_diff_trees(revision_tree, preview_tree, out)
2088
lines = out.getvalue().splitlines()
2089
self.assertEqual(lines[0], "=== added file 'file2'")
2090
# 3 lines of diff administrivia
2091
self.assertEqual(lines[4], "+content B")
2093
def test_transform_conflicts(self):
2094
revision_tree = self.create_tree()
2095
preview = TransformPreview(revision_tree)
2096
self.addCleanup(preview.finalize)
2097
preview.new_file('a', preview.root, 'content 2')
2098
resolve_conflicts(preview)
2099
trans_id = preview.trans_id_file_id('a-id')
2100
self.assertEqual('a.moved', preview.final_name(trans_id))
2102
def get_tree_and_preview_tree(self):
2103
revision_tree = self.create_tree()
2104
preview = TransformPreview(revision_tree)
2105
self.addCleanup(preview.finalize)
2106
a_trans_id = preview.trans_id_file_id('a-id')
2107
preview.delete_contents(a_trans_id)
2108
preview.create_file('b content', a_trans_id)
2109
preview_tree = preview.get_preview_tree()
2110
return revision_tree, preview_tree
2112
def test_iter_changes(self):
2113
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2114
root = revision_tree.inventory.root.file_id
2115
self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
2116
(root, root), ('a', 'a'), ('file', 'file'),
2118
list(preview_tree.iter_changes(revision_tree)))
2120
def test_include_unchanged_succeeds(self):
2121
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2122
changes = preview_tree.iter_changes(revision_tree,
2123
include_unchanged=True)
2124
root = revision_tree.inventory.root.file_id
2126
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2128
def test_specific_files(self):
2129
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2130
changes = preview_tree.iter_changes(revision_tree,
2131
specific_files=[''])
2132
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2134
def test_want_unversioned(self):
2135
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2136
changes = preview_tree.iter_changes(revision_tree,
2137
want_unversioned=True)
2138
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2140
def test_ignore_extra_trees_no_specific_files(self):
2141
# extra_trees is harmless without specific_files, so we'll silently
2142
# accept it, even though we won't use it.
2143
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2144
preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
2146
def test_ignore_require_versioned_no_specific_files(self):
2147
# require_versioned is meaningless without specific_files.
2148
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2149
preview_tree.iter_changes(revision_tree, require_versioned=False)
2151
def test_ignore_pb(self):
2152
# pb could be supported, but TT.iter_changes doesn't support it.
2153
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2154
preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
2156
def test_kind(self):
2157
revision_tree = self.create_tree()
2158
preview = TransformPreview(revision_tree)
2159
self.addCleanup(preview.finalize)
2160
preview.new_file('file', preview.root, 'contents', 'file-id')
2161
preview.new_directory('directory', preview.root, 'dir-id')
2162
preview_tree = preview.get_preview_tree()
2163
self.assertEqual('file', preview_tree.kind('file-id'))
2164
self.assertEqual('directory', preview_tree.kind('dir-id'))
2166
def test_get_file_mtime(self):
2167
preview = self.get_empty_preview()
2168
file_trans_id = preview.new_file('file', preview.root, 'contents',
2170
limbo_path = preview._limbo_name(file_trans_id)
2171
preview_tree = preview.get_preview_tree()
2172
self.assertEqual(os.stat(limbo_path).st_mtime,
2173
preview_tree.get_file_mtime('file-id'))
2175
def test_get_file(self):
2176
preview = self.get_empty_preview()
2177
preview.new_file('file', preview.root, 'contents', 'file-id')
2178
preview_tree = preview.get_preview_tree()
2179
tree_file = preview_tree.get_file('file-id')
2181
self.assertEqual('contents', tree_file.read())
2185
def test_get_symlink_target(self):
2186
self.requireFeature(SymlinkFeature)
2187
preview = self.get_empty_preview()
2188
preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
2189
preview_tree = preview.get_preview_tree()
2190
self.assertEqual('target',
2191
preview_tree.get_symlink_target('symlink-id'))
2193
def test_all_file_ids(self):
2194
tree = self.make_branch_and_tree('tree')
2195
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
2196
tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
2197
preview = TransformPreview(tree)
2198
self.addCleanup(preview.finalize)
2199
preview.unversion_file(preview.trans_id_file_id('b-id'))
2200
c_trans_id = preview.trans_id_file_id('c-id')
2201
preview.unversion_file(c_trans_id)
2202
preview.version_file('c-id', c_trans_id)
2203
preview_tree = preview.get_preview_tree()
2204
self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
2205
preview_tree.all_file_ids())
2207
def test_path2id_deleted_unchanged(self):
2208
tree = self.make_branch_and_tree('tree')
2209
self.build_tree(['tree/unchanged', 'tree/deleted'])
2210
tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
2211
preview = TransformPreview(tree)
2212
self.addCleanup(preview.finalize)
2213
preview.unversion_file(preview.trans_id_file_id('deleted-id'))
2214
preview_tree = preview.get_preview_tree()
2215
self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
2216
self.assertIs(None, preview_tree.path2id('deleted'))
2218
def test_path2id_created(self):
2219
tree = self.make_branch_and_tree('tree')
2220
self.build_tree(['tree/unchanged'])
2221
tree.add(['unchanged'], ['unchanged-id'])
2222
preview = TransformPreview(tree)
2223
self.addCleanup(preview.finalize)
2224
preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
2225
'contents', 'new-id')
2226
preview_tree = preview.get_preview_tree()
2227
self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
2229
def test_path2id_moved(self):
2230
tree = self.make_branch_and_tree('tree')
2231
self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
2232
tree.add(['old_parent', 'old_parent/child'],
2233
['old_parent-id', 'child-id'])
2234
preview = TransformPreview(tree)
2235
self.addCleanup(preview.finalize)
2236
new_parent = preview.new_directory('new_parent', preview.root,
2238
preview.adjust_path('child', new_parent,
2239
preview.trans_id_file_id('child-id'))
2240
preview_tree = preview.get_preview_tree()
2241
self.assertIs(None, preview_tree.path2id('old_parent/child'))
2242
self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
2244
def test_path2id_renamed_parent(self):
2245
tree = self.make_branch_and_tree('tree')
2246
self.build_tree(['tree/old_name/', 'tree/old_name/child'])
2247
tree.add(['old_name', 'old_name/child'],
2248
['parent-id', 'child-id'])
2249
preview = TransformPreview(tree)
2250
self.addCleanup(preview.finalize)
2251
preview.adjust_path('new_name', preview.root,
2252
preview.trans_id_file_id('parent-id'))
2253
preview_tree = preview.get_preview_tree()
2254
self.assertIs(None, preview_tree.path2id('old_name/child'))
2255
self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
2257
def assertMatchingIterEntries(self, tt, specific_file_ids=None):
2258
preview_tree = tt.get_preview_tree()
2259
preview_result = list(preview_tree.iter_entries_by_dir(
2263
actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
2264
self.assertEqual(actual_result, preview_result)
2266
def test_iter_entries_by_dir_new(self):
2267
tree = self.make_branch_and_tree('tree')
2268
tt = TreeTransform(tree)
2269
tt.new_file('new', tt.root, 'contents', 'new-id')
2270
self.assertMatchingIterEntries(tt)
2272
def test_iter_entries_by_dir_deleted(self):
2273
tree = self.make_branch_and_tree('tree')
2274
self.build_tree(['tree/deleted'])
2275
tree.add('deleted', 'deleted-id')
2276
tt = TreeTransform(tree)
2277
tt.delete_contents(tt.trans_id_file_id('deleted-id'))
2278
self.assertMatchingIterEntries(tt)
2280
def test_iter_entries_by_dir_unversioned(self):
2281
tree = self.make_branch_and_tree('tree')
2282
self.build_tree(['tree/removed'])
2283
tree.add('removed', 'removed-id')
2284
tt = TreeTransform(tree)
2285
tt.unversion_file(tt.trans_id_file_id('removed-id'))
2286
self.assertMatchingIterEntries(tt)
2288
def test_iter_entries_by_dir_moved(self):
2289
tree = self.make_branch_and_tree('tree')
2290
self.build_tree(['tree/moved', 'tree/new_parent/'])
2291
tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
2292
tt = TreeTransform(tree)
2293
tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
2294
tt.trans_id_file_id('moved-id'))
2295
self.assertMatchingIterEntries(tt)
2297
def test_iter_entries_by_dir_specific_file_ids(self):
2298
tree = self.make_branch_and_tree('tree')
2299
tree.set_root_id('tree-root-id')
2300
self.build_tree(['tree/parent/', 'tree/parent/child'])
2301
tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
2302
tt = TreeTransform(tree)
2303
self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
2305
def test_symlink_content_summary(self):
2306
self.requireFeature(SymlinkFeature)
2307
preview = self.get_empty_preview()
2308
preview.new_symlink('path', preview.root, 'target', 'path-id')
2309
summary = preview.get_preview_tree().path_content_summary('path')
2310
self.assertEqual(('symlink', None, None, 'target'), summary)
2312
def test_missing_content_summary(self):
2313
preview = self.get_empty_preview()
2314
summary = preview.get_preview_tree().path_content_summary('path')
2315
self.assertEqual(('missing', None, None, None), summary)
2317
def test_deleted_content_summary(self):
2318
tree = self.make_branch_and_tree('tree')
2319
self.build_tree(['tree/path/'])
2321
preview = TransformPreview(tree)
2322
self.addCleanup(preview.finalize)
2323
preview.delete_contents(preview.trans_id_tree_path('path'))
2324
summary = preview.get_preview_tree().path_content_summary('path')
2325
self.assertEqual(('missing', None, None, None), summary)
2327
def test_file_content_summary_executable(self):
2328
if not osutils.supports_executable():
2329
raise TestNotApplicable()
2330
preview = self.get_empty_preview()
2331
path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2332
preview.set_executability(True, path_id)
2333
summary = preview.get_preview_tree().path_content_summary('path')
2334
self.assertEqual(4, len(summary))
2335
self.assertEqual('file', summary[0])
2336
# size must be known
2337
self.assertEqual(len('contents'), summary[1])
2339
self.assertEqual(True, summary[2])
2340
# will not have hash (not cheap to determine)
2341
self.assertIs(None, summary[3])
2343
def test_change_executability(self):
2344
if not osutils.supports_executable():
2345
raise TestNotApplicable()
2346
tree = self.make_branch_and_tree('tree')
2347
self.build_tree(['tree/path'])
2349
preview = TransformPreview(tree)
2350
self.addCleanup(preview.finalize)
2351
path_id = preview.trans_id_tree_path('path')
2352
preview.set_executability(True, path_id)
2353
summary = preview.get_preview_tree().path_content_summary('path')
2354
self.assertEqual(True, summary[2])
2356
def test_file_content_summary_non_exec(self):
2357
preview = self.get_empty_preview()
2358
preview.new_file('path', preview.root, 'contents', 'path-id')
2359
summary = preview.get_preview_tree().path_content_summary('path')
2360
self.assertEqual(4, len(summary))
2361
self.assertEqual('file', summary[0])
2362
# size must be known
2363
self.assertEqual(len('contents'), summary[1])
2365
if osutils.supports_executable():
2366
self.assertEqual(False, summary[2])
2368
self.assertEqual(None, summary[2])
2369
# will not have hash (not cheap to determine)
2370
self.assertIs(None, summary[3])
2372
def test_dir_content_summary(self):
2373
preview = self.get_empty_preview()
2374
preview.new_directory('path', preview.root, 'path-id')
2375
summary = preview.get_preview_tree().path_content_summary('path')
2376
self.assertEqual(('directory', None, None, None), summary)
2378
def test_tree_content_summary(self):
2379
preview = self.get_empty_preview()
2380
path = preview.new_directory('path', preview.root, 'path-id')
2381
preview.set_tree_reference('rev-1', path)
2382
summary = preview.get_preview_tree().path_content_summary('path')
2383
self.assertEqual(4, len(summary))
2384
self.assertEqual('tree-reference', summary[0])
2386
def test_annotate(self):
2387
tree = self.make_branch_and_tree('tree')
2388
self.build_tree_contents([('tree/file', 'a\n')])
2389
tree.add('file', 'file-id')
2390
tree.commit('a', rev_id='one')
2391
self.build_tree_contents([('tree/file', 'a\nb\n')])
2392
preview = TransformPreview(tree)
2393
self.addCleanup(preview.finalize)
2394
file_trans_id = preview.trans_id_file_id('file-id')
2395
preview.delete_contents(file_trans_id)
2396
preview.create_file('a\nb\nc\n', file_trans_id)
2397
preview_tree = preview.get_preview_tree()
2403
annotation = preview_tree.annotate_iter('file-id', 'me:')
2404
self.assertEqual(expected, annotation)
2406
def test_annotate_missing(self):
2407
preview = self.get_empty_preview()
2408
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2409
preview_tree = preview.get_preview_tree()
2415
annotation = preview_tree.annotate_iter('file-id', 'me:')
2416
self.assertEqual(expected, annotation)
2418
def test_annotate_rename(self):
2419
tree = self.make_branch_and_tree('tree')
2420
self.build_tree_contents([('tree/file', 'a\n')])
2421
tree.add('file', 'file-id')
2422
tree.commit('a', rev_id='one')
2423
preview = TransformPreview(tree)
2424
self.addCleanup(preview.finalize)
2425
file_trans_id = preview.trans_id_file_id('file-id')
2426
preview.adjust_path('newname', preview.root, file_trans_id)
2427
preview_tree = preview.get_preview_tree()
2431
annotation = preview_tree.annotate_iter('file-id', 'me:')
2432
self.assertEqual(expected, annotation)
2434
def test_annotate_deleted(self):
2435
tree = self.make_branch_and_tree('tree')
2436
self.build_tree_contents([('tree/file', 'a\n')])
2437
tree.add('file', 'file-id')
2438
tree.commit('a', rev_id='one')
2439
self.build_tree_contents([('tree/file', 'a\nb\n')])
2440
preview = TransformPreview(tree)
2441
self.addCleanup(preview.finalize)
2442
file_trans_id = preview.trans_id_file_id('file-id')
2443
preview.delete_contents(file_trans_id)
2444
preview_tree = preview.get_preview_tree()
2445
annotation = preview_tree.annotate_iter('file-id', 'me:')
2446
self.assertIs(None, annotation)
2448
def test_stored_kind(self):
2449
preview = self.get_empty_preview()
2450
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2451
preview_tree = preview.get_preview_tree()
2452
self.assertEqual('file', preview_tree.stored_kind('file-id'))
2454
def test_is_executable(self):
2455
preview = self.get_empty_preview()
2456
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2457
preview.set_executability(True, preview.trans_id_file_id('file-id'))
2458
preview_tree = preview.get_preview_tree()
2459
self.assertEqual(True, preview_tree.is_executable('file-id'))
2461
def test_get_set_parent_ids(self):
2462
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2463
self.assertEqual([], preview_tree.get_parent_ids())
2464
preview_tree.set_parent_ids(['rev-1'])
2465
self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
2467
def test_plan_file_merge(self):
2468
work_a = self.make_branch_and_tree('wta')
2469
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2470
work_a.add('file', 'file-id')
2471
base_id = work_a.commit('base version')
2472
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2473
preview = TransformPreview(work_a)
2474
self.addCleanup(preview.finalize)
2475
trans_id = preview.trans_id_file_id('file-id')
2476
preview.delete_contents(trans_id)
2477
preview.create_file('b\nc\nd\ne\n', trans_id)
2478
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2479
tree_a = preview.get_preview_tree()
2480
tree_a.set_parent_ids([base_id])
2482
('killed-a', 'a\n'),
2483
('killed-b', 'b\n'),
2484
('unchanged', 'c\n'),
2485
('unchanged', 'd\n'),
2488
], list(tree_a.plan_file_merge('file-id', tree_b)))
2490
def test_plan_file_merge_revision_tree(self):
2491
work_a = self.make_branch_and_tree('wta')
2492
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2493
work_a.add('file', 'file-id')
2494
base_id = work_a.commit('base version')
2495
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2496
preview = TransformPreview(work_a.basis_tree())
2497
self.addCleanup(preview.finalize)
2498
trans_id = preview.trans_id_file_id('file-id')
2499
preview.delete_contents(trans_id)
2500
preview.create_file('b\nc\nd\ne\n', trans_id)
2501
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2502
tree_a = preview.get_preview_tree()
2503
tree_a.set_parent_ids([base_id])
2505
('killed-a', 'a\n'),
2506
('killed-b', 'b\n'),
2507
('unchanged', 'c\n'),
2508
('unchanged', 'd\n'),
2511
], list(tree_a.plan_file_merge('file-id', tree_b)))
2513
def test_walkdirs(self):
2514
preview = self.get_empty_preview()
2515
preview.version_file('tree-root', preview.root)
2516
preview_tree = preview.get_preview_tree()
2517
file_trans_id = preview.new_file('a', preview.root, 'contents',
2519
expected = [(('', 'tree-root'),
2520
[('a', 'a', 'file', None, 'a-id', 'file')])]
2521
self.assertEqual(expected, list(preview_tree.walkdirs()))
2523
def test_extras(self):
2524
work_tree = self.make_branch_and_tree('tree')
2525
self.build_tree(['tree/removed-file', 'tree/existing-file',
2526
'tree/not-removed-file'])
2527
work_tree.add(['removed-file', 'not-removed-file'])
2528
preview = TransformPreview(work_tree)
2529
self.addCleanup(preview.finalize)
2530
preview.new_file('new-file', preview.root, 'contents')
2531
preview.new_file('new-versioned-file', preview.root, 'contents',
2533
tree = preview.get_preview_tree()
2534
preview.unversion_file(preview.trans_id_tree_path('removed-file'))
2535
self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
2538
def test_merge_into_preview(self):
2539
work_tree = self.make_branch_and_tree('tree')
2540
self.build_tree_contents([('tree/file','b\n')])
2541
work_tree.add('file', 'file-id')
2542
work_tree.commit('first commit')
2543
child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
2544
self.build_tree_contents([('child/file','b\nc\n')])
2545
child_tree.commit('child commit')
2546
child_tree.lock_write()
2547
self.addCleanup(child_tree.unlock)
2548
work_tree.lock_write()
2549
self.addCleanup(work_tree.unlock)
2550
preview = TransformPreview(work_tree)
2551
self.addCleanup(preview.finalize)
2552
preview_tree = preview.get_preview_tree()
2553
file_trans_id = preview.trans_id_file_id('file-id')
2554
preview.delete_contents(file_trans_id)
2555
preview.create_file('a\nb\n', file_trans_id)
2556
pb = progress.DummyProgress()
2557
merger = Merger.from_revision_ids(pb, preview_tree,
2558
child_tree.branch.last_revision(),
2559
other_branch=child_tree.branch,
2560
tree_branch=work_tree.branch)
2561
merger.merge_type = Merge3Merger
2562
tt = merger.make_merger().make_preview_transform()
2563
self.addCleanup(tt.finalize)
2564
final_tree = tt.get_preview_tree()
2565
self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
2567
def test_merge_preview_into_workingtree(self):
2568
tree = self.make_branch_and_tree('tree')
2569
tt = TransformPreview(tree)
2570
self.addCleanup(tt.finalize)
2571
tt.new_file('name', tt.root, 'content', 'file-id')
2572
tree2 = self.make_branch_and_tree('tree2')
2573
pb = progress.DummyProgress()
2574
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2575
pb, tree.basis_tree())
2576
merger.merge_type = Merge3Merger
2579
def test_merge_preview_into_workingtree_handles_conflicts(self):
2580
tree = self.make_branch_and_tree('tree')
2581
self.build_tree_contents([('tree/foo', 'bar')])
2582
tree.add('foo', 'foo-id')
2584
tt = TransformPreview(tree)
2585
self.addCleanup(tt.finalize)
2586
trans_id = tt.trans_id_file_id('foo-id')
2587
tt.delete_contents(trans_id)
2588
tt.create_file('baz', trans_id)
2589
tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2590
self.build_tree_contents([('tree2/foo', 'qux')])
2591
pb = progress.DummyProgress()
2592
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2593
pb, tree.basis_tree())
2594
merger.merge_type = Merge3Merger
2597
def test_is_executable(self):
2598
tree = self.make_branch_and_tree('tree')
2599
preview = TransformPreview(tree)
2600
self.addCleanup(preview.finalize)
2601
preview.new_file('foo', preview.root, 'bar', 'baz-id')
2602
preview_tree = preview.get_preview_tree()
2603
self.assertEqual(False, preview_tree.is_executable('baz-id',
2605
self.assertEqual(False, preview_tree.is_executable('baz-id'))
2607
def test_commit_preview_tree(self):
2608
tree = self.make_branch_and_tree('tree')
2609
rev_id = tree.commit('rev1')
2610
tree.branch.lock_write()
2611
self.addCleanup(tree.branch.unlock)
2612
tt = TransformPreview(tree)
2613
tt.new_file('file', tt.root, 'contents', 'file_id')
2614
self.addCleanup(tt.finalize)
2615
preview = tt.get_preview_tree()
2616
preview.set_parent_ids([rev_id])
2617
builder = tree.branch.get_commit_builder([rev_id])
2618
list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
2619
builder.finish_inventory()
2620
rev2_id = builder.commit('rev2')
2621
rev2_tree = tree.branch.repository.revision_tree(rev2_id)
2622
self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
2625
class FakeSerializer(object):
2626
"""Serializer implementation that simply returns the input.
2628
The input is returned in the order used by pack.ContainerPushParser.
2631
def bytes_record(bytes, names):
2635
class TestSerializeTransform(tests.TestCaseWithTransport):
2637
_test_needs_features = [tests.UnicodeFilenameFeature]
2639
def get_preview(self, tree=None):
2641
tree = self.make_branch_and_tree('tree')
2642
tt = TransformPreview(tree)
2643
self.addCleanup(tt.finalize)
2646
def assertSerializesTo(self, expected, tt):
2647
records = list(tt.serialize(FakeSerializer()))
2648
self.assertEqual(expected, records)
2651
def default_attribs():
2656
'_new_executability': {},
2658
'_tree_path_ids': {'': 'new-0'},
2660
'_removed_contents': [],
2661
'_non_present_ids': {},
2664
def make_records(self, attribs, contents):
2666
(((('attribs'),),), bencode.bencode(attribs))]
2667
records.extend([(((n, k),), c) for n, k, c in contents])
2670
def creation_records(self):
2671
attribs = self.default_attribs()
2672
attribs['_id_number'] = 3
2673
attribs['_new_name'] = {
2674
'new-1': u'foo\u1234'.encode('utf-8'), 'new-2': 'qux'}
2675
attribs['_new_id'] = {'new-1': 'baz', 'new-2': 'quxx'}
2676
attribs['_new_parent'] = {'new-1': 'new-0', 'new-2': 'new-0'}
2677
attribs['_new_executability'] = {'new-1': 1}
2679
('new-1', 'file', 'i 1\nbar\n'),
2680
('new-2', 'directory', ''),
2682
return self.make_records(attribs, contents)
2684
def test_serialize_creation(self):
2685
tt = self.get_preview()
2686
tt.new_file(u'foo\u1234', tt.root, 'bar', 'baz', True)
2687
tt.new_directory('qux', tt.root, 'quxx')
2688
self.assertSerializesTo(self.creation_records(), tt)
2690
def test_deserialize_creation(self):
2691
tt = self.get_preview()
2692
tt.deserialize(iter(self.creation_records()))
2693
self.assertEqual(3, tt._id_number)
2694
self.assertEqual({'new-1': u'foo\u1234',
2695
'new-2': 'qux'}, tt._new_name)
2696
self.assertEqual({'new-1': 'baz', 'new-2': 'quxx'}, tt._new_id)
2697
self.assertEqual({'new-1': tt.root, 'new-2': tt.root}, tt._new_parent)
2698
self.assertEqual({'baz': 'new-1', 'quxx': 'new-2'}, tt._r_new_id)
2699
self.assertEqual({'new-1': True}, tt._new_executability)
2700
self.assertEqual({'new-1': 'file',
2701
'new-2': 'directory'}, tt._new_contents)
2702
foo_limbo = open(tt._limbo_name('new-1'), 'rb')
2704
foo_content = foo_limbo.read()
2707
self.assertEqual('bar', foo_content)
2709
def symlink_creation_records(self):
2710
attribs = self.default_attribs()
2711
attribs['_id_number'] = 2
2712
attribs['_new_name'] = {'new-1': u'foo\u1234'.encode('utf-8')}
2713
attribs['_new_parent'] = {'new-1': 'new-0'}
2714
contents = [('new-1', 'symlink', u'bar\u1234'.encode('utf-8'))]
2715
return self.make_records(attribs, contents)
2717
def test_serialize_symlink_creation(self):
2718
self.requireFeature(tests.SymlinkFeature)
2719
tt = self.get_preview()
2720
tt.new_symlink(u'foo\u1234', tt.root, u'bar\u1234')
2721
self.assertSerializesTo(self.symlink_creation_records(), tt)
2723
def test_deserialize_symlink_creation(self):
2724
self.requireFeature(tests.SymlinkFeature)
2725
tt = self.get_preview()
2726
tt.deserialize(iter(self.symlink_creation_records()))
2727
abspath = tt._limbo_name('new-1')
2728
foo_content = osutils.readlink(abspath)
2729
self.assertEqual(u'bar\u1234', foo_content)
2731
def make_destruction_preview(self):
2732
tree = self.make_branch_and_tree('.')
2733
self.build_tree([u'foo\u1234', 'bar'])
2734
tree.add([u'foo\u1234', 'bar'], ['foo-id', 'bar-id'])
2735
return self.get_preview(tree)
2737
def destruction_records(self):
2738
attribs = self.default_attribs()
2739
attribs['_id_number'] = 3
2740
attribs['_removed_id'] = ['new-1']
2741
attribs['_removed_contents'] = ['new-2']
2742
attribs['_tree_path_ids'] = {
2744
u'foo\u1234'.encode('utf-8'): 'new-1',
2747
return self.make_records(attribs, [])
2749
def test_serialize_destruction(self):
2750
tt = self.make_destruction_preview()
2751
foo_trans_id = tt.trans_id_tree_file_id('foo-id')
2752
tt.unversion_file(foo_trans_id)
2753
bar_trans_id = tt.trans_id_tree_file_id('bar-id')
2754
tt.delete_contents(bar_trans_id)
2755
self.assertSerializesTo(self.destruction_records(), tt)
2757
def test_deserialize_destruction(self):
2758
tt = self.make_destruction_preview()
2759
tt.deserialize(iter(self.destruction_records()))
2760
self.assertEqual({u'foo\u1234': 'new-1',
2762
'': tt.root}, tt._tree_path_ids)
2763
self.assertEqual({'new-1': u'foo\u1234',
2765
tt.root: ''}, tt._tree_id_paths)
2766
self.assertEqual(set(['new-1']), tt._removed_id)
2767
self.assertEqual(set(['new-2']), tt._removed_contents)
2769
def missing_records(self):
2770
attribs = self.default_attribs()
2771
attribs['_id_number'] = 2
2772
attribs['_non_present_ids'] = {
2774
return self.make_records(attribs, [])
2776
def test_serialize_missing(self):
2777
tt = self.get_preview()
2778
boo_trans_id = tt.trans_id_file_id('boo')
2779
self.assertSerializesTo(self.missing_records(), tt)
2781
def test_deserialize_missing(self):
2782
tt = self.get_preview()
2783
tt.deserialize(iter(self.missing_records()))
2784
self.assertEqual({'boo': 'new-1'}, tt._non_present_ids)
2786
def make_modification_preview(self):
2787
LINES_ONE = 'aa\nbb\ncc\ndd\n'
2788
LINES_TWO = 'z\nbb\nx\ndd\n'
2789
tree = self.make_branch_and_tree('tree')
2790
self.build_tree_contents([('tree/file', LINES_ONE)])
2791
tree.add('file', 'file-id')
2792
return self.get_preview(tree), LINES_TWO
2794
def modification_records(self):
2795
attribs = self.default_attribs()
2796
attribs['_id_number'] = 2
2797
attribs['_tree_path_ids'] = {
2800
attribs['_removed_contents'] = ['new-1']
2801
contents = [('new-1', 'file',
2802
'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
2803
return self.make_records(attribs, contents)
2805
def test_serialize_modification(self):
2806
tt, LINES = self.make_modification_preview()
2807
trans_id = tt.trans_id_file_id('file-id')
2808
tt.delete_contents(trans_id)
2809
tt.create_file(LINES, trans_id)
2810
self.assertSerializesTo(self.modification_records(), tt)
2812
def test_deserialize_modification(self):
2813
tt, LINES = self.make_modification_preview()
2814
tt.deserialize(iter(self.modification_records()))
2815
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2817
def make_kind_change_preview(self):
2818
LINES = 'a\nb\nc\nd\n'
2819
tree = self.make_branch_and_tree('tree')
2820
self.build_tree(['tree/foo/'])
2821
tree.add('foo', 'foo-id')
2822
return self.get_preview(tree), LINES
2824
def kind_change_records(self):
2825
attribs = self.default_attribs()
2826
attribs['_id_number'] = 2
2827
attribs['_tree_path_ids'] = {
2830
attribs['_removed_contents'] = ['new-1']
2831
contents = [('new-1', 'file',
2832
'i 4\na\nb\nc\nd\n\n')]
2833
return self.make_records(attribs, contents)
2835
def test_serialize_kind_change(self):
2836
tt, LINES = self.make_kind_change_preview()
2837
trans_id = tt.trans_id_file_id('foo-id')
2838
tt.delete_contents(trans_id)
2839
tt.create_file(LINES, trans_id)
2840
self.assertSerializesTo(self.kind_change_records(), tt)
2842
def test_deserialize_kind_change(self):
2843
tt, LINES = self.make_kind_change_preview()
2844
tt.deserialize(iter(self.kind_change_records()))
2845
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2847
def make_add_contents_preview(self):
2848
LINES = 'a\nb\nc\nd\n'
2849
tree = self.make_branch_and_tree('tree')
2850
self.build_tree(['tree/foo'])
2852
os.unlink('tree/foo')
2853
return self.get_preview(tree), LINES
2855
def add_contents_records(self):
2856
attribs = self.default_attribs()
2857
attribs['_id_number'] = 2
2858
attribs['_tree_path_ids'] = {
2861
contents = [('new-1', 'file',
2862
'i 4\na\nb\nc\nd\n\n')]
2863
return self.make_records(attribs, contents)
2865
def test_serialize_add_contents(self):
2866
tt, LINES = self.make_add_contents_preview()
2867
trans_id = tt.trans_id_tree_path('foo')
2868
tt.create_file(LINES, trans_id)
2869
self.assertSerializesTo(self.add_contents_records(), tt)
2871
def test_deserialize_add_contents(self):
2872
tt, LINES = self.make_add_contents_preview()
2873
tt.deserialize(iter(self.add_contents_records()))
2874
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2876
def test_get_parents_lines(self):
2877
LINES_ONE = 'aa\nbb\ncc\ndd\n'
2878
LINES_TWO = 'z\nbb\nx\ndd\n'
2879
tree = self.make_branch_and_tree('tree')
2880
self.build_tree_contents([('tree/file', LINES_ONE)])
2881
tree.add('file', 'file-id')
2882
tt = self.get_preview(tree)
2883
trans_id = tt.trans_id_tree_path('file')
2884
self.assertEqual((['aa\n', 'bb\n', 'cc\n', 'dd\n'],),
2885
tt._get_parents_lines(trans_id))
2887
def test_get_parents_texts(self):
2888
LINES_ONE = 'aa\nbb\ncc\ndd\n'
2889
LINES_TWO = 'z\nbb\nx\ndd\n'
2890
tree = self.make_branch_and_tree('tree')
2891
self.build_tree_contents([('tree/file', LINES_ONE)])
2892
tree.add('file', 'file-id')
2893
tt = self.get_preview(tree)
2894
trans_id = tt.trans_id_tree_path('file')
2895
self.assertEqual((LINES_ONE,),
2896
tt._get_parents_texts(trans_id))