359
359
"""Ensure that iteration through ids works properly"""
360
360
btree = self.make_tree_1()[0]
361
361
self.assertEqual(self.sorted_ids(btree),
362
[inventory.ROOT_ID, b'a', b'b', b'c', b'd'])
362
[inventory.ROOT_ID, b'a', b'b', b'c', b'd'])
363
363
btree.note_deletion("grandparent/parent/file")
364
364
btree.note_id(b"e", "grandparent/alt_parent/fool", kind="directory")
365
365
btree.note_last_changed("grandparent/alt_parent/fool",
366
366
"revisionidiguess")
367
367
self.assertEqual(self.sorted_ids(btree),
368
[inventory.ROOT_ID, b'a', b'b', b'd', b'e'])
368
[inventory.ROOT_ID, b'a', b'b', b'd', b'e'])
371
371
class BundleTester1(tests.TestCaseWithTransport):
618
618
# Make sure we can handle files with spaces, tabs, other
619
619
# bogus characters
620
620
self.build_tree([
623
, 'b1/dir/filein subdir.c'
624
, 'b1/dir/WithCaps.txt'
625
, 'b1/dir/ pre space'
628
, 'b1/sub/sub/nonempty.txt'
621
'b1/with space.txt', 'b1/dir/', 'b1/dir/filein subdir.c', 'b1/dir/WithCaps.txt', 'b1/dir/ pre space', 'b1/sub/', 'b1/sub/sub/', 'b1/sub/sub/nonempty.txt'
630
623
self.build_tree_contents([('b1/sub/sub/emptyfile.txt', b''),
631
624
('b1/dir/nolastnewline.txt', b'bloop')])
632
625
tt = TreeTransform(self.tree1)
636
629
# a (length-prefixed) record containing it later.
637
630
self.tree1.add('with space.txt', b'withspace-id')
640
, 'dir/filein subdir.c'
643
, 'dir/nolastnewline.txt'
646
, 'sub/sub/nonempty.txt'
647
, 'sub/sub/emptyfile.txt'
632
'dir', 'dir/filein subdir.c', 'dir/WithCaps.txt', 'dir/ pre space', 'dir/nolastnewline.txt', 'sub', 'sub/sub', 'sub/sub/nonempty.txt', 'sub/sub/emptyfile.txt'
649
634
self.tree1.commit('add whitespace', rev_id=b'a@cset-0-2')
651
636
bundle = self.get_valid_bundle(b'a@cset-0-1', b'a@cset-0-2')
668
651
bundle = self.get_valid_bundle(b'a@cset-0-2', b'a@cset-0-3')
669
652
self.assertRaises((errors.TestamentMismatch,
670
errors.VersionedFileInvalidChecksum,
671
errors.BadBundle), self.get_invalid_bundle,
672
b'a@cset-0-2', b'a@cset-0-3')
653
errors.VersionedFileInvalidChecksum,
654
errors.BadBundle), self.get_invalid_bundle,
655
b'a@cset-0-2', b'a@cset-0-3')
673
656
# Check a rollup bundle
674
657
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-3')
682
665
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-4')
685
with open('b1/sub/dir/WithCaps.txt', 'ab') as f: f.write(b'\nAdding some text\n')
686
with open('b1/sub/dir/ pre space', 'ab') as f: f.write(
687
b'\r\nAdding some\r\nDOS format lines\r\n')
688
with open('b1/sub/dir/nolastnewline.txt', 'ab') as f: f.write(b'\n')
668
with open('b1/sub/dir/WithCaps.txt', 'ab') as f:
669
f.write(b'\nAdding some text\n')
670
with open('b1/sub/dir/ pre space', 'ab') as f:
672
b'\r\nAdding some\r\nDOS format lines\r\n')
673
with open('b1/sub/dir/nolastnewline.txt', 'ab') as f:
689
675
self.tree1.rename_one('sub/dir/ pre space',
690
676
'sub/ start space')
691
677
self.tree1.commit('Modified files', rev_id=b'a@cset-0-5')
725
711
if getattr(bundle, 'revision_tree', None) is not None:
726
712
# Not all bundle formats supports revision_tree
727
713
bund_tree = bundle.revision_tree(self.b1.repository, b'l@cset-0-1')
728
self.assertEqual(link_target, bund_tree.get_symlink_target(link_name))
715
link_target, bund_tree.get_symlink_target(link_name))
730
717
tt = TreeTransform(self.tree1)
731
718
trans_id = tt.trans_id_tree_path(link_name)
771
758
tt = TreeTransform(self.tree1)
774
tt.new_file('file', tt.root, [b'\x00\n\x00\r\x01\n\x02\r\xff'], b'binary-1')
761
tt.new_file('file', tt.root, [
762
b'\x00\n\x00\r\x01\n\x02\r\xff'], b'binary-1')
775
763
tt.new_file('file2', tt.root, [b'\x01\n\x02\r\x03\n\x04\r\xff'],
778
766
self.tree1.commit('add binary', rev_id=b'b@cset-0-1')
779
767
self.get_valid_bundle(b'null:', b'b@cset-0-1')
840
828
self.tree1 = self.make_branch_and_tree('b1')
841
829
self.b1 = self.tree1.branch
843
with open('b1/one', 'wb') as f: f.write(b'one\n')
831
with open('b1/one', 'wb') as f:
844
833
self.tree1.add('one')
845
834
self.tree1.commit('add file', rev_id=b'a@cset-0-1')
846
with open('b1/one', 'wb') as f: f.write(b'two\n')
835
with open('b1/one', 'wb') as f:
847
837
self.tree1.commit('modify', rev_id=b'a@cset-0-2')
848
with open('b1/one', 'wb') as f: f.write(b'three\n')
838
with open('b1/one', 'wb') as f:
849
840
self.tree1.commit('modify', rev_id=b'a@cset-0-3')
850
841
bundle_file = BytesIO()
851
842
rev_ids = write_bundle(self.tree1.branch.repository, b'a@cset-0-3',
930
920
bundle = self.get_valid_bundle(b'null:', b'white-1')
933
with open('b1/trailing space ', 'ab') as f: f.write(b'add some text\n')
923
with open('b1/trailing space ', 'ab') as f:
924
f.write(b'add some text\n')
934
925
self.tree1.commit('add text', rev_id=b'white-2')
936
927
bundle = self.get_valid_bundle(b'white-1', b'white-2')
998
989
tree.lock_write()
999
990
self.addCleanup(tree.unlock)
1000
991
tree.add([''], [b'TREE_ROOT'])
1001
tree.commit('One', revprops={u'one': 'two', u'empty': ''}, rev_id=b'rev1')
992
tree.commit('One', revprops={u'one': 'two',
993
u'empty': ''}, rev_id=b'rev1')
1002
994
self.b1 = tree.branch
1003
995
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1004
996
bundle = read_bundle(bundle_sio)
1005
997
revision_info = bundle.revisions[0]
1006
998
self.assertEqual(b'rev1', revision_info.revision_id)
1007
999
rev = revision_info.as_revision()
1008
self.assertEqual({'branch-nick':'tree', 'empty':'', 'one':'two'},
1000
self.assertEqual({'branch-nick': 'tree', 'empty': '', 'one': 'two'},
1009
1001
rev.properties)
1011
1003
def test_bundle_sorted_properties(self):
1017
1009
tree.add([''], [b'TREE_ROOT'])
1018
1010
tree.commit('One', rev_id=b'rev1',
1019
revprops={u'a':'4', u'b':'3', u'c':'2', u'd':'1'})
1011
revprops={u'a': '4', u'b': '3', u'c': '2', u'd': '1'})
1020
1012
self.b1 = tree.branch
1021
1013
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1022
1014
bundle = read_bundle(bundle_sio)
1023
1015
revision_info = bundle.revisions[0]
1024
1016
self.assertEqual(b'rev1', revision_info.revision_id)
1025
1017
rev = revision_info.as_revision()
1026
self.assertEqual({'branch-nick':'tree', 'a':'4', 'b':'3', 'c':'2',
1027
'd':'1'}, rev.properties)
1018
self.assertEqual({'branch-nick': 'tree', 'a': '4', 'b': '3', 'c': '2',
1019
'd': '1'}, rev.properties)
1029
1021
def test_bundle_unicode_properties(self):
1030
1022
"""We should be able to round trip a non-ascii property."""
1039
1031
# However, Testaments assert than they are str(), and thus should not
1041
1033
tree.commit('One', rev_id=b'rev1',
1042
revprops={u'omega':u'\u03a9', u'alpha':u'\u03b1'})
1034
revprops={u'omega': u'\u03a9', u'alpha': u'\u03b1'})
1043
1035
self.b1 = tree.branch
1044
1036
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1045
1037
bundle = read_bundle(bundle_sio)
1046
1038
revision_info = bundle.revisions[0]
1047
1039
self.assertEqual(b'rev1', revision_info.revision_id)
1048
1040
rev = revision_info.as_revision()
1049
self.assertEqual({'branch-nick':'tree', 'omega':u'\u03a9',
1050
'alpha':u'\u03b1'}, rev.properties)
1041
self.assertEqual({'branch-nick': 'tree', 'omega': u'\u03a9',
1042
'alpha': u'\u03b1'}, rev.properties)
1052
1044
def test_bundle_with_ghosts(self):
1053
1045
tree = self.make_branch_and_tree('tree')
1094
1086
root_id = inv.root.file_id
1095
1087
repo.lock_read()
1096
1088
self.addCleanup(repo.unlock)
1097
self.assertEqual({(root_id, b'rev1'):(),
1098
(root_id, b'rev2'):((root_id, b'rev1'),)},
1099
repo.texts.get_parent_map([(root_id, b'rev1'), (root_id, b'rev2')]))
1089
self.assertEqual({(root_id, b'rev1'): (),
1090
(root_id, b'rev2'): ((root_id, b'rev1'),)},
1091
repo.texts.get_parent_map([(root_id, b'rev1'), (root_id, b'rev2')]))
1101
1093
def test_inv_hash_across_serializers(self):
1102
1094
repo = self.make_repo_with_installed_revisions()
1180
1172
file_ids = set(
1181
1173
(f, r) for b, m, k, r, f in bundle.get_bundle_reader().iter_records()
1182
1174
if f is not None)
1183
self.assertEqual({(b'file2-id', b'rev3'), (b'file3-id', rev2)}, file_ids)
1176
{(b'file2-id', b'rev3'), (b'file3-id', rev2)}, file_ids)
1184
1177
bundle.install_revisions(target.branch.repository)
1194
1187
tree.lock_write()
1195
1188
self.addCleanup(tree.unlock)
1196
1189
tree.add([''], [b'TREE_ROOT'])
1197
tree.commit('One', revprops={u'one':'two', u'empty':''}, rev_id=b'rev1')
1190
tree.commit('One', revprops={u'one': 'two',
1191
u'empty': ''}, rev_id=b'rev1')
1198
1192
self.b1 = tree.branch
1199
1193
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1200
1194
self.assertContainsRe(bundle_sio.getvalue(),
1202
1196
b'# branch-nick: tree\n'
1204
1198
b'# one: two\n'
1206
1200
bundle = read_bundle(bundle_sio)
1207
1201
revision_info = bundle.revisions[0]
1208
1202
self.assertEqual(b'rev1', revision_info.revision_id)
1209
1203
rev = revision_info.as_revision()
1210
self.assertEqual({'branch-nick':'tree', 'empty':'', 'one':'two'},
1204
self.assertEqual({'branch-nick': 'tree', 'empty': '', 'one': 'two'},
1211
1205
rev.properties)
1213
1207
def get_bundle_tree(self, bundle, revision_id):
1226
1220
tree.lock_write()
1227
1221
self.addCleanup(tree.unlock)
1228
1222
tree.add([''], [b'TREE_ROOT'])
1229
tree.commit('One', revprops={u'one':'two', u'empty':''}, rev_id=b'rev1')
1223
tree.commit('One', revprops={u'one': 'two',
1224
u'empty': ''}, rev_id=b'rev1')
1230
1225
self.b1 = tree.branch
1231
1226
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1232
1227
txt = bundle_sio.getvalue()
1233
1228
loc = txt.find(b'# empty: ') + len(b'# empty:')
1234
1229
# Create a new bundle, which strips the trailing space after empty
1235
bundle_sio = BytesIO(txt[:loc] + txt[loc+1:])
1230
bundle_sio = BytesIO(txt[:loc] + txt[loc + 1:])
1237
1232
self.assertContainsRe(bundle_sio.getvalue(),
1238
1233
b'# properties:\n'
1239
1234
b'# branch-nick: tree\n'
1241
1236
b'# one: two\n'
1243
1238
bundle = read_bundle(bundle_sio)
1244
1239
revision_info = bundle.revisions[0]
1245
1240
self.assertEqual(b'rev1', revision_info.revision_id)
1246
1241
rev = revision_info.as_revision()
1247
self.assertEqual({'branch-nick':'tree', 'empty':'', 'one':'two'},
1242
self.assertEqual({'branch-nick': 'tree', 'empty': '', 'one': 'two'},
1248
1243
rev.properties)
1250
1245
def test_bundle_sorted_properties(self):
1256
1251
tree.add([''], [b'TREE_ROOT'])
1257
1252
tree.commit('One', rev_id=b'rev1',
1258
revprops={u'a':'4', u'b':'3', u'c':'2', u'd':'1'})
1253
revprops={u'a': '4', u'b': '3', u'c': '2', u'd': '1'})
1259
1254
self.b1 = tree.branch
1260
1255
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1261
1256
self.assertContainsRe(bundle_sio.getvalue(),
1265
1260
b'# branch-nick: tree\n'
1269
1264
bundle = read_bundle(bundle_sio)
1270
1265
revision_info = bundle.revisions[0]
1271
1266
self.assertEqual(b'rev1', revision_info.revision_id)
1272
1267
rev = revision_info.as_revision()
1273
self.assertEqual({'branch-nick':'tree', 'a':'4', 'b':'3', 'c':'2',
1274
'd':'1'}, rev.properties)
1268
self.assertEqual({'branch-nick': 'tree', 'a': '4', 'b': '3', 'c': '2',
1269
'd': '1'}, rev.properties)
1276
1271
def test_bundle_unicode_properties(self):
1277
1272
"""We should be able to round trip a non-ascii property."""
1286
1281
# However, Testaments assert than they are str(), and thus should not
1288
1283
tree.commit('One', rev_id=b'rev1',
1289
revprops={u'omega':u'\u03a9', u'alpha':u'\u03b1'})
1284
revprops={u'omega': u'\u03a9', u'alpha': u'\u03b1'})
1290
1285
self.b1 = tree.branch
1291
1286
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1292
1287
self.assertContainsRe(bundle_sio.getvalue(),
1294
1289
b'# alpha: \xce\xb1\n'
1295
1290
b'# branch-nick: tree\n'
1296
1291
b'# omega: \xce\xa9\n'
1298
1293
bundle = read_bundle(bundle_sio)
1299
1294
revision_info = bundle.revisions[0]
1300
1295
self.assertEqual(b'rev1', revision_info.revision_id)
1301
1296
rev = revision_info.as_revision()
1302
self.assertEqual({'branch-nick':'tree', 'omega':u'\u03a9',
1303
'alpha':u'\u03b1'}, rev.properties)
1297
self.assertEqual({'branch-nick': 'tree', 'omega': u'\u03a9',
1298
'alpha': u'\u03b1'}, rev.properties)
1306
1301
class V09BundleKnit2Tester(V08BundleTester):
1417
1413
# Turn the 'iterators_of_bytes' back into simple strings for comparison
1418
1414
repo_texts = dict((i, b''.join(content)) for i, content
1419
1415
in target_repo.iter_files_bytes(
1420
[(b'fileid-2', b'rev1', '1'),
1421
(b'fileid-2', b'rev2', '2')]))
1422
self.assertEqual({'1':b'contents1\nstatic\n',
1423
'2':b'contents2\nstatic\n'},
1416
[(b'fileid-2', b'rev1', '1'),
1417
(b'fileid-2', b'rev2', '2')]))
1418
self.assertEqual({'1': b'contents1\nstatic\n',
1419
'2': b'contents2\nstatic\n'},
1425
1421
rtree = target_repo.revision_tree(b'rev2')
1426
1422
inventory_vf = target_repo.inventories
1468
1465
serializer = BundleSerializerV4('4')
1469
1466
with tree_a.lock_read():
1470
serializer.write_bundle(tree_a.branch.repository, b'B', b'null:', s)
1467
serializer.write_bundle(
1468
tree_a.branch.repository, b'B', b'null:', s)
1472
1470
install_bundle(repo_b, serializer.read(s))
1473
1471
self.assertTrue(repo_b.has_signature_for_revision_id(b'B'))
1579
1577
self.assertEqual({b'parents': [],
1580
1578
b'sha1': b'a13f42b142d544aac9b085c42595d304150e31a2',
1581
1579
b'storage_kind': b'mpdiff',
1583
1581
# We should have an mpdiff that takes some lines from both parents.
1584
1582
self.assertEqualDiff(
1586
1584
b'<inventory format="10" revision_id="a@cset-0-1">\n'
1587
1585
b'<directory file_id="root-id" name=""'
1588
b' revision="a@cset-0-1" />\n'
1586
b' revision="a@cset-0-1" />\n'
1589
1587
b'<file file_id="file-id" name="file" parent_id="root-id"'
1590
b' revision="a@cset-0-1"'
1591
b' text_sha1="09c2f8647e14e49e922b955c194102070597c2d1"'
1592
b' text_size="17" />\n'
1588
b' revision="a@cset-0-1"'
1589
b' text_sha1="09c2f8647e14e49e922b955c194102070597c2d1"'
1590
b' text_size="17" />\n'
1593
1591
b'</inventory>\n'
1596
1594
def test_multiple_inventories_as_xml(self):
1597
1595
self.make_merged_branch()
1598
1596
sio = self.make_bundle_just_inventories(b'a@cset-0-1', b'a@cset-0-3',
1599
[b'a@cset-0-2a', b'a@cset-0-2b', b'a@cset-0-3'])
1597
[b'a@cset-0-2a', b'a@cset-0-2b', b'a@cset-0-3'])
1600
1598
reader = v4.BundleReader(sio, stream_input=False)
1601
1599
records = list(reader.iter_records())
1602
1600
self.assertEqual(3, len(records))
1607
1605
self.assertEqual({b'parents': [b'a@cset-0-1'],
1608
1606
b'sha1': b'1e105886d62d510763e22885eec733b66f5f09bf',
1609
1607
b'storage_kind': b'mpdiff',
1611
1609
metadata_2b = records[1][1]
1612
1610
self.assertEqual({b'parents': [b'a@cset-0-1'],
1613
1611
b'sha1': b'f03f12574bdb5ed2204c28636c98a8547544ccd8',
1614
1612
b'storage_kind': b'mpdiff',
1616
1614
metadata_3 = records[2][1]
1617
1615
self.assertEqual({b'parents': [b'a@cset-0-2a', b'a@cset-0-2b'],
1618
1616
b'sha1': b'09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1619
1617
b'storage_kind': b'mpdiff',
1621
1619
bytes_2a = records[0][0]
1622
1620
self.assertEqualDiff(
1655
1653
def test_creating_bundle_preserves_chk_pages(self):
1656
1654
self.make_merged_branch()
1657
1655
target = self.b1.controldir.sprout('target',
1658
revision_id=b'a@cset-0-2a').open_branch()
1656
revision_id=b'a@cset-0-2a').open_branch()
1659
1657
bundle_txt, rev_ids = self.create_bundle_text(b'a@cset-0-2a',
1661
1659
self.assertEqual(set([b'a@cset-0-2b', b'a@cset-0-3']), set(rev_ids))
1695
1693
def check_valid(self, bundle):
1696
1694
"""Check that after whatever munging, the final object is valid."""
1697
1695
self.assertEqual([b'a@cset-0-2'],
1698
[r.revision_id for r in bundle.real_revisions])
1696
[r.revision_id for r in bundle.real_revisions])
1700
1698
def test_extra_whitespace(self):
1701
1699
bundle_txt = self.build_test_bundle()
1776
1774
writer.add_info_record({b'foo': b'bar'})
1777
1775
writer._add_record(b"Record body", {b'parents': [b'1', b'3'],
1778
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1776
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1780
1778
fileobj.seek(0)
1781
1779
reader = v4.BundleReader(fileobj, stream_input=True)
1782
1780
record_iter = reader.iter_records()
1783
1781
record = next(record_iter)
1784
1782
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1785
'info', None, None), record)
1783
'info', None, None), record)
1786
1784
record = next(record_iter)
1787
1785
self.assertEqual((b"Record body", {b'storage_kind': b'fulltext',
1788
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1786
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1791
1789
def test_roundtrip_record_memory_hungry(self):
1792
1790
fileobj = BytesIO()
1795
1793
writer.add_info_record({b'foo': b'bar'})
1796
1794
writer._add_record(b"Record body", {b'parents': [b'1', b'3'],
1797
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1795
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1799
1797
fileobj.seek(0)
1800
1798
reader = v4.BundleReader(fileobj, stream_input=False)
1801
1799
record_iter = reader.iter_records()
1802
1800
record = next(record_iter)
1803
1801
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1804
'info', None, None), record)
1802
'info', None, None), record)
1805
1803
record = next(record_iter)
1806
1804
self.assertEqual((b"Record body", {b'storage_kind': b'fulltext',
1807
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1805
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1810
1808
def test_encode_name(self):
1811
1809
self.assertEqual(b'revision/rev1',
1812
v4.BundleWriter.encode_name('revision', b'rev1'))
1810
v4.BundleWriter.encode_name('revision', b'rev1'))
1813
1811
self.assertEqual(b'file/rev//1/file-id-1',
1814
v4.BundleWriter.encode_name('file', b'rev/1', b'file-id-1'))
1812
v4.BundleWriter.encode_name('file', b'rev/1', b'file-id-1'))
1815
1813
self.assertEqual(b'info',
1816
v4.BundleWriter.encode_name('info', None, None))
1814
v4.BundleWriter.encode_name('info', None, None))
1818
1816
def test_decode_name(self):
1819
1817
self.assertEqual(('revision', b'rev1', None),
1820
v4.BundleReader.decode_name(b'revision/rev1'))
1818
v4.BundleReader.decode_name(b'revision/rev1'))
1821
1819
self.assertEqual(('file', b'rev/1', b'file-id-1'),
1822
v4.BundleReader.decode_name(b'file/rev//1/file-id-1'))
1820
v4.BundleReader.decode_name(b'file/rev//1/file-id-1'))
1823
1821
self.assertEqual(('info', None, None),
1824
1822
v4.BundleReader.decode_name(b'info'))
1834
1832
record_iter = v4.BundleReader(fileobj).iter_records()
1835
1833
record = next(record_iter)
1836
1834
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1837
'info', None, None), record)
1835
'info', None, None), record)
1838
1836
self.assertRaises(errors.BadBundle, next, record_iter)