1002
744
old = transform.trans_id_tree_path('old')
1003
745
subdir = transform.trans_id_tree_file_id('subdir-id')
1004
self.assertEqual([], list(transform.iter_changes()))
746
self.assertEqual([], list(transform._iter_changes()))
1005
747
transform.delete_contents(subdir)
1006
748
transform.create_directory(subdir)
1007
749
transform.set_executability(False, old)
1008
750
transform.unversion_file(old)
1009
751
transform.version_file('id-1', old)
1010
752
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'))
753
self.assertEqual([], list(transform._iter_changes()))
1371
757
class TransformGroup(object):
1373
758
def __init__(self, dirname, root_id):
1374
759
self.name = dirname
1375
760
os.mkdir(dirname)
1706
1092
target = self.make_branch_and_tree('target')
1707
1093
self.build_tree(['target/name'])
1708
1094
target.add('name')
1709
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1095
self.assertRaises(errors.WorkingTreeAlreadyPopulated,
1710
1096
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'))
1876
class TestCommitTransform(tests.TestCaseWithTransport):
1878
def get_branch(self):
1879
tree = self.make_branch_and_tree('tree')
1881
self.addCleanup(tree.unlock)
1882
tree.commit('empty commit')
1885
def get_branch_and_transform(self):
1886
branch = self.get_branch()
1887
tt = TransformPreview(branch.basis_tree())
1888
self.addCleanup(tt.finalize)
1891
def test_commit_wrong_basis(self):
1892
branch = self.get_branch()
1893
basis = branch.repository.revision_tree(
1894
_mod_revision.NULL_REVISION)
1895
tt = TransformPreview(basis)
1896
self.addCleanup(tt.finalize)
1897
e = self.assertRaises(ValueError, tt.commit, branch, '')
1898
self.assertEqual('TreeTransform not based on branch basis: null:',
1901
def test_empy_commit(self):
1902
branch, tt = self.get_branch_and_transform()
1903
rev = tt.commit(branch, 'my message')
1904
self.assertEqual(2, branch.revno())
1905
repo = branch.repository
1906
self.assertEqual('my message', repo.get_revision(rev).message)
1908
def test_merge_parents(self):
1909
branch, tt = self.get_branch_and_transform()
1910
rev = tt.commit(branch, 'my message', ['rev1b', 'rev1c'])
1911
self.assertEqual(['rev1b', 'rev1c'],
1912
branch.basis_tree().get_parent_ids()[1:])
1914
def test_first_commit(self):
1915
branch = self.make_branch('branch')
1917
self.addCleanup(branch.unlock)
1918
tt = TransformPreview(branch.basis_tree())
1919
tt.new_directory('', ROOT_PARENT, 'TREE_ROOT')
1920
rev = tt.commit(branch, 'my message')
1921
self.assertEqual([], branch.basis_tree().get_parent_ids())
1922
self.assertNotEqual(_mod_revision.NULL_REVISION,
1923
branch.last_revision())
1925
def test_first_commit_with_merge_parents(self):
1926
branch = self.make_branch('branch')
1928
self.addCleanup(branch.unlock)
1929
tt = TransformPreview(branch.basis_tree())
1930
e = self.assertRaises(ValueError, tt.commit, branch,
1931
'my message', ['rev1b-id'])
1932
self.assertEqual('Cannot supply merge parents for first commit.',
1934
self.assertEqual(_mod_revision.NULL_REVISION, branch.last_revision())
1936
def test_add_files(self):
1937
branch, tt = self.get_branch_and_transform()
1938
tt.new_file('file', tt.root, 'contents', 'file-id')
1939
trans_id = tt.new_directory('dir', tt.root, 'dir-id')
1940
tt.new_symlink('symlink', trans_id, 'target', 'symlink-id')
1941
rev = tt.commit(branch, 'message')
1942
tree = branch.basis_tree()
1943
self.assertEqual('file', tree.id2path('file-id'))
1944
self.assertEqual('contents', tree.get_file_text('file-id'))
1945
self.assertEqual('dir', tree.id2path('dir-id'))
1946
self.assertEqual('dir/symlink', tree.id2path('symlink-id'))
1947
self.assertEqual('target', tree.get_symlink_target('symlink-id'))
1949
def test_add_unversioned(self):
1950
branch, tt = self.get_branch_and_transform()
1951
tt.new_file('file', tt.root, 'contents')
1952
self.assertRaises(errors.StrictCommitFailed, tt.commit, branch,
1953
'message', strict=True)
1955
def test_modify_strict(self):
1956
branch, tt = self.get_branch_and_transform()
1957
tt.new_file('file', tt.root, 'contents', 'file-id')
1958
tt.commit(branch, 'message', strict=True)
1959
tt = TransformPreview(branch.basis_tree())
1960
trans_id = tt.trans_id_file_id('file-id')
1961
tt.delete_contents(trans_id)
1962
tt.create_file('contents', trans_id)
1963
tt.commit(branch, 'message', strict=True)
1965
def test_commit_malformed(self):
1966
"""Committing a malformed transform should raise an exception.
1968
In this case, we are adding a file without adding its parent.
1970
branch, tt = self.get_branch_and_transform()
1971
parent_id = tt.trans_id_file_id('parent-id')
1972
tt.new_file('file', parent_id, 'contents', 'file-id')
1973
self.assertRaises(errors.MalformedTransform, tt.commit, branch,
1977
1099
class MockTransform(object):
2005
1125
self.assertEqual(name, 'name.~1~')
2006
1126
name = get_backup_name(MockEntry(), {'a':['1', '2', '3']}, 'a', tt)
2007
1127
self.assertEqual(name, 'name.~4~')
2010
class TestFileMover(tests.TestCaseWithTransport):
2012
def test_file_mover(self):
2013
self.build_tree(['a/', 'a/b', 'c/', 'c/d'])
2014
mover = _FileMover()
2015
mover.rename('a', 'q')
2016
self.failUnlessExists('q')
2017
self.failIfExists('a')
2018
self.failUnlessExists('q/b')
2019
self.failUnlessExists('c')
2020
self.failUnlessExists('c/d')
2022
def test_pre_delete_rollback(self):
2023
self.build_tree(['a/'])
2024
mover = _FileMover()
2025
mover.pre_delete('a', 'q')
2026
self.failUnlessExists('q')
2027
self.failIfExists('a')
2029
self.failIfExists('q')
2030
self.failUnlessExists('a')
2032
def test_apply_deletions(self):
2033
self.build_tree(['a/', 'b/'])
2034
mover = _FileMover()
2035
mover.pre_delete('a', 'q')
2036
mover.pre_delete('b', 'r')
2037
self.failUnlessExists('q')
2038
self.failUnlessExists('r')
2039
self.failIfExists('a')
2040
self.failIfExists('b')
2041
mover.apply_deletions()
2042
self.failIfExists('q')
2043
self.failIfExists('r')
2044
self.failIfExists('a')
2045
self.failIfExists('b')
2047
def test_file_mover_rollback(self):
2048
self.build_tree(['a/', 'a/b', 'c/', 'c/d/', 'c/e/'])
2049
mover = _FileMover()
2050
mover.rename('c/d', 'c/f')
2051
mover.rename('c/e', 'c/d')
2053
mover.rename('a', 'c')
2054
except errors.FileExists, e:
2056
self.failUnlessExists('a')
2057
self.failUnlessExists('c/d')
2060
class Bogus(Exception):
2064
class TestTransformRollback(tests.TestCaseWithTransport):
2066
class ExceptionFileMover(_FileMover):
2068
def __init__(self, bad_source=None, bad_target=None):
2069
_FileMover.__init__(self)
2070
self.bad_source = bad_source
2071
self.bad_target = bad_target
2073
def rename(self, source, target):
2074
if (self.bad_source is not None and
2075
source.endswith(self.bad_source)):
2077
elif (self.bad_target is not None and
2078
target.endswith(self.bad_target)):
2081
_FileMover.rename(self, source, target)
2083
def test_rollback_rename(self):
2084
tree = self.make_branch_and_tree('.')
2085
self.build_tree(['a/', 'a/b'])
2086
tt = TreeTransform(tree)
2087
self.addCleanup(tt.finalize)
2088
a_id = tt.trans_id_tree_path('a')
2089
tt.adjust_path('c', tt.root, a_id)
2090
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2091
self.assertRaises(Bogus, tt.apply,
2092
_mover=self.ExceptionFileMover(bad_source='a'))
2093
self.failUnlessExists('a')
2094
self.failUnlessExists('a/b')
2096
self.failUnlessExists('c')
2097
self.failUnlessExists('c/d')
2099
def test_rollback_rename_into_place(self):
2100
tree = self.make_branch_and_tree('.')
2101
self.build_tree(['a/', 'a/b'])
2102
tt = TreeTransform(tree)
2103
self.addCleanup(tt.finalize)
2104
a_id = tt.trans_id_tree_path('a')
2105
tt.adjust_path('c', tt.root, a_id)
2106
tt.adjust_path('d', a_id, tt.trans_id_tree_path('a/b'))
2107
self.assertRaises(Bogus, tt.apply,
2108
_mover=self.ExceptionFileMover(bad_target='c/d'))
2109
self.failUnlessExists('a')
2110
self.failUnlessExists('a/b')
2112
self.failUnlessExists('c')
2113
self.failUnlessExists('c/d')
2115
def test_rollback_deletion(self):
2116
tree = self.make_branch_and_tree('.')
2117
self.build_tree(['a/', 'a/b'])
2118
tt = TreeTransform(tree)
2119
self.addCleanup(tt.finalize)
2120
a_id = tt.trans_id_tree_path('a')
2121
tt.delete_contents(a_id)
2122
tt.adjust_path('d', tt.root, tt.trans_id_tree_path('a/b'))
2123
self.assertRaises(Bogus, tt.apply,
2124
_mover=self.ExceptionFileMover(bad_target='d'))
2125
self.failUnlessExists('a')
2126
self.failUnlessExists('a/b')
2128
def test_resolve_no_parent(self):
2129
wt = self.make_branch_and_tree('.')
2130
tt = TreeTransform(wt)
2131
self.addCleanup(tt.finalize)
2132
parent = tt.trans_id_file_id('parent-id')
2133
tt.new_file('file', parent, 'Contents')
2134
resolve_conflicts(tt)
2137
A_ENTRY = ('a-id', ('a', 'a'), True, (True, True),
2138
('TREE_ROOT', 'TREE_ROOT'), ('a', 'a'), ('file', 'file'),
2140
ROOT_ENTRY = ('TREE_ROOT', ('', ''), False, (True, True), (None, None),
2141
('', ''), ('directory', 'directory'), (False, None))
2144
class TestTransformPreview(tests.TestCaseWithTransport):
2146
def create_tree(self):
2147
tree = self.make_branch_and_tree('.')
2148
self.build_tree_contents([('a', 'content 1')])
2149
tree.set_root_id('TREE_ROOT')
2150
tree.add('a', 'a-id')
2151
tree.commit('rev1', rev_id='rev1')
2152
return tree.branch.repository.revision_tree('rev1')
2154
def get_empty_preview(self):
2155
repository = self.make_repository('repo')
2156
tree = repository.revision_tree(_mod_revision.NULL_REVISION)
2157
preview = TransformPreview(tree)
2158
self.addCleanup(preview.finalize)
2161
def test_transform_preview(self):
2162
revision_tree = self.create_tree()
2163
preview = TransformPreview(revision_tree)
2164
self.addCleanup(preview.finalize)
2166
def test_transform_preview_tree(self):
2167
revision_tree = self.create_tree()
2168
preview = TransformPreview(revision_tree)
2169
self.addCleanup(preview.finalize)
2170
preview.get_preview_tree()
2172
def test_transform_new_file(self):
2173
revision_tree = self.create_tree()
2174
preview = TransformPreview(revision_tree)
2175
self.addCleanup(preview.finalize)
2176
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2177
preview_tree = preview.get_preview_tree()
2178
self.assertEqual(preview_tree.kind('file2-id'), 'file')
2180
preview_tree.get_file('file2-id').read(), 'content B\n')
2182
def test_diff_preview_tree(self):
2183
revision_tree = self.create_tree()
2184
preview = TransformPreview(revision_tree)
2185
self.addCleanup(preview.finalize)
2186
preview.new_file('file2', preview.root, 'content B\n', 'file2-id')
2187
preview_tree = preview.get_preview_tree()
2189
show_diff_trees(revision_tree, preview_tree, out)
2190
lines = out.getvalue().splitlines()
2191
self.assertEqual(lines[0], "=== added file 'file2'")
2192
# 3 lines of diff administrivia
2193
self.assertEqual(lines[4], "+content B")
2195
def test_transform_conflicts(self):
2196
revision_tree = self.create_tree()
2197
preview = TransformPreview(revision_tree)
2198
self.addCleanup(preview.finalize)
2199
preview.new_file('a', preview.root, 'content 2')
2200
resolve_conflicts(preview)
2201
trans_id = preview.trans_id_file_id('a-id')
2202
self.assertEqual('a.moved', preview.final_name(trans_id))
2204
def get_tree_and_preview_tree(self):
2205
revision_tree = self.create_tree()
2206
preview = TransformPreview(revision_tree)
2207
self.addCleanup(preview.finalize)
2208
a_trans_id = preview.trans_id_file_id('a-id')
2209
preview.delete_contents(a_trans_id)
2210
preview.create_file('b content', a_trans_id)
2211
preview_tree = preview.get_preview_tree()
2212
return revision_tree, preview_tree
2214
def test_iter_changes(self):
2215
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2216
root = revision_tree.inventory.root.file_id
2217
self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
2218
(root, root), ('a', 'a'), ('file', 'file'),
2220
list(preview_tree.iter_changes(revision_tree)))
2222
def test_include_unchanged_succeeds(self):
2223
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2224
changes = preview_tree.iter_changes(revision_tree,
2225
include_unchanged=True)
2226
root = revision_tree.inventory.root.file_id
2228
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2230
def test_specific_files(self):
2231
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2232
changes = preview_tree.iter_changes(revision_tree,
2233
specific_files=[''])
2234
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2236
def test_want_unversioned(self):
2237
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2238
changes = preview_tree.iter_changes(revision_tree,
2239
want_unversioned=True)
2240
self.assertEqual([ROOT_ENTRY, A_ENTRY], list(changes))
2242
def test_ignore_extra_trees_no_specific_files(self):
2243
# extra_trees is harmless without specific_files, so we'll silently
2244
# accept it, even though we won't use it.
2245
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2246
preview_tree.iter_changes(revision_tree, extra_trees=[preview_tree])
2248
def test_ignore_require_versioned_no_specific_files(self):
2249
# require_versioned is meaningless without specific_files.
2250
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2251
preview_tree.iter_changes(revision_tree, require_versioned=False)
2253
def test_ignore_pb(self):
2254
# pb could be supported, but TT.iter_changes doesn't support it.
2255
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2256
preview_tree.iter_changes(revision_tree, pb=progress.DummyProgress())
2258
def test_kind(self):
2259
revision_tree = self.create_tree()
2260
preview = TransformPreview(revision_tree)
2261
self.addCleanup(preview.finalize)
2262
preview.new_file('file', preview.root, 'contents', 'file-id')
2263
preview.new_directory('directory', preview.root, 'dir-id')
2264
preview_tree = preview.get_preview_tree()
2265
self.assertEqual('file', preview_tree.kind('file-id'))
2266
self.assertEqual('directory', preview_tree.kind('dir-id'))
2268
def test_get_file_mtime(self):
2269
preview = self.get_empty_preview()
2270
file_trans_id = preview.new_file('file', preview.root, 'contents',
2272
limbo_path = preview._limbo_name(file_trans_id)
2273
preview_tree = preview.get_preview_tree()
2274
self.assertEqual(os.stat(limbo_path).st_mtime,
2275
preview_tree.get_file_mtime('file-id'))
2277
def test_get_file(self):
2278
preview = self.get_empty_preview()
2279
preview.new_file('file', preview.root, 'contents', 'file-id')
2280
preview_tree = preview.get_preview_tree()
2281
tree_file = preview_tree.get_file('file-id')
2283
self.assertEqual('contents', tree_file.read())
2287
def test_get_symlink_target(self):
2288
self.requireFeature(SymlinkFeature)
2289
preview = self.get_empty_preview()
2290
preview.new_symlink('symlink', preview.root, 'target', 'symlink-id')
2291
preview_tree = preview.get_preview_tree()
2292
self.assertEqual('target',
2293
preview_tree.get_symlink_target('symlink-id'))
2295
def test_all_file_ids(self):
2296
tree = self.make_branch_and_tree('tree')
2297
self.build_tree(['tree/a', 'tree/b', 'tree/c'])
2298
tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
2299
preview = TransformPreview(tree)
2300
self.addCleanup(preview.finalize)
2301
preview.unversion_file(preview.trans_id_file_id('b-id'))
2302
c_trans_id = preview.trans_id_file_id('c-id')
2303
preview.unversion_file(c_trans_id)
2304
preview.version_file('c-id', c_trans_id)
2305
preview_tree = preview.get_preview_tree()
2306
self.assertEqual(set(['a-id', 'c-id', tree.get_root_id()]),
2307
preview_tree.all_file_ids())
2309
def test_path2id_deleted_unchanged(self):
2310
tree = self.make_branch_and_tree('tree')
2311
self.build_tree(['tree/unchanged', 'tree/deleted'])
2312
tree.add(['unchanged', 'deleted'], ['unchanged-id', 'deleted-id'])
2313
preview = TransformPreview(tree)
2314
self.addCleanup(preview.finalize)
2315
preview.unversion_file(preview.trans_id_file_id('deleted-id'))
2316
preview_tree = preview.get_preview_tree()
2317
self.assertEqual('unchanged-id', preview_tree.path2id('unchanged'))
2318
self.assertIs(None, preview_tree.path2id('deleted'))
2320
def test_path2id_created(self):
2321
tree = self.make_branch_and_tree('tree')
2322
self.build_tree(['tree/unchanged'])
2323
tree.add(['unchanged'], ['unchanged-id'])
2324
preview = TransformPreview(tree)
2325
self.addCleanup(preview.finalize)
2326
preview.new_file('new', preview.trans_id_file_id('unchanged-id'),
2327
'contents', 'new-id')
2328
preview_tree = preview.get_preview_tree()
2329
self.assertEqual('new-id', preview_tree.path2id('unchanged/new'))
2331
def test_path2id_moved(self):
2332
tree = self.make_branch_and_tree('tree')
2333
self.build_tree(['tree/old_parent/', 'tree/old_parent/child'])
2334
tree.add(['old_parent', 'old_parent/child'],
2335
['old_parent-id', 'child-id'])
2336
preview = TransformPreview(tree)
2337
self.addCleanup(preview.finalize)
2338
new_parent = preview.new_directory('new_parent', preview.root,
2340
preview.adjust_path('child', new_parent,
2341
preview.trans_id_file_id('child-id'))
2342
preview_tree = preview.get_preview_tree()
2343
self.assertIs(None, preview_tree.path2id('old_parent/child'))
2344
self.assertEqual('child-id', preview_tree.path2id('new_parent/child'))
2346
def test_path2id_renamed_parent(self):
2347
tree = self.make_branch_and_tree('tree')
2348
self.build_tree(['tree/old_name/', 'tree/old_name/child'])
2349
tree.add(['old_name', 'old_name/child'],
2350
['parent-id', 'child-id'])
2351
preview = TransformPreview(tree)
2352
self.addCleanup(preview.finalize)
2353
preview.adjust_path('new_name', preview.root,
2354
preview.trans_id_file_id('parent-id'))
2355
preview_tree = preview.get_preview_tree()
2356
self.assertIs(None, preview_tree.path2id('old_name/child'))
2357
self.assertEqual('child-id', preview_tree.path2id('new_name/child'))
2359
def assertMatchingIterEntries(self, tt, specific_file_ids=None):
2360
preview_tree = tt.get_preview_tree()
2361
preview_result = list(preview_tree.iter_entries_by_dir(
2365
actual_result = list(tree.iter_entries_by_dir(specific_file_ids))
2366
self.assertEqual(actual_result, preview_result)
2368
def test_iter_entries_by_dir_new(self):
2369
tree = self.make_branch_and_tree('tree')
2370
tt = TreeTransform(tree)
2371
tt.new_file('new', tt.root, 'contents', 'new-id')
2372
self.assertMatchingIterEntries(tt)
2374
def test_iter_entries_by_dir_deleted(self):
2375
tree = self.make_branch_and_tree('tree')
2376
self.build_tree(['tree/deleted'])
2377
tree.add('deleted', 'deleted-id')
2378
tt = TreeTransform(tree)
2379
tt.delete_contents(tt.trans_id_file_id('deleted-id'))
2380
self.assertMatchingIterEntries(tt)
2382
def test_iter_entries_by_dir_unversioned(self):
2383
tree = self.make_branch_and_tree('tree')
2384
self.build_tree(['tree/removed'])
2385
tree.add('removed', 'removed-id')
2386
tt = TreeTransform(tree)
2387
tt.unversion_file(tt.trans_id_file_id('removed-id'))
2388
self.assertMatchingIterEntries(tt)
2390
def test_iter_entries_by_dir_moved(self):
2391
tree = self.make_branch_and_tree('tree')
2392
self.build_tree(['tree/moved', 'tree/new_parent/'])
2393
tree.add(['moved', 'new_parent'], ['moved-id', 'new_parent-id'])
2394
tt = TreeTransform(tree)
2395
tt.adjust_path('moved', tt.trans_id_file_id('new_parent-id'),
2396
tt.trans_id_file_id('moved-id'))
2397
self.assertMatchingIterEntries(tt)
2399
def test_iter_entries_by_dir_specific_file_ids(self):
2400
tree = self.make_branch_and_tree('tree')
2401
tree.set_root_id('tree-root-id')
2402
self.build_tree(['tree/parent/', 'tree/parent/child'])
2403
tree.add(['parent', 'parent/child'], ['parent-id', 'child-id'])
2404
tt = TreeTransform(tree)
2405
self.assertMatchingIterEntries(tt, ['tree-root-id', 'child-id'])
2407
def test_symlink_content_summary(self):
2408
self.requireFeature(SymlinkFeature)
2409
preview = self.get_empty_preview()
2410
preview.new_symlink('path', preview.root, 'target', 'path-id')
2411
summary = preview.get_preview_tree().path_content_summary('path')
2412
self.assertEqual(('symlink', None, None, 'target'), summary)
2414
def test_missing_content_summary(self):
2415
preview = self.get_empty_preview()
2416
summary = preview.get_preview_tree().path_content_summary('path')
2417
self.assertEqual(('missing', None, None, None), summary)
2419
def test_deleted_content_summary(self):
2420
tree = self.make_branch_and_tree('tree')
2421
self.build_tree(['tree/path/'])
2423
preview = TransformPreview(tree)
2424
self.addCleanup(preview.finalize)
2425
preview.delete_contents(preview.trans_id_tree_path('path'))
2426
summary = preview.get_preview_tree().path_content_summary('path')
2427
self.assertEqual(('missing', None, None, None), summary)
2429
def test_file_content_summary_executable(self):
2430
if not osutils.supports_executable():
2431
raise TestNotApplicable()
2432
preview = self.get_empty_preview()
2433
path_id = preview.new_file('path', preview.root, 'contents', 'path-id')
2434
preview.set_executability(True, path_id)
2435
summary = preview.get_preview_tree().path_content_summary('path')
2436
self.assertEqual(4, len(summary))
2437
self.assertEqual('file', summary[0])
2438
# size must be known
2439
self.assertEqual(len('contents'), summary[1])
2441
self.assertEqual(True, summary[2])
2442
# will not have hash (not cheap to determine)
2443
self.assertIs(None, summary[3])
2445
def test_change_executability(self):
2446
if not osutils.supports_executable():
2447
raise TestNotApplicable()
2448
tree = self.make_branch_and_tree('tree')
2449
self.build_tree(['tree/path'])
2451
preview = TransformPreview(tree)
2452
self.addCleanup(preview.finalize)
2453
path_id = preview.trans_id_tree_path('path')
2454
preview.set_executability(True, path_id)
2455
summary = preview.get_preview_tree().path_content_summary('path')
2456
self.assertEqual(True, summary[2])
2458
def test_file_content_summary_non_exec(self):
2459
preview = self.get_empty_preview()
2460
preview.new_file('path', preview.root, 'contents', 'path-id')
2461
summary = preview.get_preview_tree().path_content_summary('path')
2462
self.assertEqual(4, len(summary))
2463
self.assertEqual('file', summary[0])
2464
# size must be known
2465
self.assertEqual(len('contents'), summary[1])
2467
if osutils.supports_executable():
2468
self.assertEqual(False, summary[2])
2470
self.assertEqual(None, summary[2])
2471
# will not have hash (not cheap to determine)
2472
self.assertIs(None, summary[3])
2474
def test_dir_content_summary(self):
2475
preview = self.get_empty_preview()
2476
preview.new_directory('path', preview.root, 'path-id')
2477
summary = preview.get_preview_tree().path_content_summary('path')
2478
self.assertEqual(('directory', None, None, None), summary)
2480
def test_tree_content_summary(self):
2481
preview = self.get_empty_preview()
2482
path = preview.new_directory('path', preview.root, 'path-id')
2483
preview.set_tree_reference('rev-1', path)
2484
summary = preview.get_preview_tree().path_content_summary('path')
2485
self.assertEqual(4, len(summary))
2486
self.assertEqual('tree-reference', summary[0])
2488
def test_annotate(self):
2489
tree = self.make_branch_and_tree('tree')
2490
self.build_tree_contents([('tree/file', 'a\n')])
2491
tree.add('file', 'file-id')
2492
tree.commit('a', rev_id='one')
2493
self.build_tree_contents([('tree/file', 'a\nb\n')])
2494
preview = TransformPreview(tree)
2495
self.addCleanup(preview.finalize)
2496
file_trans_id = preview.trans_id_file_id('file-id')
2497
preview.delete_contents(file_trans_id)
2498
preview.create_file('a\nb\nc\n', file_trans_id)
2499
preview_tree = preview.get_preview_tree()
2505
annotation = preview_tree.annotate_iter('file-id', 'me:')
2506
self.assertEqual(expected, annotation)
2508
def test_annotate_missing(self):
2509
preview = self.get_empty_preview()
2510
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2511
preview_tree = preview.get_preview_tree()
2517
annotation = preview_tree.annotate_iter('file-id', 'me:')
2518
self.assertEqual(expected, annotation)
2520
def test_annotate_rename(self):
2521
tree = self.make_branch_and_tree('tree')
2522
self.build_tree_contents([('tree/file', 'a\n')])
2523
tree.add('file', 'file-id')
2524
tree.commit('a', rev_id='one')
2525
preview = TransformPreview(tree)
2526
self.addCleanup(preview.finalize)
2527
file_trans_id = preview.trans_id_file_id('file-id')
2528
preview.adjust_path('newname', preview.root, file_trans_id)
2529
preview_tree = preview.get_preview_tree()
2533
annotation = preview_tree.annotate_iter('file-id', 'me:')
2534
self.assertEqual(expected, annotation)
2536
def test_annotate_deleted(self):
2537
tree = self.make_branch_and_tree('tree')
2538
self.build_tree_contents([('tree/file', 'a\n')])
2539
tree.add('file', 'file-id')
2540
tree.commit('a', rev_id='one')
2541
self.build_tree_contents([('tree/file', 'a\nb\n')])
2542
preview = TransformPreview(tree)
2543
self.addCleanup(preview.finalize)
2544
file_trans_id = preview.trans_id_file_id('file-id')
2545
preview.delete_contents(file_trans_id)
2546
preview_tree = preview.get_preview_tree()
2547
annotation = preview_tree.annotate_iter('file-id', 'me:')
2548
self.assertIs(None, annotation)
2550
def test_stored_kind(self):
2551
preview = self.get_empty_preview()
2552
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2553
preview_tree = preview.get_preview_tree()
2554
self.assertEqual('file', preview_tree.stored_kind('file-id'))
2556
def test_is_executable(self):
2557
preview = self.get_empty_preview()
2558
preview.new_file('file', preview.root, 'a\nb\nc\n', 'file-id')
2559
preview.set_executability(True, preview.trans_id_file_id('file-id'))
2560
preview_tree = preview.get_preview_tree()
2561
self.assertEqual(True, preview_tree.is_executable('file-id'))
2563
def test_get_set_parent_ids(self):
2564
revision_tree, preview_tree = self.get_tree_and_preview_tree()
2565
self.assertEqual([], preview_tree.get_parent_ids())
2566
preview_tree.set_parent_ids(['rev-1'])
2567
self.assertEqual(['rev-1'], preview_tree.get_parent_ids())
2569
def test_plan_file_merge(self):
2570
work_a = self.make_branch_and_tree('wta')
2571
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2572
work_a.add('file', 'file-id')
2573
base_id = work_a.commit('base version')
2574
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2575
preview = TransformPreview(work_a)
2576
self.addCleanup(preview.finalize)
2577
trans_id = preview.trans_id_file_id('file-id')
2578
preview.delete_contents(trans_id)
2579
preview.create_file('b\nc\nd\ne\n', trans_id)
2580
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2581
tree_a = preview.get_preview_tree()
2582
tree_a.set_parent_ids([base_id])
2584
('killed-a', 'a\n'),
2585
('killed-b', 'b\n'),
2586
('unchanged', 'c\n'),
2587
('unchanged', 'd\n'),
2590
], list(tree_a.plan_file_merge('file-id', tree_b)))
2592
def test_plan_file_merge_revision_tree(self):
2593
work_a = self.make_branch_and_tree('wta')
2594
self.build_tree_contents([('wta/file', 'a\nb\nc\nd\n')])
2595
work_a.add('file', 'file-id')
2596
base_id = work_a.commit('base version')
2597
tree_b = work_a.bzrdir.sprout('wtb').open_workingtree()
2598
preview = TransformPreview(work_a.basis_tree())
2599
self.addCleanup(preview.finalize)
2600
trans_id = preview.trans_id_file_id('file-id')
2601
preview.delete_contents(trans_id)
2602
preview.create_file('b\nc\nd\ne\n', trans_id)
2603
self.build_tree_contents([('wtb/file', 'a\nc\nd\nf\n')])
2604
tree_a = preview.get_preview_tree()
2605
tree_a.set_parent_ids([base_id])
2607
('killed-a', 'a\n'),
2608
('killed-b', 'b\n'),
2609
('unchanged', 'c\n'),
2610
('unchanged', 'd\n'),
2613
], list(tree_a.plan_file_merge('file-id', tree_b)))
2615
def test_walkdirs(self):
2616
preview = self.get_empty_preview()
2617
preview.version_file('tree-root', preview.root)
2618
preview_tree = preview.get_preview_tree()
2619
file_trans_id = preview.new_file('a', preview.root, 'contents',
2621
expected = [(('', 'tree-root'),
2622
[('a', 'a', 'file', None, 'a-id', 'file')])]
2623
self.assertEqual(expected, list(preview_tree.walkdirs()))
2625
def test_extras(self):
2626
work_tree = self.make_branch_and_tree('tree')
2627
self.build_tree(['tree/removed-file', 'tree/existing-file',
2628
'tree/not-removed-file'])
2629
work_tree.add(['removed-file', 'not-removed-file'])
2630
preview = TransformPreview(work_tree)
2631
self.addCleanup(preview.finalize)
2632
preview.new_file('new-file', preview.root, 'contents')
2633
preview.new_file('new-versioned-file', preview.root, 'contents',
2635
tree = preview.get_preview_tree()
2636
preview.unversion_file(preview.trans_id_tree_path('removed-file'))
2637
self.assertEqual(set(['new-file', 'removed-file', 'existing-file']),
2640
def test_merge_into_preview(self):
2641
work_tree = self.make_branch_and_tree('tree')
2642
self.build_tree_contents([('tree/file','b\n')])
2643
work_tree.add('file', 'file-id')
2644
work_tree.commit('first commit')
2645
child_tree = work_tree.bzrdir.sprout('child').open_workingtree()
2646
self.build_tree_contents([('child/file','b\nc\n')])
2647
child_tree.commit('child commit')
2648
child_tree.lock_write()
2649
self.addCleanup(child_tree.unlock)
2650
work_tree.lock_write()
2651
self.addCleanup(work_tree.unlock)
2652
preview = TransformPreview(work_tree)
2653
self.addCleanup(preview.finalize)
2654
preview_tree = preview.get_preview_tree()
2655
file_trans_id = preview.trans_id_file_id('file-id')
2656
preview.delete_contents(file_trans_id)
2657
preview.create_file('a\nb\n', file_trans_id)
2658
pb = progress.DummyProgress()
2659
merger = Merger.from_revision_ids(pb, preview_tree,
2660
child_tree.branch.last_revision(),
2661
other_branch=child_tree.branch,
2662
tree_branch=work_tree.branch)
2663
merger.merge_type = Merge3Merger
2664
tt = merger.make_merger().make_preview_transform()
2665
self.addCleanup(tt.finalize)
2666
final_tree = tt.get_preview_tree()
2667
self.assertEqual('a\nb\nc\n', final_tree.get_file_text('file-id'))
2669
def test_merge_preview_into_workingtree(self):
2670
tree = self.make_branch_and_tree('tree')
2671
tree.set_root_id('TREE_ROOT')
2672
tt = TransformPreview(tree)
2673
self.addCleanup(tt.finalize)
2674
tt.new_file('name', tt.root, 'content', 'file-id')
2675
tree2 = self.make_branch_and_tree('tree2')
2676
tree2.set_root_id('TREE_ROOT')
2677
pb = progress.DummyProgress()
2678
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2679
pb, tree.basis_tree())
2680
merger.merge_type = Merge3Merger
2683
def test_merge_preview_into_workingtree_handles_conflicts(self):
2684
tree = self.make_branch_and_tree('tree')
2685
self.build_tree_contents([('tree/foo', 'bar')])
2686
tree.add('foo', 'foo-id')
2688
tt = TransformPreview(tree)
2689
self.addCleanup(tt.finalize)
2690
trans_id = tt.trans_id_file_id('foo-id')
2691
tt.delete_contents(trans_id)
2692
tt.create_file('baz', trans_id)
2693
tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
2694
self.build_tree_contents([('tree2/foo', 'qux')])
2695
pb = progress.DummyProgress()
2696
merger = Merger.from_uncommitted(tree2, tt.get_preview_tree(),
2697
pb, tree.basis_tree())
2698
merger.merge_type = Merge3Merger
2701
def test_is_executable(self):
2702
tree = self.make_branch_and_tree('tree')
2703
preview = TransformPreview(tree)
2704
self.addCleanup(preview.finalize)
2705
preview.new_file('foo', preview.root, 'bar', 'baz-id')
2706
preview_tree = preview.get_preview_tree()
2707
self.assertEqual(False, preview_tree.is_executable('baz-id',
2709
self.assertEqual(False, preview_tree.is_executable('baz-id'))
2711
def test_commit_preview_tree(self):
2712
tree = self.make_branch_and_tree('tree')
2713
rev_id = tree.commit('rev1')
2714
tree.branch.lock_write()
2715
self.addCleanup(tree.branch.unlock)
2716
tt = TransformPreview(tree)
2717
tt.new_file('file', tt.root, 'contents', 'file_id')
2718
self.addCleanup(tt.finalize)
2719
preview = tt.get_preview_tree()
2720
preview.set_parent_ids([rev_id])
2721
builder = tree.branch.get_commit_builder([rev_id])
2722
list(builder.record_iter_changes(preview, rev_id, tt.iter_changes()))
2723
builder.finish_inventory()
2724
rev2_id = builder.commit('rev2')
2725
rev2_tree = tree.branch.repository.revision_tree(rev2_id)
2726
self.assertEqual('contents', rev2_tree.get_file_text('file_id'))
2729
class FakeSerializer(object):
2730
"""Serializer implementation that simply returns the input.
2732
The input is returned in the order used by pack.ContainerPushParser.
2735
def bytes_record(bytes, names):
2739
class TestSerializeTransform(tests.TestCaseWithTransport):
2741
_test_needs_features = [tests.UnicodeFilenameFeature]
2743
def get_preview(self, tree=None):
2745
tree = self.make_branch_and_tree('tree')
2746
tt = TransformPreview(tree)
2747
self.addCleanup(tt.finalize)
2750
def assertSerializesTo(self, expected, tt):
2751
records = list(tt.serialize(FakeSerializer()))
2752
self.assertEqual(expected, records)
2755
def default_attribs():
2760
'_new_executability': {},
2762
'_tree_path_ids': {'': 'new-0'},
2764
'_removed_contents': [],
2765
'_non_present_ids': {},
2768
def make_records(self, attribs, contents):
2770
(((('attribs'),),), bencode.bencode(attribs))]
2771
records.extend([(((n, k),), c) for n, k, c in contents])
2774
def creation_records(self):
2775
attribs = self.default_attribs()
2776
attribs['_id_number'] = 3
2777
attribs['_new_name'] = {
2778
'new-1': u'foo\u1234'.encode('utf-8'), 'new-2': 'qux'}
2779
attribs['_new_id'] = {'new-1': 'baz', 'new-2': 'quxx'}
2780
attribs['_new_parent'] = {'new-1': 'new-0', 'new-2': 'new-0'}
2781
attribs['_new_executability'] = {'new-1': 1}
2783
('new-1', 'file', 'i 1\nbar\n'),
2784
('new-2', 'directory', ''),
2786
return self.make_records(attribs, contents)
2788
def test_serialize_creation(self):
2789
tt = self.get_preview()
2790
tt.new_file(u'foo\u1234', tt.root, 'bar', 'baz', True)
2791
tt.new_directory('qux', tt.root, 'quxx')
2792
self.assertSerializesTo(self.creation_records(), tt)
2794
def test_deserialize_creation(self):
2795
tt = self.get_preview()
2796
tt.deserialize(iter(self.creation_records()))
2797
self.assertEqual(3, tt._id_number)
2798
self.assertEqual({'new-1': u'foo\u1234',
2799
'new-2': 'qux'}, tt._new_name)
2800
self.assertEqual({'new-1': 'baz', 'new-2': 'quxx'}, tt._new_id)
2801
self.assertEqual({'new-1': tt.root, 'new-2': tt.root}, tt._new_parent)
2802
self.assertEqual({'baz': 'new-1', 'quxx': 'new-2'}, tt._r_new_id)
2803
self.assertEqual({'new-1': True}, tt._new_executability)
2804
self.assertEqual({'new-1': 'file',
2805
'new-2': 'directory'}, tt._new_contents)
2806
foo_limbo = open(tt._limbo_name('new-1'), 'rb')
2808
foo_content = foo_limbo.read()
2811
self.assertEqual('bar', foo_content)
2813
def symlink_creation_records(self):
2814
attribs = self.default_attribs()
2815
attribs['_id_number'] = 2
2816
attribs['_new_name'] = {'new-1': u'foo\u1234'.encode('utf-8')}
2817
attribs['_new_parent'] = {'new-1': 'new-0'}
2818
contents = [('new-1', 'symlink', u'bar\u1234'.encode('utf-8'))]
2819
return self.make_records(attribs, contents)
2821
def test_serialize_symlink_creation(self):
2822
self.requireFeature(tests.SymlinkFeature)
2823
tt = self.get_preview()
2824
tt.new_symlink(u'foo\u1234', tt.root, u'bar\u1234')
2825
self.assertSerializesTo(self.symlink_creation_records(), tt)
2827
def test_deserialize_symlink_creation(self):
2828
self.requireFeature(tests.SymlinkFeature)
2829
tt = self.get_preview()
2830
tt.deserialize(iter(self.symlink_creation_records()))
2831
abspath = tt._limbo_name('new-1')
2832
foo_content = osutils.readlink(abspath)
2833
self.assertEqual(u'bar\u1234', foo_content)
2835
def make_destruction_preview(self):
2836
tree = self.make_branch_and_tree('.')
2837
self.build_tree([u'foo\u1234', 'bar'])
2838
tree.add([u'foo\u1234', 'bar'], ['foo-id', 'bar-id'])
2839
return self.get_preview(tree)
2841
def destruction_records(self):
2842
attribs = self.default_attribs()
2843
attribs['_id_number'] = 3
2844
attribs['_removed_id'] = ['new-1']
2845
attribs['_removed_contents'] = ['new-2']
2846
attribs['_tree_path_ids'] = {
2848
u'foo\u1234'.encode('utf-8'): 'new-1',
2851
return self.make_records(attribs, [])
2853
def test_serialize_destruction(self):
2854
tt = self.make_destruction_preview()
2855
foo_trans_id = tt.trans_id_tree_file_id('foo-id')
2856
tt.unversion_file(foo_trans_id)
2857
bar_trans_id = tt.trans_id_tree_file_id('bar-id')
2858
tt.delete_contents(bar_trans_id)
2859
self.assertSerializesTo(self.destruction_records(), tt)
2861
def test_deserialize_destruction(self):
2862
tt = self.make_destruction_preview()
2863
tt.deserialize(iter(self.destruction_records()))
2864
self.assertEqual({u'foo\u1234': 'new-1',
2866
'': tt.root}, tt._tree_path_ids)
2867
self.assertEqual({'new-1': u'foo\u1234',
2869
tt.root: ''}, tt._tree_id_paths)
2870
self.assertEqual(set(['new-1']), tt._removed_id)
2871
self.assertEqual(set(['new-2']), tt._removed_contents)
2873
def missing_records(self):
2874
attribs = self.default_attribs()
2875
attribs['_id_number'] = 2
2876
attribs['_non_present_ids'] = {
2878
return self.make_records(attribs, [])
2880
def test_serialize_missing(self):
2881
tt = self.get_preview()
2882
boo_trans_id = tt.trans_id_file_id('boo')
2883
self.assertSerializesTo(self.missing_records(), tt)
2885
def test_deserialize_missing(self):
2886
tt = self.get_preview()
2887
tt.deserialize(iter(self.missing_records()))
2888
self.assertEqual({'boo': 'new-1'}, tt._non_present_ids)
2890
def make_modification_preview(self):
2891
LINES_ONE = 'aa\nbb\ncc\ndd\n'
2892
LINES_TWO = 'z\nbb\nx\ndd\n'
2893
tree = self.make_branch_and_tree('tree')
2894
self.build_tree_contents([('tree/file', LINES_ONE)])
2895
tree.add('file', 'file-id')
2896
return self.get_preview(tree), LINES_TWO
2898
def modification_records(self):
2899
attribs = self.default_attribs()
2900
attribs['_id_number'] = 2
2901
attribs['_tree_path_ids'] = {
2904
attribs['_removed_contents'] = ['new-1']
2905
contents = [('new-1', 'file',
2906
'i 1\nz\n\nc 0 1 1 1\ni 1\nx\n\nc 0 3 3 1\n')]
2907
return self.make_records(attribs, contents)
2909
def test_serialize_modification(self):
2910
tt, LINES = self.make_modification_preview()
2911
trans_id = tt.trans_id_file_id('file-id')
2912
tt.delete_contents(trans_id)
2913
tt.create_file(LINES, trans_id)
2914
self.assertSerializesTo(self.modification_records(), tt)
2916
def test_deserialize_modification(self):
2917
tt, LINES = self.make_modification_preview()
2918
tt.deserialize(iter(self.modification_records()))
2919
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2921
def make_kind_change_preview(self):
2922
LINES = 'a\nb\nc\nd\n'
2923
tree = self.make_branch_and_tree('tree')
2924
self.build_tree(['tree/foo/'])
2925
tree.add('foo', 'foo-id')
2926
return self.get_preview(tree), LINES
2928
def kind_change_records(self):
2929
attribs = self.default_attribs()
2930
attribs['_id_number'] = 2
2931
attribs['_tree_path_ids'] = {
2934
attribs['_removed_contents'] = ['new-1']
2935
contents = [('new-1', 'file',
2936
'i 4\na\nb\nc\nd\n\n')]
2937
return self.make_records(attribs, contents)
2939
def test_serialize_kind_change(self):
2940
tt, LINES = self.make_kind_change_preview()
2941
trans_id = tt.trans_id_file_id('foo-id')
2942
tt.delete_contents(trans_id)
2943
tt.create_file(LINES, trans_id)
2944
self.assertSerializesTo(self.kind_change_records(), tt)
2946
def test_deserialize_kind_change(self):
2947
tt, LINES = self.make_kind_change_preview()
2948
tt.deserialize(iter(self.kind_change_records()))
2949
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2951
def make_add_contents_preview(self):
2952
LINES = 'a\nb\nc\nd\n'
2953
tree = self.make_branch_and_tree('tree')
2954
self.build_tree(['tree/foo'])
2956
os.unlink('tree/foo')
2957
return self.get_preview(tree), LINES
2959
def add_contents_records(self):
2960
attribs = self.default_attribs()
2961
attribs['_id_number'] = 2
2962
attribs['_tree_path_ids'] = {
2965
contents = [('new-1', 'file',
2966
'i 4\na\nb\nc\nd\n\n')]
2967
return self.make_records(attribs, contents)
2969
def test_serialize_add_contents(self):
2970
tt, LINES = self.make_add_contents_preview()
2971
trans_id = tt.trans_id_tree_path('foo')
2972
tt.create_file(LINES, trans_id)
2973
self.assertSerializesTo(self.add_contents_records(), tt)
2975
def test_deserialize_add_contents(self):
2976
tt, LINES = self.make_add_contents_preview()
2977
tt.deserialize(iter(self.add_contents_records()))
2978
self.assertFileEqual(LINES, tt._limbo_name('new-1'))
2980
def test_get_parents_lines(self):
2981
LINES_ONE = 'aa\nbb\ncc\ndd\n'
2982
LINES_TWO = 'z\nbb\nx\ndd\n'
2983
tree = self.make_branch_and_tree('tree')
2984
self.build_tree_contents([('tree/file', LINES_ONE)])
2985
tree.add('file', 'file-id')
2986
tt = self.get_preview(tree)
2987
trans_id = tt.trans_id_tree_path('file')
2988
self.assertEqual((['aa\n', 'bb\n', 'cc\n', 'dd\n'],),
2989
tt._get_parents_lines(trans_id))
2991
def test_get_parents_texts(self):
2992
LINES_ONE = 'aa\nbb\ncc\ndd\n'
2993
LINES_TWO = 'z\nbb\nx\ndd\n'
2994
tree = self.make_branch_and_tree('tree')
2995
self.build_tree_contents([('tree/file', LINES_ONE)])
2996
tree.add('file', 'file-id')
2997
tt = self.get_preview(tree)
2998
trans_id = tt.trans_id_tree_path('file')
2999
self.assertEqual((LINES_ONE,),
3000
tt._get_parents_texts(trans_id))