587
820
bar1_abspath = self.wt.abspath('bar')
588
821
self.assertEqual([bar1_abspath], stat_paths)
823
def test_iter_changes(self):
824
self.wt.set_root_id('eert_toor')
825
transform, root = self.get_transform()
826
transform.new_file('old', root, 'blah', 'id-1', True)
828
transform, root = self.get_transform()
830
self.assertEqual([], list(transform.iter_changes()))
831
old = transform.trans_id_tree_file_id('id-1')
832
transform.unversion_file(old)
833
self.assertEqual([('id-1', ('old', None), False, (True, False),
834
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
835
(True, True))], list(transform.iter_changes()))
836
transform.new_directory('new', root, 'id-1')
837
self.assertEqual([('id-1', ('old', 'new'), True, (True, True),
838
('eert_toor', 'eert_toor'), ('old', 'new'),
839
('file', 'directory'),
840
(True, False))], list(transform.iter_changes()))
844
def test_iter_changes_new(self):
845
self.wt.set_root_id('eert_toor')
846
transform, root = self.get_transform()
847
transform.new_file('old', root, 'blah')
849
transform, root = self.get_transform()
851
old = transform.trans_id_tree_path('old')
852
transform.version_file('id-1', old)
853
self.assertEqual([('id-1', (None, 'old'), False, (False, True),
854
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
855
(False, False))], list(transform.iter_changes()))
859
def test_iter_changes_modifications(self):
860
self.wt.set_root_id('eert_toor')
861
transform, root = self.get_transform()
862
transform.new_file('old', root, 'blah', 'id-1')
863
transform.new_file('new', root, 'blah')
864
transform.new_directory('subdir', root, 'subdir-id')
866
transform, root = self.get_transform()
868
old = transform.trans_id_tree_path('old')
869
subdir = transform.trans_id_tree_file_id('subdir-id')
870
new = transform.trans_id_tree_path('new')
871
self.assertEqual([], list(transform.iter_changes()))
874
transform.delete_contents(old)
875
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
876
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', None),
877
(False, False))], list(transform.iter_changes()))
880
transform.create_file('blah', old)
881
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
882
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
883
(False, False))], list(transform.iter_changes()))
884
transform.cancel_deletion(old)
885
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
886
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
887
(False, False))], list(transform.iter_changes()))
888
transform.cancel_creation(old)
890
# move file_id to a different file
891
self.assertEqual([], list(transform.iter_changes()))
892
transform.unversion_file(old)
893
transform.version_file('id-1', new)
894
transform.adjust_path('old', root, new)
895
self.assertEqual([('id-1', ('old', 'old'), True, (True, True),
896
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
897
(False, False))], list(transform.iter_changes()))
898
transform.cancel_versioning(new)
899
transform._removed_id = set()
902
self.assertEqual([], list(transform.iter_changes()))
903
transform.set_executability(True, old)
904
self.assertEqual([('id-1', ('old', 'old'), False, (True, True),
905
('eert_toor', 'eert_toor'), ('old', 'old'), ('file', 'file'),
906
(False, True))], list(transform.iter_changes()))
907
transform.set_executability(None, old)
910
self.assertEqual([], list(transform.iter_changes()))
911
transform.adjust_path('new', root, old)
912
transform._new_parent = {}
913
self.assertEqual([('id-1', ('old', 'new'), False, (True, True),
914
('eert_toor', 'eert_toor'), ('old', 'new'), ('file', 'file'),
915
(False, False))], list(transform.iter_changes()))
916
transform._new_name = {}
919
self.assertEqual([], list(transform.iter_changes()))
920
transform.adjust_path('new', subdir, old)
921
transform._new_name = {}
922
self.assertEqual([('id-1', ('old', 'subdir/old'), False,
923
(True, True), ('eert_toor', 'subdir-id'), ('old', 'old'),
924
('file', 'file'), (False, False))],
925
list(transform.iter_changes()))
926
transform._new_path = {}
931
def test_iter_changes_modified_bleed(self):
932
self.wt.set_root_id('eert_toor')
933
"""Modified flag should not bleed from one change to another"""
934
# unfortunately, we have no guarantee that file1 (which is modified)
935
# will be applied before file2. And if it's applied after file2, it
936
# obviously can't bleed into file2's change output. But for now, it
938
transform, root = self.get_transform()
939
transform.new_file('file1', root, 'blah', 'id-1')
940
transform.new_file('file2', root, 'blah', 'id-2')
942
transform, root = self.get_transform()
944
transform.delete_contents(transform.trans_id_file_id('id-1'))
945
transform.set_executability(True,
946
transform.trans_id_file_id('id-2'))
947
self.assertEqual([('id-1', (u'file1', u'file1'), True, (True, True),
948
('eert_toor', 'eert_toor'), ('file1', u'file1'),
949
('file', None), (False, False)),
950
('id-2', (u'file2', u'file2'), False, (True, True),
951
('eert_toor', 'eert_toor'), ('file2', u'file2'),
952
('file', 'file'), (False, True))],
953
list(transform.iter_changes()))
957
def test_iter_changes_move_missing(self):
958
"""Test moving ids with no files around"""
959
self.wt.set_root_id('toor_eert')
960
# Need two steps because versioning a non-existant file is a conflict.
961
transform, root = self.get_transform()
962
transform.new_directory('floater', root, 'floater-id')
964
transform, root = self.get_transform()
965
transform.delete_contents(transform.trans_id_tree_path('floater'))
967
transform, root = self.get_transform()
968
floater = transform.trans_id_tree_path('floater')
970
transform.adjust_path('flitter', root, floater)
971
self.assertEqual([('floater-id', ('floater', 'flitter'), False,
972
(True, True), ('toor_eert', 'toor_eert'), ('floater', 'flitter'),
973
(None, None), (False, False))], list(transform.iter_changes()))
977
def test_iter_changes_pointless(self):
978
"""Ensure that no-ops are not treated as modifications"""
979
self.wt.set_root_id('eert_toor')
980
transform, root = self.get_transform()
981
transform.new_file('old', root, 'blah', 'id-1')
982
transform.new_directory('subdir', root, 'subdir-id')
984
transform, root = self.get_transform()
986
old = transform.trans_id_tree_path('old')
987
subdir = transform.trans_id_tree_file_id('subdir-id')
988
self.assertEqual([], list(transform.iter_changes()))
989
transform.delete_contents(subdir)
990
transform.create_directory(subdir)
991
transform.set_executability(False, old)
992
transform.unversion_file(old)
993
transform.version_file('id-1', old)
994
transform.adjust_path('old', root, old)
995
self.assertEqual([], list(transform.iter_changes()))
999
def test_rename_count(self):
1000
transform, root = self.get_transform()
1001
transform.new_file('name1', root, 'contents')
1002
self.assertEqual(transform.rename_count, 0)
1004
self.assertEqual(transform.rename_count, 1)
1005
transform2, root = self.get_transform()
1006
transform2.adjust_path('name2', root,
1007
transform2.trans_id_tree_path('name1'))
1008
self.assertEqual(transform2.rename_count, 0)
1010
self.assertEqual(transform2.rename_count, 2)
1012
def test_change_parent(self):
1013
"""Ensure that after we change a parent, the results are still right.
1015
Renames and parent changes on pending transforms can happen as part
1016
of conflict resolution, and are explicitly permitted by the
1019
This test ensures they work correctly with the rename-avoidance
1022
transform, root = self.get_transform()
1023
parent1 = transform.new_directory('parent1', root)
1024
child1 = transform.new_file('child1', parent1, 'contents')
1025
parent2 = transform.new_directory('parent2', root)
1026
transform.adjust_path('child1', parent2, child1)
1028
self.failIfExists(self.wt.abspath('parent1/child1'))
1029
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1030
# rename limbo/new-1 => parent1, rename limbo/new-3 => parent2
1031
# no rename for child1 (counting only renames during apply)
1032
self.failUnlessEqual(2, transform.rename_count)
1034
def test_cancel_parent(self):
1035
"""Cancelling a parent doesn't cause deletion of a non-empty directory
1037
This is like the test_change_parent, except that we cancel the parent
1038
before adjusting the path. The transform must detect that the
1039
directory is non-empty, and move children to safe locations.
1041
transform, root = self.get_transform()
1042
parent1 = transform.new_directory('parent1', root)
1043
child1 = transform.new_file('child1', parent1, 'contents')
1044
child2 = transform.new_file('child2', parent1, 'contents')
1046
transform.cancel_creation(parent1)
1048
self.fail('Failed to move child1 before deleting parent1')
1049
transform.cancel_creation(child2)
1050
transform.create_directory(parent1)
1052
transform.cancel_creation(parent1)
1053
# If the transform incorrectly believes that child2 is still in
1054
# parent1's limbo directory, it will try to rename it and fail
1055
# because was already moved by the first cancel_creation.
1057
self.fail('Transform still thinks child2 is a child of parent1')
1058
parent2 = transform.new_directory('parent2', root)
1059
transform.adjust_path('child1', parent2, child1)
1061
self.failIfExists(self.wt.abspath('parent1'))
1062
self.failUnlessExists(self.wt.abspath('parent2/child1'))
1063
# rename limbo/new-3 => parent2, rename limbo/new-2 => child1
1064
self.failUnlessEqual(2, transform.rename_count)
1066
def test_adjust_and_cancel(self):
1067
"""Make sure adjust_path keeps track of limbo children properly"""
1068
transform, root = self.get_transform()
1069
parent1 = transform.new_directory('parent1', root)
1070
child1 = transform.new_file('child1', parent1, 'contents')
1071
parent2 = transform.new_directory('parent2', root)
1072
transform.adjust_path('child1', parent2, child1)
1073
transform.cancel_creation(child1)
1075
transform.cancel_creation(parent1)
1076
# if the transform thinks child1 is still in parent1's limbo
1077
# directory, it will attempt to move it and fail.
1079
self.fail('Transform still thinks child1 is a child of parent1')
1080
transform.finalize()
1082
def test_noname_contents(self):
1083
"""TreeTransform should permit deferring naming files."""
1084
transform, root = self.get_transform()
1085
parent = transform.trans_id_file_id('parent-id')
1087
transform.create_directory(parent)
1089
self.fail("Can't handle contents with no name")
1090
transform.finalize()
1092
def test_noname_contents_nested(self):
1093
"""TreeTransform should permit deferring naming files."""
1094
transform, root = self.get_transform()
1095
parent = transform.trans_id_file_id('parent-id')
1097
transform.create_directory(parent)
1099
self.fail("Can't handle contents with no name")
1100
child = transform.new_directory('child', parent)
1101
transform.adjust_path('parent', root, parent)
1103
self.failUnlessExists(self.wt.abspath('parent/child'))
1104
self.assertEqual(1, transform.rename_count)
1106
def test_reuse_name(self):
1107
"""Avoid reusing the same limbo name for different files"""
1108
transform, root = self.get_transform()
1109
parent = transform.new_directory('parent', root)
1110
child1 = transform.new_directory('child', parent)
1112
child2 = transform.new_directory('child', parent)
1114
self.fail('Tranform tried to use the same limbo name twice')
1115
transform.adjust_path('child2', parent, child2)
1117
# limbo/new-1 => parent, limbo/new-3 => parent/child2
1118
# child2 is put into top-level limbo because child1 has already
1119
# claimed the direct limbo path when child2 is created. There is no
1120
# advantage in renaming files once they're in top-level limbo, except
1122
self.assertEqual(2, transform.rename_count)
1124
def test_reuse_when_first_moved(self):
1125
"""Don't avoid direct paths when it is safe to use them"""
1126
transform, root = self.get_transform()
1127
parent = transform.new_directory('parent', root)
1128
child1 = transform.new_directory('child', parent)
1129
transform.adjust_path('child1', parent, child1)
1130
child2 = transform.new_directory('child', parent)
1132
# limbo/new-1 => parent
1133
self.assertEqual(1, transform.rename_count)
1135
def test_reuse_after_cancel(self):
1136
"""Don't avoid direct paths when it is safe to use them"""
1137
transform, root = self.get_transform()
1138
parent2 = transform.new_directory('parent2', root)
1139
child1 = transform.new_directory('child1', parent2)
1140
transform.cancel_creation(parent2)
1141
transform.create_directory(parent2)
1142
child2 = transform.new_directory('child1', parent2)
1143
transform.adjust_path('child2', parent2, child1)
1145
# limbo/new-1 => parent2, limbo/new-2 => parent2/child1
1146
self.assertEqual(2, transform.rename_count)
1148
def test_finalize_order(self):
1149
"""Finalize must be done in child-to-parent order"""
1150
transform, root = self.get_transform()
1151
parent = transform.new_directory('parent', root)
1152
child = transform.new_directory('child', parent)
1154
transform.finalize()
1156
self.fail('Tried to remove parent before child1')
1158
def test_cancel_with_cancelled_child_should_succeed(self):
1159
transform, root = self.get_transform()
1160
parent = transform.new_directory('parent', root)
1161
child = transform.new_directory('child', parent)
1162
transform.cancel_creation(child)
1163
transform.cancel_creation(parent)
1164
transform.finalize()
1166
def test_rollback_on_directory_clash(self):
1168
wt = self.make_branch_and_tree('.')
1169
tt = TreeTransform(wt) # TreeTransform obtains write lock
1171
foo = tt.new_directory('foo', tt.root)
1172
tt.new_file('bar', foo, 'foobar')
1173
baz = tt.new_directory('baz', tt.root)
1174
tt.new_file('qux', baz, 'quux')
1175
# Ask for a rename 'foo' -> 'baz'
1176
tt.adjust_path('baz', tt.root, foo)
1177
# Lie to tt that we've already resolved all conflicts.
1178
tt.apply(no_conflicts=True)
1182
# The rename will fail because the target directory is not empty (but
1183
# raises FileExists anyway).
1184
err = self.assertRaises(errors.FileExists, tt_helper)
1185
self.assertContainsRe(str(err),
1186
"^File exists: .+/baz")
1188
def test_two_directories_clash(self):
1190
wt = self.make_branch_and_tree('.')
1191
tt = TreeTransform(wt) # TreeTransform obtains write lock
1193
foo_1 = tt.new_directory('foo', tt.root)
1194
tt.new_directory('bar', foo_1)
1195
# Adding the same directory with a different content
1196
foo_2 = tt.new_directory('foo', tt.root)
1197
tt.new_directory('baz', foo_2)
1198
# Lie to tt that we've already resolved all conflicts.
1199
tt.apply(no_conflicts=True)
1203
err = self.assertRaises(errors.FileExists, tt_helper)
1204
self.assertContainsRe(str(err),
1205
"^File exists: .+/foo")
1207
def test_two_directories_clash_finalize(self):
1209
wt = self.make_branch_and_tree('.')
1210
tt = TreeTransform(wt) # TreeTransform obtains write lock
1212
foo_1 = tt.new_directory('foo', tt.root)
1213
tt.new_directory('bar', foo_1)
1214
# Adding the same directory with a different content
1215
foo_2 = tt.new_directory('foo', tt.root)
1216
tt.new_directory('baz', foo_2)
1217
# Lie to tt that we've already resolved all conflicts.
1218
tt.apply(no_conflicts=True)
1222
err = self.assertRaises(errors.FileExists, tt_helper)
1223
self.assertContainsRe(str(err),
1224
"^File exists: .+/foo")
1226
def test_file_to_directory(self):
1227
wt = self.make_branch_and_tree('.')
1228
self.build_tree(['foo'])
1231
tt = TreeTransform(wt)
1232
self.addCleanup(tt.finalize)
1233
foo_trans_id = tt.trans_id_tree_path("foo")
1234
tt.delete_contents(foo_trans_id)
1235
tt.create_directory(foo_trans_id)
1236
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1237
tt.create_file(["aa\n"], bar_trans_id)
1238
tt.version_file("bar-1", bar_trans_id)
1240
self.failUnlessExists("foo/bar")
1243
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1248
changes = wt.changes_from(wt.basis_tree())
1249
self.assertFalse(changes.has_changed(), changes)
1251
def test_file_to_symlink(self):
1252
self.requireFeature(SymlinkFeature)
1253
wt = self.make_branch_and_tree('.')
1254
self.build_tree(['foo'])
1257
tt = TreeTransform(wt)
1258
self.addCleanup(tt.finalize)
1259
foo_trans_id = tt.trans_id_tree_path("foo")
1260
tt.delete_contents(foo_trans_id)
1261
tt.create_symlink("bar", foo_trans_id)
1263
self.failUnlessExists("foo")
1265
self.addCleanup(wt.unlock)
1266
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1269
def test_dir_to_file(self):
1270
wt = self.make_branch_and_tree('.')
1271
self.build_tree(['foo/', 'foo/bar'])
1272
wt.add(['foo', 'foo/bar'])
1274
tt = TreeTransform(wt)
1275
self.addCleanup(tt.finalize)
1276
foo_trans_id = tt.trans_id_tree_path("foo")
1277
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1278
tt.delete_contents(foo_trans_id)
1279
tt.delete_versioned(bar_trans_id)
1280
tt.create_file(["aa\n"], foo_trans_id)
1282
self.failUnlessExists("foo")
1284
self.addCleanup(wt.unlock)
1285
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1288
def test_dir_to_hardlink(self):
1289
self.requireFeature(HardlinkFeature)
1290
wt = self.make_branch_and_tree('.')
1291
self.build_tree(['foo/', 'foo/bar'])
1292
wt.add(['foo', 'foo/bar'])
1294
tt = TreeTransform(wt)
1295
self.addCleanup(tt.finalize)
1296
foo_trans_id = tt.trans_id_tree_path("foo")
1297
bar_trans_id = tt.trans_id_tree_path("foo/bar")
1298
tt.delete_contents(foo_trans_id)
1299
tt.delete_versioned(bar_trans_id)
1300
self.build_tree(['baz'])
1301
tt.create_hardlink("baz", foo_trans_id)
1303
self.failUnlessExists("foo")
1304
self.failUnlessExists("baz")
1306
self.addCleanup(wt.unlock)
1307
self.assertEqual(wt.inventory.get_file_kind(wt.path2id("foo")),
1310
def test_no_final_path(self):
1311
transform, root = self.get_transform()
1312
trans_id = transform.trans_id_file_id('foo')
1313
transform.create_file('bar', trans_id)
1314
transform.cancel_creation(trans_id)
1317
def test_create_from_tree(self):
1318
tree1 = self.make_branch_and_tree('tree1')
1319
self.build_tree_contents([('tree1/foo/',), ('tree1/bar', 'baz')])
1320
tree1.add(['foo', 'bar'], ['foo-id', 'bar-id'])
1321
tree2 = self.make_branch_and_tree('tree2')
1322
tt = TreeTransform(tree2)
1323
foo_trans_id = tt.create_path('foo', tt.root)
1324
create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1325
bar_trans_id = tt.create_path('bar', tt.root)
1326
create_from_tree(tt, bar_trans_id, tree1, 'bar-id')
1328
self.assertEqual('directory', osutils.file_kind('tree2/foo'))
1329
self.assertFileEqual('baz', 'tree2/bar')
1331
def test_create_from_tree_bytes(self):
1332
"""Provided lines are used instead of tree content."""
1333
tree1 = self.make_branch_and_tree('tree1')
1334
self.build_tree_contents([('tree1/foo', 'bar'),])
1335
tree1.add('foo', 'foo-id')
1336
tree2 = self.make_branch_and_tree('tree2')
1337
tt = TreeTransform(tree2)
1338
foo_trans_id = tt.create_path('foo', tt.root)
1339
create_from_tree(tt, foo_trans_id, tree1, 'foo-id', bytes='qux')
1341
self.assertFileEqual('qux', 'tree2/foo')
1343
def test_create_from_tree_symlink(self):
1344
self.requireFeature(SymlinkFeature)
1345
tree1 = self.make_branch_and_tree('tree1')
1346
os.symlink('bar', 'tree1/foo')
1347
tree1.add('foo', 'foo-id')
1348
tt = TreeTransform(self.make_branch_and_tree('tree2'))
1349
foo_trans_id = tt.create_path('foo', tt.root)
1350
create_from_tree(tt, foo_trans_id, tree1, 'foo-id')
1352
self.assertEqual('bar', os.readlink('tree2/foo'))
591
1355
class TransformGroup(object):
592
1357
def __init__(self, dirname, root_id):
593
1358
self.name = dirname
594
1359
os.mkdir(dirname)
944
1885
self.assertEqual(name, 'name.~1~')
945
1886
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
946
1887
self.assertEqual(name, 'name.~4~')
1890
class TestFileMover(tests.TestCaseWithTransport):
1892
def test_file_mover(self):
1893
self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
1894
mover = _FileMover()
1895
mover.rename('a', 'q')
1896
self.failUnlessExists('q')
1897
self.failIfExists('a')
1898
self.failUnlessExists('q/b')
1899
self.failUnlessExists('c')
1900
self.failUnlessExists('c/d')
1902
def test_pre_delete_rollback(self):
1903
self.build_tree(['a/'])
1904
mover = _FileMover()
1905
mover.pre_delete('a', 'q')
1906
self.failUnlessExists('q')
1907
self.failIfExists('a')
1909
self.failIfExists('q')
1910
self.failUnlessExists('a')
1912
def test_apply_deletions(self):
1913
self.build_tree(['a/', 'b/'])
1914
mover = _FileMover()
1915
mover.pre_delete('a', 'q')
1916
mover.pre_delete('b', 'r')
1917
self.failUnlessExists('q')
1918
self.failUnlessExists('r')
1919
self.failIfExists('a')
1920
self.failIfExists('b')
1921
mover.apply_deletions()
1922
self.failIfExists('q')
1923
self.failIfExists('r')
1924
self.failIfExists('a')
1925
self.failIfExists('b')
1927
def test_file_mover_rollback(self):
1928
self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
1929
mover = _FileMover()
1930
mover.rename('c/d', 'c/f')
1931
mover.rename('c/e', 'c/d')
1933
mover.rename('a', 'c')
1934
except errors.FileExists, e:
1936
self.failUnlessExists('a')
1937
self.failUnlessExists('c/d')
1940
class Bogus(Exception):
1944
class TestTransformRollback(tests.TestCaseWithTransport):
1946
class ExceptionFileMover(_FileMover):
1948
def __init__(self, bad_source=None, bad_target=None):
1949
_FileMover.__init__(self)
1950
self.bad_source = bad_source
1951
self.bad_target = bad_target
1953
def rename(self, source, target):
1954
if (self.bad_source is not None and
1955
source.endswith(self.bad_source)):
1957
elif (self.bad_target is not None and
1958
target.endswith(self.bad_target)):
1961
_FileMover.rename(self, source, target)
1963
def test_rollback_rename(self):
1964
tree = self.make_branch_and_tree('.')
1965
self.build_tree(['a/', 'a/b'])
1966
tt = TreeTransform(tree)
1967
self.addCleanup(tt.finalize)
1968
a_id = tt.trans_id_tree_path('a')
1969
tt.adjust_path('c', tt.root, a_id)
1970
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1971
self.assertRaises(Bogus, tt.apply,
1972
_mover=self.ExceptionFileMover(bad_source='a'))
1973
self.failUnlessExists('a')
1974
self.failUnlessExists('a/b')
1976
self.failUnlessExists('c')
1977
self.failUnlessExists('c/d')
1979
def test_rollback_rename_into_place(self):
1980
tree = self.make_branch_and_tree('.')
1981
self.build_tree(['a/', 'a/b'])
1982
tt = TreeTransform(tree)
1983
self.addCleanup(tt.finalize)
1984
a_id = tt.trans_id_tree_path('a')
1985
tt.adjust_path('c', tt.root, a_id)
1986
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
1987
self.assertRaises(Bogus, tt.apply,
1988
_mover=self.ExceptionFileMover(bad_target='c/d'))
1989
self.failUnlessExists('a')
1990
self.failUnlessExists('a/b')
1992
self.failUnlessExists('c')
1993
self.failUnlessExists('c/d')
1995
def test_rollback_deletion(self):
1996
tree = self.make_branch_and_tree('.')
1997
self.build_tree(['a/', 'a/b'])
1998
tt = TreeTransform(tree)
1999
self.addCleanup(tt.finalize)
2000
a_id = tt.trans_id_tree_path('a')
2001
tt.delete_contents(a_id)
2002
tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
2003
self.assertRaises(Bogus, tt.apply,
2004
_mover=self.ExceptionFileMover(bad_target='d'))
2005
self.failUnlessExists('a')
2006
self.failUnlessExists('a/b')
2008
def test_resolve_no_parent(self):
2009
wt = self.make_branch_and_tree('.')
2010
tt = TreeTransform(wt)
2011
self.addCleanup(tt.finalize)
2012
parent = tt.trans_id_file_id('parent-id')
2013
tt.new_file('file', parent, 'Contents')
2014
resolve_conflicts(tt)
2017
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
2018
('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
2020
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
2021
('', ''), ('directory', 'directory'), (False, None))
2024
class TestTransformPreview(tests.TestCaseWithTransport):
2026
def create_tree(self):
2027
tree = self.make_branch_and_tree('.')
2028
self.build_tree_contents([('a', 'content 1')])
2029
tree.add('a', 'a-id')
2030
tree.commit('rev1', rev_id='rev1')
2031
return tree.branch.repository.revision_tree('rev1')
2033
def get_empty_preview(self):
2034
repository = self.make_repository('repo')
2035
tree = repository.revision_tree(_mod_revision.NULL_REVISION)
2036
preview = TransformPreview(tree)
2037
self.addCleanup(preview.finalize)
2040
def test_transform_preview(self):
2041
revision_tree = self.create_tree()
2042
preview = TransformPreview(revision_tree)
2043
self.addCleanup(preview.finalize)
2045
def test_transform_preview_tree(self):
2046
revision_tree = self.create_tree()
2047
preview = TransformPreview(revision_tree)
2048
self.addCleanup(preview.finalize)
2049
preview.get_preview_tree()
2051
def test_transform_new_file(self):
2052
revision_tree = self.create_tree()
2053
preview = TransformPreview(revision_tree)
2054
self.addCleanup(preview.finalize)
2055
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2056
preview_tree = preview.get_preview_tree()
2057
self.assertEqual(preview_tree.kind('file2-id'), 'file')
2059
preview_tree.get_file('file2-id').read(), 'content B\n')
2061
def test_diff_preview_tree(self):
2062
revision_tree = self.create_tree()
2063
preview = TransformPreview(revision_tree)
2064
self.addCleanup(preview.finalize)
2065
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2066
preview_tree = preview.get_preview_tree()
2068
show_diff_trees(revision_tree, preview_tree, out)
2069
lines = out.getvalue().splitlines()
2070
self.assertEqual(lines[0], "=== added file 'file2'")
2071
# 3 lines of diff administrivia
2072
self.assertEqual(lines[4], "+content B")
2074
def test_transform_conflicts(self):
2075
revision_tree = self.create_tree()
2076
preview = TransformPreview(revision_tree)
2077
self.addCleanup(preview.finalize)
2078
preview.new_file('a', preview.root, 'content 2')
2079
resolve_conflicts(preview)
2080
trans_id = preview.trans_id_file_id('a-id')
2081
self.assertEqual('a.moved', preview.final_name(trans_id))
2083
def get_tree_and_preview_tree(self):
2084
revision_tree = self.create_tree()
2085
preview = TransformPreview(revision_tree)
2086
self.addCleanup(preview.finalize)
2087
a_trans_id = preview.trans_id_file_id('a-id')
2088
preview.delete_contents(a_trans_id)
2089
preview.create_file('b content', a_trans_id)
2090
preview_tree = preview.get_preview_tree()
2091
return revision_tree, preview_tree
2093
def test_iter_changes(self):
2094
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2095
root = revision_tree.inventory.root.file_id
2096
self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
2097
(root, root), ('a', 'a'), ('file', 'file'),
2099
list(preview_tree.iter_changes(revision_tree)))
2101
def test_include_unchanged_succeeds(self):
2102
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2103
changes = preview_tree.iter_changes(revision_tree,
2104
include_unchanged=True)
2105
root = revision_tree.inventory.root.file_id
2107
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2109
def test_specific_files(self):
2110
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2111
changes = preview_tree.iter_changes(revision_tree,
2112
specific_files=[''])
2113
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2115
def test_want_unversioned(self):
2116
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2117
changes = preview_tree.iter_changes(revision_tree,
2118
want_unversioned=True)
2119
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2121
def test_ignore_extra_trees_no_specific_files(self):
2122
# extra_trees is harmless without specific_files, so we'll silently
2123
# accept it, even though we won't use it.
2124
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2125
preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
2127
def test_ignore_require_versioned_no_specific_files(self):
2128
# require_versioned is meaningless without specific_files.
2129
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2130
preview_tree.iter_changes(revision_tree, require_versioned=False)
2132
def test_ignore_pb(self):
2133
# pb could be supported, but TT.iter_changes doesn't support it.
2134
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2135
preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
2137
def test_kind(self):
2138
revision_tree = self.create_tree()
2139
preview = TransformPreview(revision_tree)
2140
self.addCleanup(preview.finalize)
2141
preview.new_file('file', preview.root, 'contents', 'file-id')
2142
preview.new_directory('directory', preview.root, 'dir-id')
2143
preview_tree = preview.get_preview_tree()
2144
self.assertEqual('file', preview_tree.kind('file-id'))
2145
self.assertEqual('directory', preview_tree.kind('dir-id'))
2147
def test_get_file_mtime(self):
2148
preview = self.get_empty_preview()
2149
file_trans_id = preview.new_file('file', preview.root, 'contents',
2151
limbo_path = preview._limbo_name(file_trans_id)
2152
preview_tree = preview.get_preview_tree()
2153
self.assertEqual(os.stat(limbo_path).st_mtime,
2154
preview_tree.get_file_mtime('file-id'))
2156
def test_get_file(self):
2157
preview = self.get_empty_preview()
2158
preview.new_file('file', preview.root, 'contents', 'file-id')
2159
preview_tree = preview.get_preview_tree()
2160
tree_file = preview_tree.get_file('file-id')
2162
self.assertEqual('contents', tree_file.read())
2166
def test_get_symlink_target(self):
2167
self.requireFeature(SymlinkFeature)
2168
preview = self.get_empty_preview()
2169
preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
2170
preview_tree = preview.get_preview_tree()
2171
self.assertEqual('target',
2172
preview_tree.get_symlink_target('symlink-id'))
2174
def test_all_file_ids(self):
2175
tree = self.make_branch_and_tree('tree')
2176
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
2177
tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
2178
preview = TransformPreview(tree)
2179
self.addCleanup(preview.finalize)
2180
preview.unversion_file(preview.trans_id_file_id('b-id'))
2181
c_trans_id = preview.trans_id_file_id('c-id')
2182
preview.unversion_file(c_trans_id)
2183
preview.version_file('c-id', c_trans_id)
2184
preview_tree = preview.get_preview_tree()
2185
self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
2186
preview_tree.all_file_ids())
2188
def test_path2id_deleted_unchanged(self):
2189
tree = self.make_branch_and_tree('tree')
2190
self.build_tree(['tree/unchanged', 'tree/deleted'])
2191
tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
2192
preview = TransformPreview(tree)
2193
self.addCleanup(preview.finalize)
2194
preview.unversion_file(preview.trans_id_file_id('deleted-id'))
2195
preview_tree = preview.get_preview_tree()
2196
self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
2197
self.assertIs(None, preview_tree.path2id('deleted'))
2199
def test_path2id_created(self):
2200
tree = self.make_branch_and_tree('tree')
2201
self.build_tree(['tree/unchanged'])
2202
tree.add(['unchanged'], ['unchanged-id'])
2203
preview = TransformPreview(tree)
2204
self.addCleanup(preview.finalize)
2205
preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
2206
'contents', 'new-id')
2207
preview_tree = preview.get_preview_tree()
2208
self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
2210
def test_path2id_moved(self):
2211
tree = self.make_branch_and_tree('tree')
2212
self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
2213
tree.add(['old_parent', 'old_parent/child'],
2214
['old_parent-id', 'child-id'])
2215
preview = TransformPreview(tree)
2216
self.addCleanup(preview.finalize)
2217
new_parent = preview.new_directory('new_parent', preview.root,
2219
preview.adjust_path('child', new_parent,
2220
preview.trans_id_file_id('child-id'))
2221
preview_tree = preview.get_preview_tree()
2222
self.assertIs(None, preview_tree.path2id('old_parent/child'))
2223
self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
2225
def test_path2id_renamed_parent(self):
2226
tree = self.make_branch_and_tree('tree')
2227
self.build_tree(['tree/old_name/', 'tree/old_name/child'])
2228
tree.add(['old_name', 'old_name/child'],
2229
['parent-id', 'child-id'])
2230
preview = TransformPreview(tree)
2231
self.addCleanup(preview.finalize)
2232
preview.adjust_path('new_name', preview.root,
2233
preview.trans_id_file_id('parent-id'))
2234
preview_tree = preview.get_preview_tree()
2235
self.assertIs(None, preview_tree.path2id('old_name/child'))
2236
self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
2238
def assertMatchingIterEntries(self, tt, specific_file_ids=None):
2239
preview_tree = tt.get_preview_tree()
2240
preview_result = list(preview_tree.iter_entries_by_dir(
2244
actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
2245
self.assertEqual(actual_result, preview_result)
2247
def test_iter_entries_by_dir_new(self):
2248
tree = self.make_branch_and_tree('tree')
2249
tt = TreeTransform(tree)
2250
tt.new_file('new', tt.root, 'contents', 'new-id')
2251
self.assertMatchingIterEntries(tt)
2253
def test_iter_entries_by_dir_deleted(self):
2254
tree = self.make_branch_and_tree('tree')
2255
self.build_tree(['tree/deleted'])
2256
tree.add('deleted', 'deleted-id')
2257
tt = TreeTransform(tree)
2258
tt.delete_contents(tt.trans_id_file_id('deleted-id'))
2259
self.assertMatchingIterEntries(tt)
2261
def test_iter_entries_by_dir_unversioned(self):
2262
tree = self.make_branch_and_tree('tree')
2263
self.build_tree(['tree/removed'])
2264
tree.add('removed', 'removed-id')
2265
tt = TreeTransform(tree)
2266
tt.unversion_file(tt.trans_id_file_id('removed-id'))
2267
self.assertMatchingIterEntries(tt)
2269
def test_iter_entries_by_dir_moved(self):
2270
tree = self.make_branch_and_tree('tree')
2271
self.build_tree(['tree/moved', 'tree/new_parent/'])
2272
tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
2273
tt = TreeTransform(tree)
2274
tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
2275
tt.trans_id_file_id('moved-id'))
2276
self.assertMatchingIterEntries(tt)
2278
def test_iter_entries_by_dir_specific_file_ids(self):
2279
tree = self.make_branch_and_tree('tree')
2280
tree.set_root_id('tree-root-id')
2281
self.build_tree(['tree/parent/', 'tree/parent/child'])
2282
tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
2283
tt = TreeTransform(tree)
2284
self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
2286
def test_symlink_content_summary(self):
2287
self.requireFeature(SymlinkFeature)
2288
preview = self.get_empty_preview()
2289
preview.new_symlink('path', preview.root, 'target', 'path-id')
2290
summary = preview.get_preview_tree().path_content_summary('path')
2291
self.assertEqual(('symlink', None, None, 'target'), summary)
2293
def test_missing_content_summary(self):
2294
preview = self.get_empty_preview()
2295
summary = preview.get_preview_tree().path_content_summary('path')
2296
self.assertEqual(('missing', None, None, None), summary)
2298
def test_deleted_content_summary(self):
2299
tree = self.make_branch_and_tree('tree')
2300
self.build_tree(['tree/path/'])
2302
preview = TransformPreview(tree)
2303
self.addCleanup(preview.finalize)
2304
preview.delete_contents(preview.trans_id_tree_path('path'))
2305
summary = preview.get_preview_tree().path_content_summary('path')
2306
self.assertEqual(('missing', None, None, None), summary)
2308
def test_file_content_summary_executable(self):
2309
if not osutils.supports_executable():
2310
raise TestNotApplicable()
2311
preview = self.get_empty_preview()
2312
path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2313
preview.set_executability(True, path_id)
2314
summary = preview.get_preview_tree().path_content_summary('path')
2315
self.assertEqual(4, len(summary))
2316
self.assertEqual('file', summary[0])
2317
# size must be known
2318
self.assertEqual(len('contents'), summary[1])
2320
self.assertEqual(True, summary[2])
2321
# will not have hash (not cheap to determine)
2322
self.assertIs(None, summary[3])
2324
def test_change_executability(self):
2325
if not osutils.supports_executable():
2326
raise TestNotApplicable()
2327
tree = self.make_branch_and_tree('tree')
2328
self.build_tree(['tree/path'])
2330
preview = TransformPreview(tree)
2331
self.addCleanup(preview.finalize)
2332
path_id = preview.trans_id_tree_path('path')
2333
preview.set_executability(True, path_id)
2334
summary = preview.get_preview_tree().path_content_summary('path')
2335
self.assertEqual(True, summary[2])
2337
def test_file_content_summary_non_exec(self):
2338
preview = self.get_empty_preview()
2339
preview.new_file('path', preview.root, 'contents', 'path-id')
2340
summary = preview.get_preview_tree().path_content_summary('path')
2341
self.assertEqual(4, len(summary))
2342
self.assertEqual('file', summary[0])
2343
# size must be known
2344
self.assertEqual(len('contents'), summary[1])
2346
if osutils.supports_executable():
2347
self.assertEqual(False, summary[2])
2349
self.assertEqual(None, summary[2])
2350
# will not have hash (not cheap to determine)
2351
self.assertIs(None, summary[3])
2353
def test_dir_content_summary(self):
2354
preview = self.get_empty_preview()
2355
preview.new_directory('path', preview.root, 'path-id')
2356
summary = preview.get_preview_tree().path_content_summary('path')
2357
self.assertEqual(('directory', None, None, None), summary)
2359
def test_tree_content_summary(self):
2360
preview = self.get_empty_preview()
2361
path = preview.new_directory('path', preview.root, 'path-id')
2362
preview.set_tree_reference('rev-1', path)
2363
summary = preview.get_preview_tree().path_content_summary('path')
2364
self.assertEqual(4, len(summary))
2365
self.assertEqual('tree-reference', summary[0])
2367
def test_annotate(self):
2368
tree = self.make_branch_and_tree('tree')
2369
self.build_tree_contents([('tree/file', 'a\n')])
2370
tree.add('file', 'file-id')
2371
tree.commit('a', rev_id='one')
2372
self.build_tree_contents([('tree/file', 'a\nb\n')])
2373
preview = TransformPreview(tree)
2374
self.addCleanup(preview.finalize)
2375
file_trans_id = preview.trans_id_file_id('file-id')
2376
preview.delete_contents(file_trans_id)
2377
preview.create_file('a\nb\nc\n', file_trans_id)
2378
preview_tree = preview.get_preview_tree()
2384
annotation = preview_tree.annotate_iter('file-id', 'me:')
2385
self.assertEqual(expected, annotation)
2387
def test_annotate_missing(self):
2388
preview = self.get_empty_preview()
2389
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2390
preview_tree = preview.get_preview_tree()
2396
annotation = preview_tree.annotate_iter('file-id', 'me:')
2397
self.assertEqual(expected, annotation)
2399
def test_annotate_rename(self):
2400
tree = self.make_branch_and_tree('tree')
2401
self.build_tree_contents([('tree/file', 'a\n')])
2402
tree.add('file', 'file-id')
2403
tree.commit('a', rev_id='one')
2404
preview = TransformPreview(tree)
2405
self.addCleanup(preview.finalize)
2406
file_trans_id = preview.trans_id_file_id('file-id')
2407
preview.adjust_path('newname', preview.root, file_trans_id)
2408
preview_tree = preview.get_preview_tree()
2412
annotation = preview_tree.annotate_iter('file-id', 'me:')
2413
self.assertEqual(expected, annotation)
2415
def test_annotate_deleted(self):
2416
tree = self.make_branch_and_tree('tree')
2417
self.build_tree_contents([('tree/file', 'a\n')])
2418
tree.add('file', 'file-id')
2419
tree.commit('a', rev_id='one')
2420
self.build_tree_contents([('tree/file', 'a\nb\n')])
2421
preview = TransformPreview(tree)
2422
self.addCleanup(preview.finalize)
2423
file_trans_id = preview.trans_id_file_id('file-id')
2424
preview.delete_contents(file_trans_id)
2425
preview_tree = preview.get_preview_tree()
2426
annotation = preview_tree.annotate_iter('file-id', 'me:')
2427
self.assertIs(None, annotation)
2429
def test_stored_kind(self):
2430
preview = self.get_empty_preview()
2431
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2432
preview_tree = preview.get_preview_tree()
2433
self.assertEqual('file', preview_tree.stored_kind('file-id'))
2435
def test_is_executable(self):
2436
preview = self.get_empty_preview()
2437
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2438
preview.set_executability(True, preview.trans_id_file_id('file-id'))
2439
preview_tree = preview.get_preview_tree()
2440
self.assertEqual(True, preview_tree.is_executable('file-id'))
2442
def test_get_set_parent_ids(self):
2443
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2444
self.assertEqual([], preview_tree.get_parent_ids())
2445
preview_tree.set_parent_ids(['rev-1'])
2446
self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
2448
def test_plan_file_merge(self):
2449
work_a = self.make_branch_and_tree('wta')
2450
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2451
work_a.add('file', 'file-id')
2452
base_id = work_a.commit('base version')
2453
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2454
preview = TransformPreview(work_a)
2455
self.addCleanup(preview.finalize)
2456
trans_id = preview.trans_id_file_id('file-id')
2457
preview.delete_contents(trans_id)
2458
preview.create_file('b\nc\nd\ne\n', trans_id)
2459
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2460
tree_a = preview.get_preview_tree()
2461
tree_a.set_parent_ids([base_id])
2463
('killed-a', 'a\n'),
2464
('killed-b', 'b\n'),
2465
('unchanged', 'c\n'),
2466
('unchanged', 'd\n'),
2469
], list(tree_a.plan_file_merge('file-id', tree_b)))
2471
def test_plan_file_merge_revision_tree(self):
2472
work_a = self.make_branch_and_tree('wta')
2473
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2474
work_a.add('file', 'file-id')
2475
base_id = work_a.commit('base version')
2476
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2477
preview = TransformPreview(work_a.basis_tree())
2478
self.addCleanup(preview.finalize)
2479
trans_id = preview.trans_id_file_id('file-id')
2480
preview.delete_contents(trans_id)
2481
preview.create_file('b\nc\nd\ne\n', trans_id)
2482
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2483
tree_a = preview.get_preview_tree()
2484
tree_a.set_parent_ids([base_id])
2486
('killed-a', 'a\n'),
2487
('killed-b', 'b\n'),
2488
('unchanged', 'c\n'),
2489
('unchanged', 'd\n'),
2492
], list(tree_a.plan_file_merge('file-id', tree_b)))
2494
def test_walkdirs(self):
2495
preview = self.get_empty_preview()
2496
preview.version_file('tree-root', preview.root)
2497
preview_tree = preview.get_preview_tree()
2498
file_trans_id = preview.new_file('a', preview.root, 'contents',
2500
expected = [(('', 'tree-root'),
2501
[('a', 'a', 'file', None, 'a-id', 'file')])]
2502
self.assertEqual(expected, list(preview_tree.walkdirs()))
2504
def test_extras(self):
2505
work_tree = self.make_branch_and_tree('tree')
2506
self.build_tree(['tree/removed-file', 'tree/existing-file',
2507
'tree/not-removed-file'])
2508
work_tree.add(['removed-file', 'not-removed-file'])
2509
preview = TransformPreview(work_tree)
2510
self.addCleanup(preview.finalize)
2511
preview.new_file('new-file', preview.root, 'contents')
2512
preview.new_file('new-versioned-file', preview.root, 'contents',
2514
tree = preview.get_preview_tree()
2515
preview.unversion_file(preview.trans_id_tree_path('removed-file'))
2516
self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
2519
def test_merge_into_preview(self):
2520
work_tree = self.make_branch_and_tree('tree')
2521
self.build_tree_contents([('tree/file','b\n')])
2522
work_tree.add('file', 'file-id')
2523
work_tree.commit('first commit')
2524
child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
2525
self.build_tree_contents([('child/file','b\nc\n')])
2526
child_tree.commit('child commit')
2527
child_tree.lock_write()
2528
self.addCleanup(child_tree.unlock)
2529
work_tree.lock_write()
2530
self.addCleanup(work_tree.unlock)
2531
preview = TransformPreview(work_tree)
2532
self.addCleanup(preview.finalize)
2533
preview_tree = preview.get_preview_tree()
2534
file_trans_id = preview.trans_id_file_id('file-id')
2535
preview.delete_contents(file_trans_id)
2536
preview.create_file('a\nb\n', file_trans_id)
2537
pb = progress.DummyProgress()
2538
merger = Merger.from_revision_ids(pb, preview_tree,
2539
child_tree.branch.last_revision(),
2540
other_branch=child_tree.branch,
2541
tree_branch=work_tree.branch)
2542
merger.merge_type = Merge3Merger
2543
tt = merger.make_merger().make_preview_transform()
2544
self.addCleanup(tt.finalize)
2545
final_tree = tt.get_preview_tree()
2546
self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
2548
def test_merge_preview_into_workingtree(self):
2549
tree = self.make_branch_and_tree('tree')
2550
tt = TransformPreview(tree)
2551
self.addCleanup(tt.finalize)
2552
tt.new_file('name', tt.root, 'content', 'file-id')
2553
tree2 = self.make_branch_and_tree('tree2')
2554
pb = progress.DummyProgress()
2555
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2556
pb, tree.basis_tree())
2557
merger.merge_type = Merge3Merger
2560
def test_merge_preview_into_workingtree_handles_conflicts(self):
2561
tree = self.make_branch_and_tree('tree')
2562
self.build_tree_contents([('tree/foo', 'bar')])
2563
tree.add('foo', 'foo-id')
2565
tt = TransformPreview(tree)
2566
self.addCleanup(tt.finalize)
2567
trans_id = tt.trans_id_file_id('foo-id')
2568
tt.delete_contents(trans_id)
2569
tt.create_file('baz', trans_id)
2570
tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2571
self.build_tree_contents([('tree2/foo', 'qux')])
2572
pb = progress.DummyProgress()
2573
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2574
pb, tree.basis_tree())
2575
merger.merge_type = Merge3Merger
2578
def test_is_executable(self):
2579
tree = self.make_branch_and_tree('tree')
2580
preview = TransformPreview(tree)
2581
self.addCleanup(preview.finalize)
2582
preview.new_file('foo', preview.root, 'bar', 'baz-id')
2583
preview_tree = preview.get_preview_tree()
2584
self.assertEqual(False, preview_tree.is_executable('baz-id',
2586
self.assertEqual(False, preview_tree.is_executable('baz-id'))
2589
class FakeSerializer(object):
2590
"""Serializer implementation that simply returns the input.
2592
The input is returned in the order used by pack.ContainerPushParser.
2595
def bytes_record(bytes, names):
2599
class TestSerializeTransform(tests.TestCaseWithTransport):
2601
_test_needs_features = [tests.UnicodeFilenameFeature]
2603
def get_preview(self, tree=None):
2605
tree = self.make_branch_and_tree('tree')
2606
tt = TransformPreview(tree)
2607
self.addCleanup(tt.finalize)
2610
def assertSerializesTo(self, expected, tt):
2611
records = list(tt.serialize(FakeSerializer()))
2612
self.assertEqual(expected, records)
2615
def default_attribs():
2620
'_new_executability': {},
2622
'_tree_path_ids': {'': 'new-0'},
2624
'_removed_contents': [],
2625
'_non_present_ids': {},
2628
def make_records(self, attribs, contents):
2630
(((('attribs'),),), bencode.bencode(attribs))]
2631
records.extend([(((n, k),), c) for n, k, c in contents])
2634
def creation_records(self):
2635
attribs = self.default_attribs()
2636
attribs['_id_number'] = 3
2637
attribs['_new_name'] = {
2638
'new-1': u'foo\u1234'.encode('utf-8'), 'new-2': 'qux'}
2639
attribs['_new_id'] = {'new-1': 'baz', 'new-2': 'quxx'}
2640
attribs['_new_parent'] = {'new-1': 'new-0', 'new-2': 'new-0'}
2641
attribs['_new_executability'] = {'new-1': 1}
2643
('new-1', 'file', 'i 1\nbar\n'),
2644
('new-2', 'directory', ''),
2646
return self.make_records(attribs, contents)
2648
def test_serialize_creation(self):
2649
tt = self.get_preview()
2650
tt.new_file(u'foo\u1234', tt.root, 'bar', 'baz', True)
2651
tt.new_directory('qux', tt.root, 'quxx')
2652
self.assertSerializesTo(self.creation_records(), tt)
2654
def test_deserialize_creation(self):
2655
tt = self.get_preview()
2656
tt.deserialize(iter(self.creation_records()))
2657
self.assertEqual(3, tt._id_number)
2658
self.assertEqual({'new-1': u'foo\u1234',
2659
'new-2': 'qux'}, tt._new_name)
2660
self.assertEqual({'new-1': 'baz', 'new-2': 'quxx'}, tt._new_id)
2661
self.assertEqual({'new-1': tt.root, 'new-2': tt.root}, tt._new_parent)
2662
self.assertEqual({'baz': 'new-1', 'quxx': 'new-2'}, tt._r_new_id)
2663
self.assertEqual({'new-1': True}, tt._new_executability)
2664
self.assertEqual({'new-1': 'file',
2665
'new-2': 'directory'}, tt._new_contents)
2666
foo_limbo = open(tt._limbo_name('new-1'), 'rb')
2668
foo_content = foo_limbo.read()
2671
self.assertEqual('bar', foo_content)
2673
def symlink_creation_records(self):
2674
attribs = self.default_attribs()
2675
attribs['_id_number'] = 2
2676
attribs['_new_name'] = {'new-1': u'foo\u1234'.encode('utf-8')}
2677
attribs['_new_parent'] = {'new-1': 'new-0'}
2678
contents = [('new-1', 'symlink', u'bar\u1234'.encode('utf-8'))]
2679
return self.make_records(attribs, contents)
2681
def test_serialize_symlink_creation(self):
2682
self.requireFeature(tests.SymlinkFeature)
2683
tt = self.get_preview()
2684
tt.new_symlink(u'foo\u1234', tt.root, u'bar\u1234')
2685
self.assertSerializesTo(self.symlink_creation_records(), tt)
2687
def test_deserialize_symlink_creation(self):
2688
tt = self.get_preview()
2689
tt.deserialize(iter(self.symlink_creation_records()))
2690
# XXX readlink should be returning unicode, not utf-8
2691
foo_content = os.readlink(tt._limbo_name('new-1')).decode('utf-8')
2692
self.assertEqual(u'bar\u1234', foo_content)
2694
def make_destruction_preview(self):
2695
tree = self.make_branch_and_tree('.')
2696
self.build_tree([u'foo\u1234', 'bar'])
2697
tree.add([u'foo\u1234', 'bar'], ['foo-id', 'bar-id'])
2698
return self.get_preview(tree)
2700
def destruction_records(self):
2701
attribs = self.default_attribs()
2702
attribs['_id_number'] = 3
2703
attribs['_removed_id'] = ['new-1']
2704
attribs['_removed_contents'] = ['new-2']
2705
attribs['_tree_path_ids'] = {
2707
u'foo\u1234'.encode('utf-8'): 'new-1',
2710
return self.make_records(attribs, [])
2712
def test_serialize_destruction(self):
2713
tt = self.make_destruction_preview()
2714
foo_trans_id = tt.trans_id_tree_file_id('foo-id')
2715
tt.unversion_file(foo_trans_id)
2716
bar_trans_id = tt.trans_id_tree_file_id('bar-id')
2717
tt.delete_contents(bar_trans_id)
2718
self.assertSerializesTo(self.destruction_records(), tt)
2720
def test_deserialize_destruction(self):
2721
tt = self.make_destruction_preview()
2722
tt.deserialize(iter(self.destruction_records()))
2723
self.assertEqual({u'foo\u1234': 'new-1',
2725
'': tt.root}, tt._tree_path_ids)
2726
self.assertEqual({'new-1': u'foo\u1234',
2728
tt.root: ''}, tt._tree_id_paths)
2729
self.assertEqual(set(['new-1']), tt._removed_id)
2730
self.assertEqual(set(['new-2']), tt._removed_contents)
2732
def missing_records(self):
2733
attribs = self.default_attribs()
2734
attribs['_id_number'] = 2
2735
attribs['_non_present_ids'] = {
2737
return self.make_records(attribs, [])
2739
def test_serialize_missing(self):
2740
tt = self.get_preview()
2741
boo_trans_id = tt.trans_id_file_id('boo')
2742
self.assertSerializesTo(self.missing_records(), tt)
2744
def test_deserialize_missing(self):
2745
tt = self.get_preview()
2746
tt.deserialize(iter(self.missing_records()))
2747
self.assertEqual({'boo': 'new-1'}, tt._non_present_ids)
2749
def make_modification_preview(self):
2750
LINES_ONE = 'aa\nbb\ncc\ndd\n'
2751
LINES_TWO = 'z\nbb\nx\ndd\n'
2752
tree = self.make_branch_and_tree('tree')
2753
self.build_tree_contents([('tree/file', LINES_ONE)])
2754
tree.add('file', 'file-id')
2755
return self.get_preview(tree), LINES_TWO
2757
def modification_records(self):
2758
attribs = self.default_attribs()
2759
attribs['_id_number'] = 2
2760
attribs['_tree_path_ids'] = {
2763
attribs['_removed_contents'] = ['new-1']
2764
contents = [('new-1', 'file',
2765
'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
2766
return self.make_records(attribs, contents)
2768
def test_serialize_modification(self):
2769
tt, LINES = self.make_modification_preview()
2770
trans_id = tt.trans_id_file_id('file-id')
2771
tt.delete_contents(trans_id)
2772
tt.create_file(LINES, trans_id)
2773
self.assertSerializesTo(self.modification_records(), tt)
2775
def test_deserialize_modification(self):
2776
tt, LINES = self.make_modification_preview()
2777
tt.deserialize(iter(self.modification_records()))
2778
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2780
def make_kind_change_preview(self):
2781
LINES = 'a\nb\nc\nd\n'
2782
tree = self.make_branch_and_tree('tree')
2783
self.build_tree(['tree/foo/'])
2784
tree.add('foo', 'foo-id')
2785
return self.get_preview(tree), LINES
2787
def kind_change_records(self):
2788
attribs = self.default_attribs()
2789
attribs['_id_number'] = 2
2790
attribs['_tree_path_ids'] = {
2793
attribs['_removed_contents'] = ['new-1']
2794
contents = [('new-1', 'file',
2795
'i 4\na\nb\nc\nd\n\n')]
2796
return self.make_records(attribs, contents)
2798
def test_serialize_kind_change(self):
2799
tt, LINES = self.make_kind_change_preview()
2800
trans_id = tt.trans_id_file_id('foo-id')
2801
tt.delete_contents(trans_id)
2802
tt.create_file(LINES, trans_id)
2803
self.assertSerializesTo(self.kind_change_records(), tt)
2805
def test_deserialize_kind_change(self):
2806
tt, LINES = self.make_kind_change_preview()
2807
tt.deserialize(iter(self.kind_change_records()))
2808
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2810
def make_add_contents_preview(self):
2811
LINES = 'a\nb\nc\nd\n'
2812
tree = self.make_branch_and_tree('tree')
2813
self.build_tree(['tree/foo'])
2815
os.unlink('tree/foo')
2816
return self.get_preview(tree), LINES
2818
def add_contents_records(self):
2819
attribs = self.default_attribs()
2820
attribs['_id_number'] = 2
2821
attribs['_tree_path_ids'] = {
2824
contents = [('new-1', 'file',
2825
'i 4\na\nb\nc\nd\n\n')]
2826
return self.make_records(attribs, contents)
2828
def test_serialize_add_contents(self):
2829
tt, LINES = self.make_add_contents_preview()
2830
trans_id = tt.trans_id_tree_path('foo')
2831
tt.create_file(LINES, trans_id)
2832
self.assertSerializesTo(self.add_contents_records(), tt)
2834
def test_deserialize_add_contents(self):
2835
tt, LINES = self.make_add_contents_preview()
2836
tt.deserialize(iter(self.add_contents_records()))
2837
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2839
def test_get_parents_lines(self):
2840
LINES_ONE = 'aa\nbb\ncc\ndd\n'
2841
LINES_TWO = 'z\nbb\nx\ndd\n'
2842
tree = self.make_branch_and_tree('tree')
2843
self.build_tree_contents([('tree/file', LINES_ONE)])
2844
tree.add('file', 'file-id')
2845
tt = self.get_preview(tree)
2846
trans_id = tt.trans_id_tree_path('file')
2847
self.assertEqual((['aa\n', 'bb\n', 'cc\n', 'dd\n'],),
2848
tt._get_parents_lines(trans_id))
2850
def test_get_parents_texts(self):
2851
LINES_ONE = 'aa\nbb\ncc\ndd\n'
2852
LINES_TWO = 'z\nbb\nx\ndd\n'
2853
tree = self.make_branch_and_tree('tree')
2854
self.build_tree_contents([('tree/file', LINES_ONE)])
2855
tree.add('file', 'file-id')
2856
tt = self.get_preview(tree)
2857
trans_id = tt.trans_id_tree_path('file')
2858
self.assertEqual((LINES_ONE,),
2859
tt._get_parents_texts(trans_id))