14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from cStringIO import StringIO
18
from io import BytesIO
23
import SocketServer as socketserver
31
31
revision as _mod_revision,
35
from bzrlib.bundle import read_mergeable_from_url
36
from bzrlib.bundle.apply_bundle import install_bundle, merge_bundle
37
from bzrlib.bundle.bundle_data import BundleTree
38
from bzrlib.bzrdir import BzrDir
39
from bzrlib.directory_service import directories
40
from bzrlib.bundle.serializer import write_bundle, read_bundle, v09, v4
41
from bzrlib.bundle.serializer.v08 import BundleSerializerV08
42
from bzrlib.bundle.serializer.v09 import BundleSerializerV09
43
from bzrlib.bundle.serializer.v4 import BundleSerializerV4
44
from bzrlib.branch import Branch
45
from bzrlib.repofmt import knitrepo
46
from bzrlib.tests import (
39
from ..bundle import read_mergeable_from_url
40
from ..bundle.apply_bundle import install_bundle, merge_bundle
41
from ..bundle.bundle_data import BundleTree
42
from ..directory_service import directories
43
from ..bundle.serializer import write_bundle, read_bundle, v09, v4
44
from ..bundle.serializer.v08 import BundleSerializerV08
45
from ..bundle.serializer.v09 import BundleSerializerV09
46
from ..bundle.serializer.v4 import BundleSerializerV4
47
from ..bzr import knitrepo
50
from bzrlib.transform import TreeTransform
54
from ..transform import TreeTransform
53
57
def get_text(vf, key):
54
58
"""Get the fulltext for a given revision id that is present in the vf"""
55
59
stream = vf.get_record_stream([key], 'unordered', True)
56
record = stream.next()
57
61
return record.get_bytes_as('fulltext')
172
210
self.assertEqual(btree.old_path("grandparent/parent/file"),
173
211
"grandparent/parent/file")
175
self.assertEqual(btree.id2path("a"), "grandparent")
176
self.assertEqual(btree.id2path("b"), "grandparent/parent")
177
self.assertEqual(btree.id2path("c"), "grandparent/parent/file")
179
self.assertEqual(btree.path2id("grandparent"), "a")
180
self.assertEqual(btree.path2id("grandparent/parent"), "b")
181
self.assertEqual(btree.path2id("grandparent/parent/file"), "c")
183
self.assertTrue(btree.path2id("grandparent2") is None)
184
self.assertTrue(btree.path2id("grandparent2/parent") is None)
185
self.assertTrue(btree.path2id("grandparent2/parent/file") is None)
213
self.assertEqual(btree.id2path(b"a"), "grandparent")
214
self.assertEqual(btree.id2path(b"b"), "grandparent/parent")
215
self.assertEqual(btree.id2path(b"c"), "grandparent/parent/file")
217
self.assertEqual(btree.path2id("grandparent"), b"a")
218
self.assertEqual(btree.path2id("grandparent/parent"), b"b")
219
self.assertEqual(btree.path2id("grandparent/parent/file"), b"c")
221
self.assertIs(btree.path2id("grandparent2"), None)
222
self.assertIs(btree.path2id("grandparent2/parent"), None)
223
self.assertIs(btree.path2id("grandparent2/parent/file"), None)
187
225
btree.note_rename("grandparent", "grandparent2")
188
self.assertTrue(btree.old_path("grandparent") is None)
189
self.assertTrue(btree.old_path("grandparent/parent") is None)
190
self.assertTrue(btree.old_path("grandparent/parent/file") is None)
192
self.assertEqual(btree.id2path("a"), "grandparent2")
193
self.assertEqual(btree.id2path("b"), "grandparent2/parent")
194
self.assertEqual(btree.id2path("c"), "grandparent2/parent/file")
196
self.assertEqual(btree.path2id("grandparent2"), "a")
197
self.assertEqual(btree.path2id("grandparent2/parent"), "b")
198
self.assertEqual(btree.path2id("grandparent2/parent/file"), "c")
226
self.assertIs(btree.old_path("grandparent"), None)
227
self.assertIs(btree.old_path("grandparent/parent"), None)
228
self.assertIs(btree.old_path("grandparent/parent/file"), None)
230
self.assertEqual(btree.id2path(b"a"), "grandparent2")
231
self.assertEqual(btree.id2path(b"b"), "grandparent2/parent")
232
self.assertEqual(btree.id2path(b"c"), "grandparent2/parent/file")
234
self.assertEqual(btree.path2id("grandparent2"), b"a")
235
self.assertEqual(btree.path2id("grandparent2/parent"), b"b")
236
self.assertEqual(btree.path2id("grandparent2/parent/file"), b"c")
200
238
self.assertTrue(btree.path2id("grandparent") is None)
201
239
self.assertTrue(btree.path2id("grandparent/parent") is None)
202
240
self.assertTrue(btree.path2id("grandparent/parent/file") is None)
204
242
btree.note_rename("grandparent/parent", "grandparent2/parent2")
205
self.assertEqual(btree.id2path("a"), "grandparent2")
206
self.assertEqual(btree.id2path("b"), "grandparent2/parent2")
207
self.assertEqual(btree.id2path("c"), "grandparent2/parent2/file")
243
self.assertEqual(btree.id2path(b"a"), "grandparent2")
244
self.assertEqual(btree.id2path(b"b"), "grandparent2/parent2")
245
self.assertEqual(btree.id2path(b"c"), "grandparent2/parent2/file")
209
self.assertEqual(btree.path2id("grandparent2"), "a")
210
self.assertEqual(btree.path2id("grandparent2/parent2"), "b")
211
self.assertEqual(btree.path2id("grandparent2/parent2/file"), "c")
247
self.assertEqual(btree.path2id("grandparent2"), b"a")
248
self.assertEqual(btree.path2id("grandparent2/parent2"), b"b")
249
self.assertEqual(btree.path2id("grandparent2/parent2/file"), b"c")
213
251
self.assertTrue(btree.path2id("grandparent2/parent") is None)
214
252
self.assertTrue(btree.path2id("grandparent2/parent/file") is None)
216
254
btree.note_rename("grandparent/parent/file",
217
255
"grandparent2/parent2/file2")
218
self.assertEqual(btree.id2path("a"), "grandparent2")
219
self.assertEqual(btree.id2path("b"), "grandparent2/parent2")
220
self.assertEqual(btree.id2path("c"), "grandparent2/parent2/file2")
256
self.assertEqual(btree.id2path(b"a"), "grandparent2")
257
self.assertEqual(btree.id2path(b"b"), "grandparent2/parent2")
258
self.assertEqual(btree.id2path(b"c"), "grandparent2/parent2/file2")
222
self.assertEqual(btree.path2id("grandparent2"), "a")
223
self.assertEqual(btree.path2id("grandparent2/parent2"), "b")
224
self.assertEqual(btree.path2id("grandparent2/parent2/file2"), "c")
260
self.assertEqual(btree.path2id("grandparent2"), b"a")
261
self.assertEqual(btree.path2id("grandparent2/parent2"), b"b")
262
self.assertEqual(btree.path2id("grandparent2/parent2/file2"), b"c")
226
264
self.assertTrue(btree.path2id("grandparent2/parent2/file") is None)
307
349
def test_delete(self):
308
350
"Deletion by bundle"
309
351
btree = self.make_tree_1()[0]
310
self.assertEqual(btree.get_file("c").read(), "Hello\n")
352
with btree.get_file(btree.id2path(b"c")) as f:
353
self.assertEqual(f.read(), b"Hello\n")
311
354
btree.note_deletion("grandparent/parent/file")
312
self.assertTrue(btree.id2path("c") is None)
313
self.assertTrue(btree.path2id("grandparent/parent/file") is None)
355
self.assertTrue(btree.id2path(b"c") is None)
356
self.assertFalse(btree.is_versioned("grandparent/parent/file"))
315
358
def sorted_ids(self, tree):
359
ids = sorted(tree.all_file_ids())
320
362
def test_iteration(self):
321
363
"""Ensure that iteration through ids works properly"""
322
364
btree = self.make_tree_1()[0]
323
365
self.assertEqual(self.sorted_ids(btree),
324
[inventory.ROOT_ID, 'a', 'b', 'c', 'd'])
366
[inventory.ROOT_ID, b'a', b'b', b'c', b'd'])
325
367
btree.note_deletion("grandparent/parent/file")
326
btree.note_id("e", "grandparent/alt_parent/fool", kind="directory")
368
btree.note_id(b"e", "grandparent/alt_parent/fool", kind="directory")
327
369
btree.note_last_changed("grandparent/alt_parent/fool",
328
370
"revisionidiguess")
329
371
self.assertEqual(self.sorted_ids(btree),
330
[inventory.ROOT_ID, 'a', 'b', 'd', 'e'])
372
[inventory.ROOT_ID, b'a', b'b', b'd', b'e'])
333
375
class BundleTester1(tests.TestCaseWithTransport):
628
667
tt = TreeTransform(self.tree1)
629
trans_id = tt.trans_id_tree_file_id('exe-1')
668
trans_id = tt.trans_id_tree_path('executable')
630
669
tt.set_executability(False, trans_id)
632
self.tree1.commit('removed', rev_id='a@cset-0-3')
671
self.tree1.commit('removed', rev_id=b'a@cset-0-3')
634
bundle = self.get_valid_bundle('a@cset-0-2', 'a@cset-0-3')
673
bundle = self.get_valid_bundle(b'a@cset-0-2', b'a@cset-0-3')
635
674
self.assertRaises((errors.TestamentMismatch,
636
675
errors.VersionedFileInvalidChecksum,
637
676
errors.BadBundle), self.get_invalid_bundle,
638
'a@cset-0-2', 'a@cset-0-3')
677
b'a@cset-0-2', b'a@cset-0-3')
639
678
# Check a rollup bundle
640
bundle = self.get_valid_bundle('null:', 'a@cset-0-3')
679
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-3')
642
681
# Now move the directory
643
682
self.tree1.rename_one('dir', 'sub/dir')
644
self.tree1.commit('rename dir', rev_id='a@cset-0-4')
683
self.tree1.commit('rename dir', rev_id=b'a@cset-0-4')
646
bundle = self.get_valid_bundle('a@cset-0-3', 'a@cset-0-4')
685
bundle = self.get_valid_bundle(b'a@cset-0-3', b'a@cset-0-4')
647
686
# Check a rollup bundle
648
bundle = self.get_valid_bundle('null:', 'a@cset-0-4')
687
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-4')
651
open('b1/sub/dir/WithCaps.txt', 'ab').write('\nAdding some text\n')
652
open('b1/sub/dir/ pre space', 'ab').write(
653
'\r\nAdding some\r\nDOS format lines\r\n')
654
open('b1/sub/dir/nolastnewline.txt', 'ab').write('\n')
690
with open('b1/sub/dir/WithCaps.txt', 'ab') as f: f.write(b'\nAdding some text\n')
691
with open('b1/sub/dir/ pre space', 'ab') as f: f.write(
692
b'\r\nAdding some\r\nDOS format lines\r\n')
693
with open('b1/sub/dir/nolastnewline.txt', 'ab') as f: f.write(b'\n')
655
694
self.tree1.rename_one('sub/dir/ pre space',
656
695
'sub/ start space')
657
self.tree1.commit('Modified files', rev_id='a@cset-0-5')
658
bundle = self.get_valid_bundle('a@cset-0-4', 'a@cset-0-5')
696
self.tree1.commit('Modified files', rev_id=b'a@cset-0-5')
697
bundle = self.get_valid_bundle(b'a@cset-0-4', b'a@cset-0-5')
660
699
self.tree1.rename_one('sub/dir/WithCaps.txt', 'temp')
661
700
self.tree1.rename_one('with space.txt', 'WithCaps.txt')
662
701
self.tree1.rename_one('temp', 'with space.txt')
663
self.tree1.commit(u'swap filenames', rev_id='a@cset-0-6',
702
self.tree1.commit(u'swap filenames', rev_id=b'a@cset-0-6',
665
bundle = self.get_valid_bundle('a@cset-0-5', 'a@cset-0-6')
666
other = self.get_checkout('a@cset-0-5')
704
bundle = self.get_valid_bundle(b'a@cset-0-5', b'a@cset-0-6')
705
other = self.get_checkout(b'a@cset-0-5')
667
706
tree1_inv = get_inventory_text(self.tree1.branch.repository,
669
708
tree2_inv = get_inventory_text(other.branch.repository,
671
710
self.assertEqualDiff(tree1_inv, tree2_inv)
672
711
other.rename_one('sub/dir/nolastnewline.txt', 'sub/nolastnewline.txt')
673
other.commit('rename file', rev_id='a@cset-0-6b')
712
other.commit('rename file', rev_id=b'a@cset-0-6b')
674
713
self.tree1.merge_from_branch(other.branch)
675
self.tree1.commit(u'Merge', rev_id='a@cset-0-7',
714
self.tree1.commit(u'Merge', rev_id=b'a@cset-0-7',
677
bundle = self.get_valid_bundle('a@cset-0-6', 'a@cset-0-7')
716
bundle = self.get_valid_bundle(b'a@cset-0-6', b'a@cset-0-7')
679
718
def _test_symlink_bundle(self, link_name, link_target, new_link_target):
682
self.requireFeature(tests.SymlinkFeature)
721
self.requireFeature(features.SymlinkFeature)
683
722
self.tree1 = self.make_branch_and_tree('b1')
684
723
self.b1 = self.tree1.branch
686
725
tt = TreeTransform(self.tree1)
687
726
tt.new_symlink(link_name, tt.root, link_target, link_id)
689
self.tree1.commit('add symlink', rev_id='l@cset-0-1')
690
bundle = self.get_valid_bundle('null:', 'l@cset-0-1')
691
if getattr(bundle ,'revision_tree', None) is not None:
728
self.tree1.commit('add symlink', rev_id=b'l@cset-0-1')
729
bundle = self.get_valid_bundle(b'null:', b'l@cset-0-1')
730
if getattr(bundle, 'revision_tree', None) is not None:
692
731
# Not all bundle formats supports revision_tree
693
bund_tree = bundle.revision_tree(self.b1.repository, 'l@cset-0-1')
694
self.assertEqual(link_target, bund_tree.get_symlink_target(link_id))
732
bund_tree = bundle.revision_tree(self.b1.repository, b'l@cset-0-1')
733
self.assertEqual(link_target, bund_tree.get_symlink_target(link_name))
696
735
tt = TreeTransform(self.tree1)
697
trans_id = tt.trans_id_tree_file_id(link_id)
736
trans_id = tt.trans_id_tree_path(link_name)
698
737
tt.adjust_path('link2', tt.root, trans_id)
699
738
tt.delete_contents(trans_id)
700
739
tt.create_symlink(new_link_target, trans_id)
702
self.tree1.commit('rename and change symlink', rev_id='l@cset-0-2')
703
bundle = self.get_valid_bundle('l@cset-0-1', 'l@cset-0-2')
704
if getattr(bundle ,'revision_tree', None) is not None:
741
self.tree1.commit('rename and change symlink', rev_id=b'l@cset-0-2')
742
bundle = self.get_valid_bundle(b'l@cset-0-1', b'l@cset-0-2')
743
if getattr(bundle, 'revision_tree', None) is not None:
705
744
# Not all bundle formats supports revision_tree
706
bund_tree = bundle.revision_tree(self.b1.repository, 'l@cset-0-2')
745
bund_tree = bundle.revision_tree(self.b1.repository, b'l@cset-0-2')
707
746
self.assertEqual(new_link_target,
708
bund_tree.get_symlink_target(link_id))
747
bund_tree.get_symlink_target('link2'))
710
749
tt = TreeTransform(self.tree1)
711
trans_id = tt.trans_id_tree_file_id(link_id)
750
trans_id = tt.trans_id_tree_path('link2')
712
751
tt.delete_contents(trans_id)
713
752
tt.create_symlink('jupiter', trans_id)
715
self.tree1.commit('just change symlink target', rev_id='l@cset-0-3')
716
bundle = self.get_valid_bundle('l@cset-0-2', 'l@cset-0-3')
754
self.tree1.commit('just change symlink target', rev_id=b'l@cset-0-3')
755
bundle = self.get_valid_bundle(b'l@cset-0-2', b'l@cset-0-3')
718
757
tt = TreeTransform(self.tree1)
719
trans_id = tt.trans_id_tree_file_id(link_id)
758
trans_id = tt.trans_id_tree_path('link2')
720
759
tt.delete_contents(trans_id)
722
self.tree1.commit('Delete symlink', rev_id='l@cset-0-4')
723
bundle = self.get_valid_bundle('l@cset-0-3', 'l@cset-0-4')
761
self.tree1.commit('Delete symlink', rev_id=b'l@cset-0-4')
762
bundle = self.get_valid_bundle(b'l@cset-0-3', b'l@cset-0-4')
725
764
def test_symlink_bundle(self):
726
765
self._test_symlink_bundle('link', 'bar/foo', 'mars')
728
767
def test_unicode_symlink_bundle(self):
729
self.requireFeature(tests.UnicodeFilenameFeature)
768
self.requireFeature(features.UnicodeFilenameFeature)
730
769
self._test_symlink_bundle(u'\N{Euro Sign}link',
731
770
u'bar/\N{Euro Sign}foo',
732
771
u'mars\N{Euro Sign}')
737
776
tt = TreeTransform(self.tree1)
740
tt.new_file('file', tt.root, '\x00\n\x00\r\x01\n\x02\r\xff', 'binary-1')
741
tt.new_file('file2', tt.root, '\x01\n\x02\r\x03\n\x04\r\xff',
779
tt.new_file('file', tt.root, [b'\x00\n\x00\r\x01\n\x02\r\xff'], b'binary-1')
780
tt.new_file('file2', tt.root, [b'\x01\n\x02\r\x03\n\x04\r\xff'],
744
self.tree1.commit('add binary', rev_id='b@cset-0-1')
745
self.get_valid_bundle('null:', 'b@cset-0-1')
783
self.tree1.commit('add binary', rev_id=b'b@cset-0-1')
784
self.get_valid_bundle(b'null:', b'b@cset-0-1')
748
787
tt = TreeTransform(self.tree1)
749
trans_id = tt.trans_id_tree_file_id('binary-1')
788
trans_id = tt.trans_id_tree_path('file')
750
789
tt.delete_contents(trans_id)
752
self.tree1.commit('delete binary', rev_id='b@cset-0-2')
753
self.get_valid_bundle('b@cset-0-1', 'b@cset-0-2')
791
self.tree1.commit('delete binary', rev_id=b'b@cset-0-2')
792
self.get_valid_bundle(b'b@cset-0-1', b'b@cset-0-2')
755
794
# Rename & modify
756
795
tt = TreeTransform(self.tree1)
757
trans_id = tt.trans_id_tree_file_id('binary-2')
796
trans_id = tt.trans_id_tree_path('file2')
758
797
tt.adjust_path('file3', tt.root, trans_id)
759
798
tt.delete_contents(trans_id)
760
tt.create_file('file\rcontents\x00\n\x00', trans_id)
799
tt.create_file([b'file\rcontents\x00\n\x00'], trans_id)
762
self.tree1.commit('rename and modify binary', rev_id='b@cset-0-3')
763
self.get_valid_bundle('b@cset-0-2', 'b@cset-0-3')
801
self.tree1.commit('rename and modify binary', rev_id=b'b@cset-0-3')
802
self.get_valid_bundle(b'b@cset-0-2', b'b@cset-0-3')
766
805
tt = TreeTransform(self.tree1)
767
trans_id = tt.trans_id_tree_file_id('binary-2')
806
trans_id = tt.trans_id_tree_path('file3')
768
807
tt.delete_contents(trans_id)
769
tt.create_file('\x00file\rcontents', trans_id)
808
tt.create_file([b'\x00file\rcontents'], trans_id)
771
self.tree1.commit('just modify binary', rev_id='b@cset-0-4')
772
self.get_valid_bundle('b@cset-0-3', 'b@cset-0-4')
810
self.tree1.commit('just modify binary', rev_id=b'b@cset-0-4')
811
self.get_valid_bundle(b'b@cset-0-3', b'b@cset-0-4')
775
self.get_valid_bundle('null:', 'b@cset-0-4')
814
self.get_valid_bundle(b'null:', b'b@cset-0-4')
777
816
def test_last_modified(self):
778
817
self.tree1 = self.make_branch_and_tree('b1')
779
818
self.b1 = self.tree1.branch
780
819
tt = TreeTransform(self.tree1)
781
tt.new_file('file', tt.root, 'file', 'file')
820
tt.new_file('file', tt.root, [b'file'], b'file')
783
self.tree1.commit('create file', rev_id='a@lmod-0-1')
822
self.tree1.commit('create file', rev_id=b'a@lmod-0-1')
785
824
tt = TreeTransform(self.tree1)
786
trans_id = tt.trans_id_tree_file_id('file')
825
trans_id = tt.trans_id_tree_path('file')
787
826
tt.delete_contents(trans_id)
788
tt.create_file('file2', trans_id)
827
tt.create_file([b'file2'], trans_id)
790
self.tree1.commit('modify text', rev_id='a@lmod-0-2a')
829
self.tree1.commit('modify text', rev_id=b'a@lmod-0-2a')
792
other = self.get_checkout('a@lmod-0-1')
831
other = self.get_checkout(b'a@lmod-0-1')
793
832
tt = TreeTransform(other)
794
trans_id = tt.trans_id_tree_file_id('file')
833
trans_id = tt.trans_id_tree_path('file2')
795
834
tt.delete_contents(trans_id)
796
tt.create_file('file2', trans_id)
835
tt.create_file([b'file2'], trans_id)
798
other.commit('modify text in another tree', rev_id='a@lmod-0-2b')
837
other.commit('modify text in another tree', rev_id=b'a@lmod-0-2b')
799
838
self.tree1.merge_from_branch(other.branch)
800
self.tree1.commit(u'Merge', rev_id='a@lmod-0-3',
839
self.tree1.commit(u'Merge', rev_id=b'a@lmod-0-3',
802
self.tree1.commit(u'Merge', rev_id='a@lmod-0-4')
803
bundle = self.get_valid_bundle('a@lmod-0-2a', 'a@lmod-0-4')
841
self.tree1.commit(u'Merge', rev_id=b'a@lmod-0-4')
842
bundle = self.get_valid_bundle(b'a@lmod-0-2a', b'a@lmod-0-4')
805
844
def test_hide_history(self):
806
845
self.tree1 = self.make_branch_and_tree('b1')
807
846
self.b1 = self.tree1.branch
809
open('b1/one', 'wb').write('one\n')
848
with open('b1/one', 'wb') as f: f.write(b'one\n')
810
849
self.tree1.add('one')
811
self.tree1.commit('add file', rev_id='a@cset-0-1')
812
open('b1/one', 'wb').write('two\n')
813
self.tree1.commit('modify', rev_id='a@cset-0-2')
814
open('b1/one', 'wb').write('three\n')
815
self.tree1.commit('modify', rev_id='a@cset-0-3')
816
bundle_file = StringIO()
817
rev_ids = write_bundle(self.tree1.branch.repository, 'a@cset-0-3',
818
'a@cset-0-1', bundle_file, format=self.format)
819
self.assertNotContainsRe(bundle_file.getvalue(), '\btwo\b')
820
self.assertContainsRe(self.get_raw(bundle_file), 'one')
821
self.assertContainsRe(self.get_raw(bundle_file), 'three')
850
self.tree1.commit('add file', rev_id=b'a@cset-0-1')
851
with open('b1/one', 'wb') as f: f.write(b'two\n')
852
self.tree1.commit('modify', rev_id=b'a@cset-0-2')
853
with open('b1/one', 'wb') as f: f.write(b'three\n')
854
self.tree1.commit('modify', rev_id=b'a@cset-0-3')
855
bundle_file = BytesIO()
856
rev_ids = write_bundle(self.tree1.branch.repository, b'a@cset-0-3',
857
b'a@cset-0-1', bundle_file, format=self.format)
858
self.assertNotContainsRe(bundle_file.getvalue(), b'\btwo\b')
859
self.assertContainsRe(self.get_raw(bundle_file), b'one')
860
self.assertContainsRe(self.get_raw(bundle_file), b'three')
823
862
def test_bundle_same_basis(self):
824
863
"""Ensure using the basis as the target doesn't cause an error"""
825
864
self.tree1 = self.make_branch_and_tree('b1')
826
self.tree1.commit('add file', rev_id='a@cset-0-1')
827
bundle_file = StringIO()
828
rev_ids = write_bundle(self.tree1.branch.repository, 'a@cset-0-1',
829
'a@cset-0-1', bundle_file)
865
self.tree1.commit('add file', rev_id=b'a@cset-0-1')
866
bundle_file = BytesIO()
867
rev_ids = write_bundle(self.tree1.branch.repository, b'a@cset-0-1',
868
b'a@cset-0-1', bundle_file)
832
871
def get_raw(bundle_file):
833
872
return bundle_file.getvalue()
835
874
def test_unicode_bundle(self):
836
self.requireFeature(tests.UnicodeFilenameFeature)
875
self.requireFeature(features.UnicodeFilenameFeature)
837
876
# Handle international characters
839
878
f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
846
885
u'William Dod\xe9\n').encode('utf-8'))
849
self.tree1.add([u'with Dod\N{Euro Sign}'], ['withdod-id'])
888
self.tree1.add([u'with Dod\N{Euro Sign}'], [b'withdod-id'])
850
889
self.tree1.commit(u'i18n commit from William Dod\xe9',
851
rev_id='i18n-1', committer=u'William Dod\xe9')
890
rev_id=b'i18n-1', committer=u'William Dod\xe9')
854
bundle = self.get_valid_bundle('null:', 'i18n-1')
893
bundle = self.get_valid_bundle(b'null:', b'i18n-1')
857
896
f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
858
897
f.write(u'Modified \xb5\n'.encode('utf8'))
860
self.tree1.commit(u'modified', rev_id='i18n-2')
899
self.tree1.commit(u'modified', rev_id=b'i18n-2')
862
bundle = self.get_valid_bundle('i18n-1', 'i18n-2')
901
bundle = self.get_valid_bundle(b'i18n-1', b'i18n-2')
865
904
self.tree1.rename_one(u'with Dod\N{Euro Sign}', u'B\N{Euro Sign}gfors')
866
self.tree1.commit(u'renamed, the new i18n man', rev_id='i18n-3',
905
self.tree1.commit(u'renamed, the new i18n man', rev_id=b'i18n-3',
867
906
committer=u'Erik B\xe5gfors')
869
bundle = self.get_valid_bundle('i18n-2', 'i18n-3')
908
bundle = self.get_valid_bundle(b'i18n-2', b'i18n-3')
872
911
self.tree1.remove([u'B\N{Euro Sign}gfors'])
873
self.tree1.commit(u'removed', rev_id='i18n-4')
912
self.tree1.commit(u'removed', rev_id=b'i18n-4')
875
bundle = self.get_valid_bundle('i18n-3', 'i18n-4')
914
bundle = self.get_valid_bundle(b'i18n-3', b'i18n-4')
878
bundle = self.get_valid_bundle('null:', 'i18n-4')
917
bundle = self.get_valid_bundle(b'null:', b'i18n-4')
881
920
def test_whitespace_bundle(self):
891
930
# once we actually support them
894
self.tree1.commit('funky whitespace', rev_id='white-1')
933
self.tree1.commit('funky whitespace', rev_id=b'white-1')
896
bundle = self.get_valid_bundle('null:', 'white-1')
935
bundle = self.get_valid_bundle(b'null:', b'white-1')
899
open('b1/trailing space ', 'ab').write('add some text\n')
900
self.tree1.commit('add text', rev_id='white-2')
938
with open('b1/trailing space ', 'ab') as f: f.write(b'add some text\n')
939
self.tree1.commit('add text', rev_id=b'white-2')
902
bundle = self.get_valid_bundle('white-1', 'white-2')
941
bundle = self.get_valid_bundle(b'white-1', b'white-2')
905
944
self.tree1.rename_one('trailing space ', ' start and end space ')
906
self.tree1.commit('rename', rev_id='white-3')
945
self.tree1.commit('rename', rev_id=b'white-3')
908
bundle = self.get_valid_bundle('white-2', 'white-3')
947
bundle = self.get_valid_bundle(b'white-2', b'white-3')
911
950
self.tree1.remove([' start and end space '])
912
self.tree1.commit('removed', rev_id='white-4')
951
self.tree1.commit('removed', rev_id=b'white-4')
914
bundle = self.get_valid_bundle('white-3', 'white-4')
953
bundle = self.get_valid_bundle(b'white-3', b'white-4')
916
955
# Now test a complet roll-up
917
bundle = self.get_valid_bundle('null:', 'white-4')
956
bundle = self.get_valid_bundle(b'null:', b'white-4')
919
958
def test_alt_timezone_bundle(self):
920
959
self.tree1 = self.make_branch_and_memory_tree('b1')
941
980
def test_bundle_root_id(self):
942
981
self.tree1 = self.make_branch_and_tree('b1')
943
982
self.b1 = self.tree1.branch
944
self.tree1.commit('message', rev_id='revid1')
945
bundle = self.get_valid_bundle('null:', 'revid1')
946
tree = self.get_bundle_tree(bundle, 'revid1')
947
self.assertEqual('revid1', tree.inventory.root.revision)
983
self.tree1.commit('message', rev_id=b'revid1')
984
bundle = self.get_valid_bundle(b'null:', b'revid1')
985
tree = self.get_bundle_tree(bundle, b'revid1')
986
root_revision = tree.get_file_revision(u'', tree.get_root_id())
987
self.assertEqual(b'revid1', root_revision)
949
989
def test_install_revisions(self):
950
990
self.tree1 = self.make_branch_and_tree('b1')
951
991
self.b1 = self.tree1.branch
952
self.tree1.commit('message', rev_id='rev2a')
953
bundle = self.get_valid_bundle('null:', 'rev2a')
992
self.tree1.commit('message', rev_id=b'rev2a')
993
bundle = self.get_valid_bundle(b'null:', b'rev2a')
954
994
branch2 = self.make_branch('b2')
955
self.assertFalse(branch2.repository.has_revision('rev2a'))
995
self.assertFalse(branch2.repository.has_revision(b'rev2a'))
956
996
target_revision = bundle.install_revisions(branch2.repository)
957
self.assertTrue(branch2.repository.has_revision('rev2a'))
958
self.assertEqual('rev2a', target_revision)
997
self.assertTrue(branch2.repository.has_revision(b'rev2a'))
998
self.assertEqual(b'rev2a', target_revision)
960
1000
def test_bundle_empty_property(self):
961
1001
"""Test serializing revision properties with an empty value."""
962
1002
tree = self.make_branch_and_memory_tree('tree')
963
1003
tree.lock_write()
964
1004
self.addCleanup(tree.unlock)
965
tree.add([''], ['TREE_ROOT'])
966
tree.commit('One', revprops={'one':'two', 'empty':''}, rev_id='rev1')
1005
tree.add([''], [b'TREE_ROOT'])
1006
tree.commit('One', revprops={u'one': 'two', u'empty': ''}, rev_id=b'rev1')
967
1007
self.b1 = tree.branch
968
bundle_sio, revision_ids = self.create_bundle_text('null:', 'rev1')
1008
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
969
1009
bundle = read_bundle(bundle_sio)
970
1010
revision_info = bundle.revisions[0]
971
self.assertEqual('rev1', revision_info.revision_id)
1011
self.assertEqual(b'rev1', revision_info.revision_id)
972
1012
rev = revision_info.as_revision()
973
1013
self.assertEqual({'branch-nick':'tree', 'empty':'', 'one':'two'},
1035
1075
def test_across_serializers(self):
1036
1076
tree = self.make_simple_tree('knit')
1037
tree.commit('hello', rev_id='rev1')
1038
tree.commit('hello', rev_id='rev2')
1039
bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
1077
tree.commit('hello', rev_id=b'rev1')
1078
tree.commit('hello', rev_id=b'rev2')
1079
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev2')[0])
1040
1080
repo = self.make_repository('repo', format='dirstate-with-subtree')
1041
1081
bundle.install_revisions(repo)
1042
inv_text = repo._get_inventory_xml('rev2')
1043
self.assertNotContainsRe(inv_text, 'format="5"')
1044
self.assertContainsRe(inv_text, 'format="7"')
1082
inv_text = repo._get_inventory_xml(b'rev2')
1083
self.assertNotContainsRe(inv_text, b'format="5"')
1084
self.assertContainsRe(inv_text, b'format="7"')
1046
1086
def make_repo_with_installed_revisions(self):
1047
1087
tree = self.make_simple_tree('knit')
1048
tree.commit('hello', rev_id='rev1')
1049
tree.commit('hello', rev_id='rev2')
1050
bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
1088
tree.commit('hello', rev_id=b'rev1')
1089
tree.commit('hello', rev_id=b'rev2')
1090
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev2')[0])
1051
1091
repo = self.make_repository('repo', format='dirstate-with-subtree')
1052
1092
bundle.install_revisions(repo)
1055
1095
def test_across_models(self):
1056
1096
repo = self.make_repo_with_installed_revisions()
1057
inv = repo.get_inventory('rev2')
1058
self.assertEqual('rev2', inv.root.revision)
1097
inv = repo.get_inventory(b'rev2')
1098
self.assertEqual(b'rev2', inv.root.revision)
1059
1099
root_id = inv.root.file_id
1060
1100
repo.lock_read()
1061
1101
self.addCleanup(repo.unlock)
1062
self.assertEqual({(root_id, 'rev1'):(),
1063
(root_id, 'rev2'):((root_id, 'rev1'),)},
1064
repo.texts.get_parent_map([(root_id, 'rev1'), (root_id, 'rev2')]))
1102
self.assertEqual({(root_id, b'rev1'):(),
1103
(root_id, b'rev2'):((root_id, b'rev1'),)},
1104
repo.texts.get_parent_map([(root_id, b'rev1'), (root_id, b'rev2')]))
1066
1106
def test_inv_hash_across_serializers(self):
1067
1107
repo = self.make_repo_with_installed_revisions()
1068
recorded_inv_sha1 = repo.get_revision('rev2').inventory_sha1
1069
xml = repo._get_inventory_xml('rev2')
1108
recorded_inv_sha1 = repo.get_revision(b'rev2').inventory_sha1
1109
xml = repo._get_inventory_xml(b'rev2')
1070
1110
self.assertEqual(osutils.sha_string(xml), recorded_inv_sha1)
1072
1112
def test_across_models_incompatible(self):
1073
1113
tree = self.make_simple_tree('dirstate-with-subtree')
1074
tree.commit('hello', rev_id='rev1')
1075
tree.commit('hello', rev_id='rev2')
1114
tree.commit('hello', rev_id=b'rev1')
1115
tree.commit('hello', rev_id=b'rev2')
1077
bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
1117
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev1')[0])
1078
1118
except errors.IncompatibleBundleFormat:
1079
1119
raise tests.TestSkipped("Format 0.8 doesn't work with knit3")
1080
1120
repo = self.make_repository('repo', format='knit')
1081
1121
bundle.install_revisions(repo)
1083
bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
1123
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev2')[0])
1084
1124
self.assertRaises(errors.IncompatibleRevision,
1085
1125
bundle.install_revisions, repo)
1087
1127
def test_get_merge_request(self):
1088
1128
tree = self.make_simple_tree()
1089
tree.commit('hello', rev_id='rev1')
1090
tree.commit('hello', rev_id='rev2')
1091
bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
1129
tree.commit('hello', rev_id=b'rev1')
1130
tree.commit('hello', rev_id=b'rev2')
1131
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev1')[0])
1092
1132
result = bundle.get_merge_request(tree.branch.repository)
1093
self.assertEqual((None, 'rev1', 'inapplicable'), result)
1133
self.assertEqual((None, b'rev1', 'inapplicable'), result)
1095
1135
def test_with_subtree(self):
1096
1136
tree = self.make_branch_and_tree('tree',
1116
1156
self.tree1 = self.make_branch_and_tree('tree')
1117
1157
self.b1 = self.tree1.branch
1119
self.tree1.commit('Revision/id/with/slashes', rev_id='rev/id')
1159
self.tree1.commit('Revision/id/with/slashes', rev_id=b'rev/id')
1120
1160
except ValueError:
1121
1161
raise tests.TestSkipped(
1122
1162
"Repository doesn't support revision ids with slashes")
1123
bundle = self.get_valid_bundle('null:', 'rev/id')
1163
bundle = self.get_valid_bundle(b'null:', b'rev/id')
1125
1165
def test_skip_file(self):
1126
1166
"""Make sure we don't accidentally write to the wrong versionedfile"""
1127
1167
self.tree1 = self.make_branch_and_tree('tree')
1128
1168
self.b1 = self.tree1.branch
1129
1169
# rev1 is not present in bundle, done by fetch
1130
self.build_tree_contents([('tree/file2', 'contents1')])
1131
self.tree1.add('file2', 'file2-id')
1132
self.tree1.commit('rev1', rev_id='reva')
1133
self.build_tree_contents([('tree/file3', 'contents2')])
1170
self.build_tree_contents([('tree/file2', b'contents1')])
1171
self.tree1.add('file2', b'file2-id')
1172
self.tree1.commit('rev1', rev_id=b'reva')
1173
self.build_tree_contents([('tree/file3', b'contents2')])
1134
1174
# rev2 is present in bundle, and done by fetch
1135
1175
# having file1 in the bunle causes file1's versionedfile to be opened.
1136
self.tree1.add('file3', 'file3-id')
1176
self.tree1.add('file3', b'file3-id')
1137
1177
self.tree1.commit('rev2')
1138
1178
# Updating file2 should not cause an attempt to add to file1's vf
1139
target = self.tree1.bzrdir.sprout('target').open_workingtree()
1140
self.build_tree_contents([('tree/file2', 'contents3')])
1141
self.tree1.commit('rev3', rev_id='rev3')
1142
bundle = self.get_valid_bundle('reva', 'rev3')
1179
target = self.tree1.controldir.sprout('target').open_workingtree()
1180
self.build_tree_contents([('tree/file2', b'contents3')])
1181
self.tree1.commit('rev3', rev_id=b'rev3')
1182
bundle = self.get_valid_bundle(b'reva', b'rev3')
1143
1183
if getattr(bundle, 'get_bundle_reader', None) is None:
1144
1184
raise tests.TestSkipped('Bundle format cannot provide reader')
1145
1185
# be sure that file1 comes before file2
1146
1186
for b, m, k, r, f in bundle.get_bundle_reader().iter_records():
1187
if f == b'file3-id':
1149
self.assertNotEqual(f, 'file2-id')
1189
self.assertNotEqual(f, b'file2-id')
1150
1190
bundle.install_revisions(target.branch.repository)
1332
1372
:return: The in-memory bundle
1334
from bzrlib.bundle import serializer
1374
from ..bundle import serializer
1335
1375
bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
1336
new_text = self.get_raw(StringIO(''.join(bundle_txt)))
1337
new_text = new_text.replace('<file file_id="exe-1"',
1338
'<file executable="y" file_id="exe-1"')
1339
new_text = new_text.replace('B260', 'B275')
1340
bundle_txt = StringIO()
1376
new_text = self.get_raw(BytesIO(b''.join(bundle_txt)))
1377
new_text = new_text.replace(b'<file file_id="exe-1"',
1378
b'<file executable="y" file_id="exe-1"')
1379
new_text = new_text.replace(b'B260', b'B275')
1380
bundle_txt = BytesIO()
1341
1381
bundle_txt.write(serializer._get_bundle_header('4'))
1342
bundle_txt.write('\n')
1343
bundle_txt.write(new_text.encode('bz2'))
1382
bundle_txt.write(b'\n')
1383
bundle_txt.write(bz2.compress(new_text))
1344
1384
bundle_txt.seek(0)
1345
1385
bundle = read_bundle(bundle_txt)
1346
1386
self.valid_apply_bundle(base_rev_id, bundle)
1349
1389
def create_bundle_text(self, base_rev_id, rev_id):
1350
bundle_txt = StringIO()
1390
bundle_txt = BytesIO()
1351
1391
rev_ids = write_bundle(self.b1.repository, rev_id, base_rev_id,
1352
1392
bundle_txt, format=self.format)
1353
1393
bundle_txt.seek(0)
1354
1394
self.assertEqual(bundle_txt.readline(),
1355
'# Bazaar revision bundle v%s\n' % self.format)
1356
self.assertEqual(bundle_txt.readline(), '#\n')
1395
b'# Bazaar revision bundle v%s\n' % self.format.encode('ascii'))
1396
self.assertEqual(bundle_txt.readline(), b'#\n')
1357
1397
rev = self.b1.repository.get_revision(rev_id)
1358
1398
bundle_txt.seek(0)
1359
1399
return bundle_txt, rev_ids
1380
1420
target_repo.lock_read()
1381
1421
self.addCleanup(target_repo.unlock)
1382
1422
# Turn the 'iterators_of_bytes' back into simple strings for comparison
1383
repo_texts = dict((i, ''.join(content)) for i, content
1423
repo_texts = dict((i, b''.join(content)) for i, content
1384
1424
in target_repo.iter_files_bytes(
1385
[('fileid-2', 'rev1', '1'),
1386
('fileid-2', 'rev2', '2')]))
1387
self.assertEqual({'1':'contents1\nstatic\n',
1388
'2':'contents2\nstatic\n'},
1425
[(b'fileid-2', b'rev1', '1'),
1426
(b'fileid-2', b'rev2', '2')]))
1427
self.assertEqual({'1':b'contents1\nstatic\n',
1428
'2':b'contents2\nstatic\n'},
1390
rtree = target_repo.revision_tree('rev2')
1430
rtree = target_repo.revision_tree(b'rev2')
1391
1431
inventory_vf = target_repo.inventories
1392
1432
# If the inventory store has a graph, it must match the revision graph.
1393
1433
self.assertSubset(
1394
[inventory_vf.get_parent_map([('rev2',)])[('rev2',)]],
1395
[None, (('rev1',),)])
1434
[inventory_vf.get_parent_map([(b'rev2',)])[(b'rev2',)]],
1435
[None, ((b'rev1',),)])
1396
1436
self.assertEqual('changed file',
1397
target_repo.get_revision('rev2').message)
1437
target_repo.get_revision(b'rev2').message)
1400
1440
def get_raw(bundle_file):
1402
1442
line = bundle_file.readline()
1403
1443
line = bundle_file.readline()
1404
1444
lines = bundle_file.readlines()
1405
return ''.join(lines).decode('bz2')
1445
return bz2.decompress(b''.join(lines))
1407
1447
def test_copy_signatures(self):
1408
1448
tree_a = self.make_branch_and_tree('tree_a')
1410
import bzrlib.commit as commit
1411
oldstrategy = bzrlib.gpg.GPGStrategy
1450
import breezy.commit as commit
1451
oldstrategy = breezy.gpg.GPGStrategy
1412
1452
branch = tree_a.branch
1413
1453
repo_a = branch.repository
1414
tree_a.commit("base", allow_pointless=True, rev_id='A')
1415
self.failIf(branch.repository.has_signature_for_revision_id('A'))
1454
tree_a.commit("base", allow_pointless=True, rev_id=b'A')
1455
self.assertFalse(branch.repository.has_signature_for_revision_id(b'A'))
1417
from bzrlib.testament import Testament
1457
from ..testament import Testament
1418
1458
# monkey patch gpg signing mechanism
1419
bzrlib.gpg.GPGStrategy = bzrlib.gpg.LoopbackGPGStrategy
1420
new_config = test_commit.MustSignConfig(branch)
1421
commit.Commit(config=new_config).commit(message="base",
1459
breezy.gpg.GPGStrategy = breezy.gpg.LoopbackGPGStrategy
1460
new_config = test_commit.MustSignConfig()
1461
commit.Commit(config_stack=new_config).commit(message="base",
1422
1462
allow_pointless=True,
1424
1464
working_tree=tree_a)
1425
1465
def sign(text):
1426
return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
1427
self.assertTrue(repo_a.has_signature_for_revision_id('B'))
1466
return breezy.gpg.LoopbackGPGStrategy(None).sign(text)
1467
self.assertTrue(repo_a.has_signature_for_revision_id(b'B'))
1429
bzrlib.gpg.GPGStrategy = oldstrategy
1469
breezy.gpg.GPGStrategy = oldstrategy
1430
1470
tree_b = self.make_branch_and_tree('tree_b')
1431
1471
repo_b = tree_b.branch.repository
1433
1473
serializer = BundleSerializerV4('4')
1434
serializer.write(tree_a.branch.repository, ['A', 'B'], {}, s)
1474
serializer.write(tree_a.branch.repository, [b'A', b'B'], {}, s)
1436
1476
install_bundle(repo_b, serializer.read(s))
1437
self.assertTrue(repo_b.has_signature_for_revision_id('B'))
1438
self.assertEqual(repo_b.get_signature_text('B'),
1439
repo_a.get_signature_text('B'))
1477
self.assertTrue(repo_b.has_signature_for_revision_id(b'B'))
1478
self.assertEqual(repo_b.get_signature_text(b'B'),
1479
repo_a.get_signature_text(b'B'))
1441
1481
# ensure repeat installs are harmless
1442
1482
install_bundle(repo_b, serializer.read(s))
1445
class V4WeaveBundleTester(V4BundleTester):
1447
def bzrdir_format(self):
1451
1485
class V4_2aBundleTester(V4BundleTester):
1453
1487
def bzrdir_format(self):
1480
1514
def make_merged_branch(self):
1481
1515
builder = self.make_branch_builder('source')
1482
1516
builder.start_series()
1483
builder.build_snapshot('a@cset-0-1', None, [
1484
('add', ('', 'root-id', 'directory', None)),
1485
('add', ('file', 'file-id', 'file', 'original content\n')),
1487
builder.build_snapshot('a@cset-0-2a', ['a@cset-0-1'], [
1488
('modify', ('file-id', 'new-content\n')),
1490
builder.build_snapshot('a@cset-0-2b', ['a@cset-0-1'], [
1491
('add', ('other-file', 'file2-id', 'file', 'file2-content\n')),
1493
builder.build_snapshot('a@cset-0-3', ['a@cset-0-2a', 'a@cset-0-2b'], [
1494
('add', ('other-file', 'file2-id', 'file', 'file2-content\n')),
1517
builder.build_snapshot(None, [
1518
('add', ('', b'root-id', 'directory', None)),
1519
('add', ('file', b'file-id', 'file', b'original content\n')),
1520
], revision_id=b'a@cset-0-1')
1521
builder.build_snapshot([b'a@cset-0-1'], [
1522
('modify', ('file', b'new-content\n')),
1523
], revision_id=b'a@cset-0-2a')
1524
builder.build_snapshot([b'a@cset-0-1'], [
1525
('add', ('other-file', b'file2-id', 'file', b'file2-content\n')),
1526
], revision_id=b'a@cset-0-2b')
1527
builder.build_snapshot([b'a@cset-0-2a', b'a@cset-0-2b'], [
1528
('add', ('other-file', b'file2-id', 'file', b'file2-content\n')),
1529
], revision_id=b'a@cset-0-3')
1496
1530
builder.finish_series()
1497
1531
self.b1 = builder.get_branch()
1498
1532
self.b1.lock_read()
1513
1547
def test_single_inventory_multiple_parents_as_xml(self):
1514
1548
self.make_merged_branch()
1515
sio = self.make_bundle_just_inventories('a@cset-0-1', 'a@cset-0-3',
1549
sio = self.make_bundle_just_inventories(b'a@cset-0-1', b'a@cset-0-3',
1517
1551
reader = v4.BundleReader(sio, stream_input=False)
1518
1552
records = list(reader.iter_records())
1519
1553
self.assertEqual(1, len(records))
1520
1554
(bytes, metadata, repo_kind, revision_id,
1521
1555
file_id) = records[0]
1522
1556
self.assertIs(None, file_id)
1523
self.assertEqual('a@cset-0-3', revision_id)
1557
self.assertEqual(b'a@cset-0-3', revision_id)
1524
1558
self.assertEqual('inventory', repo_kind)
1525
self.assertEqual({'parents': ['a@cset-0-2a', 'a@cset-0-2b'],
1526
'sha1': '09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1527
'storage_kind': 'mpdiff',
1559
self.assertEqual({b'parents': [b'a@cset-0-2a', b'a@cset-0-2b'],
1560
b'sha1': b'09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1561
b'storage_kind': b'mpdiff',
1529
1563
# We should have an mpdiff that takes some lines from both parents.
1530
1564
self.assertEqualDiff(
1532
'<inventory format="10" revision_id="a@cset-0-3">\n'
1535
'c 1 3 3 2\n', bytes)
1566
b'<inventory format="10" revision_id="a@cset-0-3">\n'
1569
b'c 1 3 3 2\n', bytes)
1537
1571
def test_single_inv_no_parents_as_xml(self):
1538
1572
self.make_merged_branch()
1539
sio = self.make_bundle_just_inventories('null:', 'a@cset-0-1',
1573
sio = self.make_bundle_just_inventories(b'null:', b'a@cset-0-1',
1541
1575
reader = v4.BundleReader(sio, stream_input=False)
1542
1576
records = list(reader.iter_records())
1543
1577
self.assertEqual(1, len(records))
1544
1578
(bytes, metadata, repo_kind, revision_id,
1545
1579
file_id) = records[0]
1546
1580
self.assertIs(None, file_id)
1547
self.assertEqual('a@cset-0-1', revision_id)
1581
self.assertEqual(b'a@cset-0-1', revision_id)
1548
1582
self.assertEqual('inventory', repo_kind)
1549
self.assertEqual({'parents': [],
1550
'sha1': 'a13f42b142d544aac9b085c42595d304150e31a2',
1551
'storage_kind': 'mpdiff',
1583
self.assertEqual({b'parents': [],
1584
b'sha1': b'a13f42b142d544aac9b085c42595d304150e31a2',
1585
b'storage_kind': b'mpdiff',
1553
1587
# We should have an mpdiff that takes some lines from both parents.
1554
1588
self.assertEqualDiff(
1556
'<inventory format="10" revision_id="a@cset-0-1">\n'
1557
'<directory file_id="root-id" name=""'
1558
' revision="a@cset-0-1" />\n'
1559
'<file file_id="file-id" name="file" parent_id="root-id"'
1560
' revision="a@cset-0-1"'
1561
' text_sha1="09c2f8647e14e49e922b955c194102070597c2d1"'
1562
' text_size="17" />\n'
1590
b'<inventory format="10" revision_id="a@cset-0-1">\n'
1591
b'<directory file_id="root-id" name=""'
1592
b' revision="a@cset-0-1" />\n'
1593
b'<file file_id="file-id" name="file" parent_id="root-id"'
1594
b' revision="a@cset-0-1"'
1595
b' text_sha1="09c2f8647e14e49e922b955c194102070597c2d1"'
1596
b' text_size="17" />\n'
1566
1600
def test_multiple_inventories_as_xml(self):
1567
1601
self.make_merged_branch()
1568
sio = self.make_bundle_just_inventories('a@cset-0-1', 'a@cset-0-3',
1569
['a@cset-0-2a', 'a@cset-0-2b', 'a@cset-0-3'])
1602
sio = self.make_bundle_just_inventories(b'a@cset-0-1', b'a@cset-0-3',
1603
[b'a@cset-0-2a', b'a@cset-0-2b', b'a@cset-0-3'])
1570
1604
reader = v4.BundleReader(sio, stream_input=False)
1571
1605
records = list(reader.iter_records())
1572
1606
self.assertEqual(3, len(records))
1573
1607
revision_ids = [rev_id for b, m, k, rev_id, f in records]
1574
self.assertEqual(['a@cset-0-2a', 'a@cset-0-2b', 'a@cset-0-3'],
1608
self.assertEqual([b'a@cset-0-2a', b'a@cset-0-2b', b'a@cset-0-3'],
1576
1610
metadata_2a = records[0][1]
1577
self.assertEqual({'parents': ['a@cset-0-1'],
1578
'sha1': '1e105886d62d510763e22885eec733b66f5f09bf',
1579
'storage_kind': 'mpdiff',
1611
self.assertEqual({b'parents': [b'a@cset-0-1'],
1612
b'sha1': b'1e105886d62d510763e22885eec733b66f5f09bf',
1613
b'storage_kind': b'mpdiff',
1580
1614
}, metadata_2a)
1581
1615
metadata_2b = records[1][1]
1582
self.assertEqual({'parents': ['a@cset-0-1'],
1583
'sha1': 'f03f12574bdb5ed2204c28636c98a8547544ccd8',
1584
'storage_kind': 'mpdiff',
1616
self.assertEqual({b'parents': [b'a@cset-0-1'],
1617
b'sha1': b'f03f12574bdb5ed2204c28636c98a8547544ccd8',
1618
b'storage_kind': b'mpdiff',
1585
1619
}, metadata_2b)
1586
1620
metadata_3 = records[2][1]
1587
self.assertEqual({'parents': ['a@cset-0-2a', 'a@cset-0-2b'],
1588
'sha1': '09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1589
'storage_kind': 'mpdiff',
1621
self.assertEqual({b'parents': [b'a@cset-0-2a', b'a@cset-0-2b'],
1622
b'sha1': b'09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1623
b'storage_kind': b'mpdiff',
1591
1625
bytes_2a = records[0][0]
1592
1626
self.assertEqualDiff(
1594
'<inventory format="10" revision_id="a@cset-0-2a">\n'
1598
'<file file_id="file-id" name="file" parent_id="root-id"'
1599
' revision="a@cset-0-2a"'
1600
' text_sha1="50f545ff40e57b6924b1f3174b267ffc4576e9a9"'
1601
' text_size="12" />\n'
1603
'c 0 3 3 1\n', bytes_2a)
1628
b'<inventory format="10" revision_id="a@cset-0-2a">\n'
1632
b'<file file_id="file-id" name="file" parent_id="root-id"'
1633
b' revision="a@cset-0-2a"'
1634
b' text_sha1="50f545ff40e57b6924b1f3174b267ffc4576e9a9"'
1635
b' text_size="12" />\n'
1637
b'c 0 3 3 1\n', bytes_2a)
1604
1638
bytes_2b = records[1][0]
1605
1639
self.assertEqualDiff(
1607
'<inventory format="10" revision_id="a@cset-0-2b">\n'
1611
'<file file_id="file2-id" name="other-file" parent_id="root-id"'
1612
' revision="a@cset-0-2b"'
1613
' text_sha1="b46c0c8ea1e5ef8e46fc8894bfd4752a88ec939e"'
1614
' text_size="14" />\n'
1616
'c 0 3 4 1\n', bytes_2b)
1641
b'<inventory format="10" revision_id="a@cset-0-2b">\n'
1645
b'<file file_id="file2-id" name="other-file" parent_id="root-id"'
1646
b' revision="a@cset-0-2b"'
1647
b' text_sha1="b46c0c8ea1e5ef8e46fc8894bfd4752a88ec939e"'
1648
b' text_size="14" />\n'
1650
b'c 0 3 4 1\n', bytes_2b)
1617
1651
bytes_3 = records[2][0]
1618
1652
self.assertEqualDiff(
1620
'<inventory format="10" revision_id="a@cset-0-3">\n'
1623
'c 1 3 3 2\n', bytes_3)
1654
b'<inventory format="10" revision_id="a@cset-0-3">\n'
1657
b'c 1 3 3 2\n', bytes_3)
1625
1659
def test_creating_bundle_preserves_chk_pages(self):
1626
1660
self.make_merged_branch()
1627
target = self.b1.bzrdir.sprout('target',
1628
revision_id='a@cset-0-2a').open_branch()
1629
bundle_txt, rev_ids = self.create_bundle_text('a@cset-0-2a',
1631
self.assertEqual(['a@cset-0-2b', 'a@cset-0-3'], rev_ids)
1661
target = self.b1.controldir.sprout('target',
1662
revision_id=b'a@cset-0-2a').open_branch()
1663
bundle_txt, rev_ids = self.create_bundle_text(b'a@cset-0-2a',
1665
self.assertEqual([b'a@cset-0-2b', b'a@cset-0-3'], rev_ids)
1632
1666
bundle = read_bundle(bundle_txt)
1633
1667
target.lock_write()
1634
1668
self.addCleanup(target.unlock)
1635
1669
install_bundle(target.repository, bundle)
1636
inv1 = self.b1.repository.inventories.get_record_stream([
1637
('a@cset-0-3',)], 'unordered',
1638
True).next().get_bytes_as('fulltext')
1639
inv2 = target.repository.inventories.get_record_stream([
1640
('a@cset-0-3',)], 'unordered',
1641
True).next().get_bytes_as('fulltext')
1670
inv1 = next(self.b1.repository.inventories.get_record_stream([
1671
(b'a@cset-0-3',)], 'unordered',
1672
True)).get_bytes_as('fulltext')
1673
inv2 = next(target.repository.inventories.get_record_stream([
1674
(b'a@cset-0-3',)], 'unordered',
1675
True)).get_bytes_as('fulltext')
1642
1676
self.assertEqualDiff(inv1, inv2)
1740
1774
class TestBundleWriterReader(tests.TestCase):
1742
1776
def test_roundtrip_record(self):
1743
fileobj = StringIO()
1744
1778
writer = v4.BundleWriter(fileobj)
1746
writer.add_info_record(foo='bar')
1747
writer._add_record("Record body", {'parents': ['1', '3'],
1748
'storage_kind':'fulltext'}, 'file', 'revid', 'fileid')
1780
writer.add_info_record({b'foo': b'bar'})
1781
writer._add_record("Record body", {b'parents': [b'1', b'3'],
1782
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1750
1784
fileobj.seek(0)
1751
1785
reader = v4.BundleReader(fileobj, stream_input=True)
1752
1786
record_iter = reader.iter_records()
1753
record = record_iter.next()
1754
self.assertEqual((None, {'foo': 'bar', 'storage_kind': 'header'},
1787
record = next(record_iter)
1788
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1755
1789
'info', None, None), record)
1756
record = record_iter.next()
1757
self.assertEqual(("Record body", {'storage_kind': 'fulltext',
1758
'parents': ['1', '3']}, 'file', 'revid', 'fileid'),
1790
record = next(record_iter)
1791
self.assertEqual(("Record body", {b'storage_kind': b'fulltext',
1792
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1761
1795
def test_roundtrip_record_memory_hungry(self):
1762
fileobj = StringIO()
1763
1797
writer = v4.BundleWriter(fileobj)
1765
writer.add_info_record(foo='bar')
1766
writer._add_record("Record body", {'parents': ['1', '3'],
1767
'storage_kind':'fulltext'}, 'file', 'revid', 'fileid')
1799
writer.add_info_record({b'foo': b'bar'})
1800
writer._add_record("Record body", {b'parents': [b'1', b'3'],
1801
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1769
1803
fileobj.seek(0)
1770
1804
reader = v4.BundleReader(fileobj, stream_input=False)
1771
1805
record_iter = reader.iter_records()
1772
record = record_iter.next()
1773
self.assertEqual((None, {'foo': 'bar', 'storage_kind': 'header'},
1806
record = next(record_iter)
1807
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1774
1808
'info', None, None), record)
1775
record = record_iter.next()
1776
self.assertEqual(("Record body", {'storage_kind': 'fulltext',
1777
'parents': ['1', '3']}, 'file', 'revid', 'fileid'),
1809
record = next(record_iter)
1810
self.assertEqual(("Record body", {b'storage_kind': b'fulltext',
1811
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1780
1814
def test_encode_name(self):
1781
self.assertEqual('revision/rev1',
1782
v4.BundleWriter.encode_name('revision', 'rev1'))
1783
self.assertEqual('file/rev//1/file-id-1',
1784
v4.BundleWriter.encode_name('file', 'rev/1', 'file-id-1'))
1785
self.assertEqual('info',
1815
self.assertEqual(b'revision/rev1',
1816
v4.BundleWriter.encode_name('revision', b'rev1'))
1817
self.assertEqual(b'file/rev//1/file-id-1',
1818
v4.BundleWriter.encode_name('file', b'rev/1', b'file-id-1'))
1819
self.assertEqual(b'info',
1786
1820
v4.BundleWriter.encode_name('info', None, None))
1788
1822
def test_decode_name(self):
1789
self.assertEqual(('revision', 'rev1', None),
1790
v4.BundleReader.decode_name('revision/rev1'))
1791
self.assertEqual(('file', 'rev/1', 'file-id-1'),
1792
v4.BundleReader.decode_name('file/rev//1/file-id-1'))
1823
self.assertEqual(('revision', b'rev1', None),
1824
v4.BundleReader.decode_name(b'revision/rev1'))
1825
self.assertEqual(('file', b'rev/1', b'file-id-1'),
1826
v4.BundleReader.decode_name(b'file/rev//1/file-id-1'))
1793
1827
self.assertEqual(('info', None, None),
1794
v4.BundleReader.decode_name('info'))
1828
v4.BundleReader.decode_name(b'info'))
1796
1830
def test_too_many_names(self):
1797
fileobj = StringIO()
1798
1832
writer = v4.BundleWriter(fileobj)
1800
writer.add_info_record(foo='bar')
1801
writer._container.add_bytes_record('blah', ['two', 'names'])
1834
writer.add_info_record({b'foo': b'bar'})
1835
writer._container.add_bytes_record(b'blah', [b'two', b'names'])
1803
1837
fileobj.seek(0)
1804
1838
record_iter = v4.BundleReader(fileobj).iter_records()
1805
record = record_iter.next()
1806
self.assertEqual((None, {'foo': 'bar', 'storage_kind': 'header'},
1839
record = next(record_iter)
1840
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1807
1841
'info', None, None), record)
1808
self.assertRaises(errors.BadBundle, record_iter.next)
1842
self.assertRaises(errors.BadBundle, next, record_iter)
1811
1845
class TestReadMergeableFromUrl(tests.TestCaseWithTransport):