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')
60
64
def get_inventory_text(repo, revision_id):
61
65
"""Get the fulltext for the inventory at revision id"""
66
with repo.lock_read():
64
67
return get_text(repo.inventories, (revision_id,))
69
70
class MockTree(object):
70
72
def __init__(self):
71
from bzrlib.inventory import InventoryDirectory, ROOT_ID
73
from ..bzr.inventory import InventoryDirectory, ROOT_ID
72
74
object.__init__(self)
73
75
self.paths = {ROOT_ID: ""}
74
76
self.ids = {"": ROOT_ID}
76
78
self.root = InventoryDirectory(ROOT_ID, '', None)
78
inventory = property(lambda x:x)
81
return self.paths.iterkeys()
80
inventory = property(lambda x: x)
81
root_inventory = property(lambda x: x)
83
def get_root_id(self):
84
return self.root.file_id
86
def all_file_ids(self):
87
return set(self.paths.keys())
89
def all_versioned_paths(self):
90
return set(self.paths.values())
92
def is_executable(self, path):
93
# Not all the files are executable.
83
96
def __getitem__(self, file_id):
84
97
if file_id == self.root.file_id:
93
109
return self.ids[parent_dir]
95
111
def iter_entries(self):
96
for path, file_id in self.ids.iteritems():
112
for path, file_id in self.ids.items():
97
113
yield path, self[file_id]
99
def get_file_kind(self, file_id):
100
if file_id in self.contents:
115
def kind(self, path):
116
if path in self.contents:
103
119
kind = 'directory'
106
122
def make_entry(self, file_id, path):
107
from bzrlib.inventory import (InventoryEntry, InventoryFile
108
, InventoryDirectory, InventoryLink)
123
from ..bzr.inventory import (InventoryFile, InventoryDirectory,
125
if not isinstance(file_id, bytes):
126
raise TypeError(file_id)
109
127
name = os.path.basename(path)
110
kind = self.get_file_kind(file_id)
128
kind = self.kind(path)
111
129
parent_id = self.parent_id(file_id)
112
text_sha_1, text_size = self.contents_stats(file_id)
130
text_sha_1, text_size = self.contents_stats(path)
113
131
if kind == 'directory':
114
132
ie = InventoryDirectory(file_id, name, parent_id)
115
133
elif kind == 'file':
116
134
ie = InventoryFile(file_id, name, parent_id)
135
ie.text_sha1 = text_sha_1
136
ie.text_size = text_size
117
137
elif kind == 'symlink':
118
138
ie = InventoryLink(file_id, name, parent_id)
120
140
raise errors.BzrError('unknown kind %r' % kind)
121
ie.text_sha1 = text_sha_1
122
ie.text_size = text_size
125
143
def add_dir(self, file_id, path):
144
if not isinstance(file_id, bytes):
145
raise TypeError(file_id)
126
146
self.paths[file_id] = path
127
147
self.ids[path] = file_id
129
149
def add_file(self, file_id, path, contents):
150
if not isinstance(file_id, bytes):
151
raise TypeError(file_id)
130
152
self.add_dir(file_id, path)
131
self.contents[file_id] = contents
153
self.contents[path] = contents
133
155
def path2id(self, path):
134
156
return self.ids.get(path)
172
206
self.assertEqual(btree.old_path("grandparent/parent/file"),
173
207
"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)
209
self.assertEqual(btree.id2path(b"a"), "grandparent")
210
self.assertEqual(btree.id2path(b"b"), "grandparent/parent")
211
self.assertEqual(btree.id2path(b"c"), "grandparent/parent/file")
213
self.assertEqual(btree.path2id("grandparent"), b"a")
214
self.assertEqual(btree.path2id("grandparent/parent"), b"b")
215
self.assertEqual(btree.path2id("grandparent/parent/file"), b"c")
217
self.assertIs(btree.path2id("grandparent2"), None)
218
self.assertIs(btree.path2id("grandparent2/parent"), None)
219
self.assertIs(btree.path2id("grandparent2/parent/file"), None)
187
221
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")
222
self.assertIs(btree.old_path("grandparent"), None)
223
self.assertIs(btree.old_path("grandparent/parent"), None)
224
self.assertIs(btree.old_path("grandparent/parent/file"), None)
226
self.assertEqual(btree.id2path(b"a"), "grandparent2")
227
self.assertEqual(btree.id2path(b"b"), "grandparent2/parent")
228
self.assertEqual(btree.id2path(b"c"), "grandparent2/parent/file")
230
self.assertEqual(btree.path2id("grandparent2"), b"a")
231
self.assertEqual(btree.path2id("grandparent2/parent"), b"b")
232
self.assertEqual(btree.path2id("grandparent2/parent/file"), b"c")
200
234
self.assertTrue(btree.path2id("grandparent") is None)
201
235
self.assertTrue(btree.path2id("grandparent/parent") is None)
202
236
self.assertTrue(btree.path2id("grandparent/parent/file") is None)
204
238
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")
239
self.assertEqual(btree.id2path(b"a"), "grandparent2")
240
self.assertEqual(btree.id2path(b"b"), "grandparent2/parent2")
241
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")
243
self.assertEqual(btree.path2id("grandparent2"), b"a")
244
self.assertEqual(btree.path2id("grandparent2/parent2"), b"b")
245
self.assertEqual(btree.path2id("grandparent2/parent2/file"), b"c")
213
247
self.assertTrue(btree.path2id("grandparent2/parent") is None)
214
248
self.assertTrue(btree.path2id("grandparent2/parent/file") is None)
216
250
btree.note_rename("grandparent/parent/file",
217
251
"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")
252
self.assertEqual(btree.id2path(b"a"), "grandparent2")
253
self.assertEqual(btree.id2path(b"b"), "grandparent2/parent2")
254
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")
256
self.assertEqual(btree.path2id("grandparent2"), b"a")
257
self.assertEqual(btree.path2id("grandparent2/parent2"), b"b")
258
self.assertEqual(btree.path2id("grandparent2/parent2/file2"), b"c")
226
260
self.assertTrue(btree.path2id("grandparent2/parent2/file") is None)
230
264
btree = self.make_tree_1()[0]
231
265
btree.note_rename("grandparent/parent/file",
232
266
"grandparent/alt_parent/file")
233
self.assertEqual(btree.id2path("c"), "grandparent/alt_parent/file")
234
self.assertEqual(btree.path2id("grandparent/alt_parent/file"), "c")
267
self.assertEqual(btree.id2path(b"c"), "grandparent/alt_parent/file")
268
self.assertEqual(btree.path2id("grandparent/alt_parent/file"), b"c")
235
269
self.assertTrue(btree.path2id("grandparent/parent/file") is None)
237
271
def unified_diff(self, old, new):
239
273
diff.internal_diff("old", old, "new", new, out)
241
275
return out.read()
243
277
def make_tree_2(self):
244
278
btree = self.make_tree_1()[0]
245
279
btree.note_rename("grandparent/parent/file",
246
280
"grandparent/alt_parent/file")
247
self.assertTrue(btree.id2path("e") is None)
248
self.assertTrue(btree.path2id("grandparent/parent/file") is None)
249
btree.note_id("e", "grandparent/parent/file")
281
self.assertTrue(btree.id2path(b"e") is None)
282
self.assertFalse(btree.is_versioned("grandparent/parent/file"))
283
btree.note_id(b"e", "grandparent/parent/file")
252
286
def test_adds(self):
253
287
"""File/inventory adds"""
254
288
btree = self.make_tree_2()
255
add_patch = self.unified_diff([], ["Extra cheese\n"])
289
add_patch = self.unified_diff([], [b"Extra cheese\n"])
256
290
btree.note_patch("grandparent/parent/file", add_patch)
257
btree.note_id('f', 'grandparent/parent/symlink', kind='symlink')
291
btree.note_id(b'f', 'grandparent/parent/symlink', kind='symlink')
258
292
btree.note_target('grandparent/parent/symlink', 'venus')
259
293
self.adds_test(btree)
261
295
def adds_test(self, btree):
262
self.assertEqual(btree.id2path("e"), "grandparent/parent/file")
263
self.assertEqual(btree.path2id("grandparent/parent/file"), "e")
264
self.assertEqual(btree.get_file("e").read(), "Extra cheese\n")
265
self.assertEqual(btree.get_symlink_target('f'), 'venus')
296
self.assertEqual(btree.id2path(b"e"), "grandparent/parent/file")
297
self.assertEqual(btree.path2id("grandparent/parent/file"), b"e")
298
with btree.get_file("grandparent/parent/file") as f:
299
self.assertEqual(f.read(), b"Extra cheese\n")
301
btree.get_symlink_target('grandparent/parent/symlink'), 'venus')
267
303
def test_adds2(self):
268
304
"""File/inventory adds, with patch-compatibile renames"""
269
305
btree = self.make_tree_2()
270
306
btree.contents_by_id = False
271
add_patch = self.unified_diff(["Hello\n"], ["Extra cheese\n"])
307
add_patch = self.unified_diff([b"Hello\n"], [b"Extra cheese\n"])
272
308
btree.note_patch("grandparent/parent/file", add_patch)
273
btree.note_id('f', 'grandparent/parent/symlink', kind='symlink')
309
btree.note_id(b'f', 'grandparent/parent/symlink', kind='symlink')
274
310
btree.note_target('grandparent/parent/symlink', 'venus')
275
311
self.adds_test(btree)
277
313
def make_tree_3(self):
278
314
btree, mtree = self.make_tree_1()
279
mtree.add_file("e", "grandparent/parent/topping", "Anchovies\n")
315
mtree.add_file(b"e", "grandparent/parent/topping", b"Anchovies\n")
280
316
btree.note_rename("grandparent/parent/file",
281
317
"grandparent/alt_parent/file")
282
318
btree.note_rename("grandparent/parent/topping",
286
322
def get_file_test(self, btree):
287
self.assertEqual(btree.get_file("e").read(), "Lemon\n")
288
self.assertEqual(btree.get_file("c").read(), "Hello\n")
323
with btree.get_file(btree.id2path(b"e")) as f:
324
self.assertEqual(f.read(), b"Lemon\n")
325
with btree.get_file(btree.id2path(b"c")) as f:
326
self.assertEqual(f.read(), b"Hello\n")
290
328
def test_get_file(self):
291
329
"""Get file contents"""
292
330
btree = self.make_tree_3()
293
mod_patch = self.unified_diff(["Anchovies\n"], ["Lemon\n"])
331
mod_patch = self.unified_diff([b"Anchovies\n"], [b"Lemon\n"])
294
332
btree.note_patch("grandparent/alt_parent/stopping", mod_patch)
295
333
self.get_file_test(btree)
297
335
def test_get_file2(self):
298
"""Get file contents, with patch-compatibile renames"""
336
"""Get file contents, with patch-compatible renames"""
299
337
btree = self.make_tree_3()
300
338
btree.contents_by_id = False
301
mod_patch = self.unified_diff([], ["Lemon\n"])
339
mod_patch = self.unified_diff([], [b"Lemon\n"])
302
340
btree.note_patch("grandparent/alt_parent/stopping", mod_patch)
303
mod_patch = self.unified_diff([], ["Hello\n"])
341
mod_patch = self.unified_diff([], [b"Hello\n"])
304
342
btree.note_patch("grandparent/alt_parent/file", mod_patch)
305
343
self.get_file_test(btree)
307
345
def test_delete(self):
308
346
"Deletion by bundle"
309
347
btree = self.make_tree_1()[0]
310
self.assertEqual(btree.get_file("c").read(), "Hello\n")
348
with btree.get_file(btree.id2path(b"c")) as f:
349
self.assertEqual(f.read(), b"Hello\n")
311
350
btree.note_deletion("grandparent/parent/file")
312
self.assertTrue(btree.id2path("c") is None)
313
self.assertTrue(btree.path2id("grandparent/parent/file") is None)
351
self.assertTrue(btree.id2path(b"c") is None)
352
self.assertFalse(btree.is_versioned("grandparent/parent/file"))
315
354
def sorted_ids(self, tree):
355
ids = sorted(tree.all_file_ids())
320
358
def test_iteration(self):
321
359
"""Ensure that iteration through ids works properly"""
322
360
btree = self.make_tree_1()[0]
323
361
self.assertEqual(self.sorted_ids(btree),
324
[inventory.ROOT_ID, 'a', 'b', 'c', 'd'])
362
[inventory.ROOT_ID, b'a', b'b', b'c', b'd'])
325
363
btree.note_deletion("grandparent/parent/file")
326
btree.note_id("e", "grandparent/alt_parent/fool", kind="directory")
364
btree.note_id(b"e", "grandparent/alt_parent/fool", kind="directory")
327
365
btree.note_last_changed("grandparent/alt_parent/fool",
328
366
"revisionidiguess")
329
367
self.assertEqual(self.sorted_ids(btree),
330
[inventory.ROOT_ID, 'a', 'b', 'd', 'e'])
368
[inventory.ROOT_ID, b'a', b'b', b'd', b'e'])
333
371
class BundleTester1(tests.TestCaseWithTransport):
574
610
self.tree1 = self.make_branch_and_tree('b1')
575
611
self.b1 = self.tree1.branch
577
self.build_tree_contents([('b1/one', 'one\n')])
578
self.tree1.add('one', 'one-id')
579
self.tree1.set_root_id('root-id')
580
self.tree1.commit('add one', rev_id='a@cset-0-1')
613
self.build_tree_contents([('b1/one', b'one\n')])
614
self.tree1.add('one', b'one-id')
615
self.tree1.set_root_id(b'root-id')
616
self.tree1.commit('add one', rev_id=b'a@cset-0-1')
582
bundle = self.get_valid_bundle('null:', 'a@cset-0-1')
618
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-1')
584
620
# Make sure we can handle files with spaces, tabs, other
585
621
# bogus characters
586
622
self.build_tree([
589
, 'b1/dir/filein subdir.c'
590
, 'b1/dir/WithCaps.txt'
591
, 'b1/dir/ pre space'
594
, 'b1/sub/sub/nonempty.txt'
596
self.build_tree_contents([('b1/sub/sub/emptyfile.txt', ''),
597
('b1/dir/nolastnewline.txt', 'bloop')])
623
'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'
625
self.build_tree_contents([('b1/sub/sub/emptyfile.txt', b''),
626
('b1/dir/nolastnewline.txt', b'bloop')])
598
627
tt = TreeTransform(self.tree1)
599
tt.new_file('executable', tt.root, '#!/bin/sh\n', 'exe-1', True)
628
tt.new_file('executable', tt.root, [b'#!/bin/sh\n'], b'exe-1', True)
601
630
# have to fix length of file-id so that we can predictably rewrite
602
631
# a (length-prefixed) record containing it later.
603
self.tree1.add('with space.txt', 'withspace-id')
632
self.tree1.add('with space.txt', b'withspace-id')
606
, 'dir/filein subdir.c'
609
, 'dir/nolastnewline.txt'
612
, 'sub/sub/nonempty.txt'
613
, 'sub/sub/emptyfile.txt'
615
self.tree1.commit('add whitespace', rev_id='a@cset-0-2')
634
'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'
636
self.tree1.commit('add whitespace', rev_id=b'a@cset-0-2')
617
bundle = self.get_valid_bundle('a@cset-0-1', 'a@cset-0-2')
638
bundle = self.get_valid_bundle(b'a@cset-0-1', b'a@cset-0-2')
619
640
# Check a rollup bundle
620
bundle = self.get_valid_bundle('null:', 'a@cset-0-2')
641
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-2')
622
643
# Now delete entries
623
644
self.tree1.remove(
624
['sub/sub/nonempty.txt'
625
, 'sub/sub/emptyfile.txt'
645
['sub/sub/nonempty.txt', 'sub/sub/emptyfile.txt', 'sub/sub'
628
647
tt = TreeTransform(self.tree1)
629
trans_id = tt.trans_id_tree_file_id('exe-1')
648
trans_id = tt.trans_id_tree_path('executable')
630
649
tt.set_executability(False, trans_id)
632
self.tree1.commit('removed', rev_id='a@cset-0-3')
651
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')
653
bundle = self.get_valid_bundle(b'a@cset-0-2', b'a@cset-0-3')
635
654
self.assertRaises((errors.TestamentMismatch,
636
errors.VersionedFileInvalidChecksum,
637
errors.BadBundle), self.get_invalid_bundle,
638
'a@cset-0-2', 'a@cset-0-3')
655
errors.VersionedFileInvalidChecksum,
656
errors.BadBundle), self.get_invalid_bundle,
657
b'a@cset-0-2', b'a@cset-0-3')
639
658
# Check a rollup bundle
640
bundle = self.get_valid_bundle('null:', 'a@cset-0-3')
659
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-3')
642
661
# Now move the directory
643
662
self.tree1.rename_one('dir', 'sub/dir')
644
self.tree1.commit('rename dir', rev_id='a@cset-0-4')
663
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')
665
bundle = self.get_valid_bundle(b'a@cset-0-3', b'a@cset-0-4')
647
666
# Check a rollup bundle
648
bundle = self.get_valid_bundle('null:', 'a@cset-0-4')
667
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')
670
with open('b1/sub/dir/WithCaps.txt', 'ab') as f:
671
f.write(b'\nAdding some text\n')
672
with open('b1/sub/dir/ pre space', 'ab') as f:
674
b'\r\nAdding some\r\nDOS format lines\r\n')
675
with open('b1/sub/dir/nolastnewline.txt', 'ab') as f:
655
677
self.tree1.rename_one('sub/dir/ pre space',
656
678
'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')
679
self.tree1.commit('Modified files', rev_id=b'a@cset-0-5')
680
bundle = self.get_valid_bundle(b'a@cset-0-4', b'a@cset-0-5')
660
682
self.tree1.rename_one('sub/dir/WithCaps.txt', 'temp')
661
683
self.tree1.rename_one('with space.txt', 'WithCaps.txt')
662
684
self.tree1.rename_one('temp', 'with space.txt')
663
self.tree1.commit(u'swap filenames', rev_id='a@cset-0-6',
685
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')
687
bundle = self.get_valid_bundle(b'a@cset-0-5', b'a@cset-0-6')
688
other = self.get_checkout(b'a@cset-0-5')
667
689
tree1_inv = get_inventory_text(self.tree1.branch.repository,
669
691
tree2_inv = get_inventory_text(other.branch.repository,
671
693
self.assertEqualDiff(tree1_inv, tree2_inv)
672
694
other.rename_one('sub/dir/nolastnewline.txt', 'sub/nolastnewline.txt')
673
other.commit('rename file', rev_id='a@cset-0-6b')
695
other.commit('rename file', rev_id=b'a@cset-0-6b')
674
696
self.tree1.merge_from_branch(other.branch)
675
self.tree1.commit(u'Merge', rev_id='a@cset-0-7',
697
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')
699
bundle = self.get_valid_bundle(b'a@cset-0-6', b'a@cset-0-7')
679
701
def _test_symlink_bundle(self, link_name, link_target, new_link_target):
682
self.requireFeature(tests.SymlinkFeature)
704
self.requireFeature(features.SymlinkFeature)
683
705
self.tree1 = self.make_branch_and_tree('b1')
684
706
self.b1 = self.tree1.branch
686
708
tt = TreeTransform(self.tree1)
687
709
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:
711
self.tree1.commit('add symlink', rev_id=b'l@cset-0-1')
712
bundle = self.get_valid_bundle(b'null:', b'l@cset-0-1')
713
if getattr(bundle, 'revision_tree', None) is not None:
692
714
# 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))
715
bund_tree = bundle.revision_tree(self.b1.repository, b'l@cset-0-1')
717
link_target, bund_tree.get_symlink_target(link_name))
696
719
tt = TreeTransform(self.tree1)
697
trans_id = tt.trans_id_tree_file_id(link_id)
720
trans_id = tt.trans_id_tree_path(link_name)
698
721
tt.adjust_path('link2', tt.root, trans_id)
699
722
tt.delete_contents(trans_id)
700
723
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:
725
self.tree1.commit('rename and change symlink', rev_id=b'l@cset-0-2')
726
bundle = self.get_valid_bundle(b'l@cset-0-1', b'l@cset-0-2')
727
if getattr(bundle, 'revision_tree', None) is not None:
705
728
# Not all bundle formats supports revision_tree
706
bund_tree = bundle.revision_tree(self.b1.repository, 'l@cset-0-2')
729
bund_tree = bundle.revision_tree(self.b1.repository, b'l@cset-0-2')
707
730
self.assertEqual(new_link_target,
708
bund_tree.get_symlink_target(link_id))
731
bund_tree.get_symlink_target('link2'))
710
733
tt = TreeTransform(self.tree1)
711
trans_id = tt.trans_id_tree_file_id(link_id)
734
trans_id = tt.trans_id_tree_path('link2')
712
735
tt.delete_contents(trans_id)
713
736
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')
738
self.tree1.commit('just change symlink target', rev_id=b'l@cset-0-3')
739
bundle = self.get_valid_bundle(b'l@cset-0-2', b'l@cset-0-3')
718
741
tt = TreeTransform(self.tree1)
719
trans_id = tt.trans_id_tree_file_id(link_id)
742
trans_id = tt.trans_id_tree_path('link2')
720
743
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')
745
self.tree1.commit('Delete symlink', rev_id=b'l@cset-0-4')
746
bundle = self.get_valid_bundle(b'l@cset-0-3', b'l@cset-0-4')
725
748
def test_symlink_bundle(self):
726
749
self._test_symlink_bundle('link', 'bar/foo', 'mars')
728
751
def test_unicode_symlink_bundle(self):
729
self.requireFeature(tests.UnicodeFilenameFeature)
752
self.requireFeature(features.UnicodeFilenameFeature)
730
753
self._test_symlink_bundle(u'\N{Euro Sign}link',
731
754
u'bar/\N{Euro Sign}foo',
732
755
u'mars\N{Euro Sign}')
737
760
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',
763
tt.new_file('file', tt.root, [
764
b'\x00\n\x00\r\x01\n\x02\r\xff'], b'binary-1')
765
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')
768
self.tree1.commit('add binary', rev_id=b'b@cset-0-1')
769
self.get_valid_bundle(b'null:', b'b@cset-0-1')
748
772
tt = TreeTransform(self.tree1)
749
trans_id = tt.trans_id_tree_file_id('binary-1')
773
trans_id = tt.trans_id_tree_path('file')
750
774
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')
776
self.tree1.commit('delete binary', rev_id=b'b@cset-0-2')
777
self.get_valid_bundle(b'b@cset-0-1', b'b@cset-0-2')
755
779
# Rename & modify
756
780
tt = TreeTransform(self.tree1)
757
trans_id = tt.trans_id_tree_file_id('binary-2')
781
trans_id = tt.trans_id_tree_path('file2')
758
782
tt.adjust_path('file3', tt.root, trans_id)
759
783
tt.delete_contents(trans_id)
760
tt.create_file('file\rcontents\x00\n\x00', trans_id)
784
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')
786
self.tree1.commit('rename and modify binary', rev_id=b'b@cset-0-3')
787
self.get_valid_bundle(b'b@cset-0-2', b'b@cset-0-3')
766
790
tt = TreeTransform(self.tree1)
767
trans_id = tt.trans_id_tree_file_id('binary-2')
791
trans_id = tt.trans_id_tree_path('file3')
768
792
tt.delete_contents(trans_id)
769
tt.create_file('\x00file\rcontents', trans_id)
793
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')
795
self.tree1.commit('just modify binary', rev_id=b'b@cset-0-4')
796
self.get_valid_bundle(b'b@cset-0-3', b'b@cset-0-4')
775
self.get_valid_bundle('null:', 'b@cset-0-4')
799
self.get_valid_bundle(b'null:', b'b@cset-0-4')
777
801
def test_last_modified(self):
778
802
self.tree1 = self.make_branch_and_tree('b1')
779
803
self.b1 = self.tree1.branch
780
804
tt = TreeTransform(self.tree1)
781
tt.new_file('file', tt.root, 'file', 'file')
805
tt.new_file('file', tt.root, [b'file'], b'file')
783
self.tree1.commit('create file', rev_id='a@lmod-0-1')
807
self.tree1.commit('create file', rev_id=b'a@lmod-0-1')
785
809
tt = TreeTransform(self.tree1)
786
trans_id = tt.trans_id_tree_file_id('file')
810
trans_id = tt.trans_id_tree_path('file')
787
811
tt.delete_contents(trans_id)
788
tt.create_file('file2', trans_id)
812
tt.create_file([b'file2'], trans_id)
790
self.tree1.commit('modify text', rev_id='a@lmod-0-2a')
814
self.tree1.commit('modify text', rev_id=b'a@lmod-0-2a')
792
other = self.get_checkout('a@lmod-0-1')
816
other = self.get_checkout(b'a@lmod-0-1')
793
817
tt = TreeTransform(other)
794
trans_id = tt.trans_id_tree_file_id('file')
818
trans_id = tt.trans_id_tree_path('file2')
795
819
tt.delete_contents(trans_id)
796
tt.create_file('file2', trans_id)
820
tt.create_file([b'file2'], trans_id)
798
other.commit('modify text in another tree', rev_id='a@lmod-0-2b')
822
other.commit('modify text in another tree', rev_id=b'a@lmod-0-2b')
799
823
self.tree1.merge_from_branch(other.branch)
800
self.tree1.commit(u'Merge', rev_id='a@lmod-0-3',
824
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')
826
self.tree1.commit(u'Merge', rev_id=b'a@lmod-0-4')
827
bundle = self.get_valid_bundle(b'a@lmod-0-2a', b'a@lmod-0-4')
805
829
def test_hide_history(self):
806
830
self.tree1 = self.make_branch_and_tree('b1')
807
831
self.b1 = self.tree1.branch
809
open('b1/one', 'wb').write('one\n')
833
with open('b1/one', 'wb') as f:
810
835
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')
836
self.tree1.commit('add file', rev_id=b'a@cset-0-1')
837
with open('b1/one', 'wb') as f:
839
self.tree1.commit('modify', rev_id=b'a@cset-0-2')
840
with open('b1/one', 'wb') as f:
842
self.tree1.commit('modify', rev_id=b'a@cset-0-3')
843
bundle_file = BytesIO()
844
rev_ids = write_bundle(self.tree1.branch.repository, b'a@cset-0-3',
845
b'a@cset-0-1', bundle_file, format=self.format)
846
self.assertNotContainsRe(bundle_file.getvalue(), b'\btwo\b')
847
self.assertContainsRe(self.get_raw(bundle_file), b'one')
848
self.assertContainsRe(self.get_raw(bundle_file), b'three')
823
850
def test_bundle_same_basis(self):
824
851
"""Ensure using the basis as the target doesn't cause an error"""
825
852
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)
853
self.tree1.commit('add file', rev_id=b'a@cset-0-1')
854
bundle_file = BytesIO()
855
rev_ids = write_bundle(self.tree1.branch.repository, b'a@cset-0-1',
856
b'a@cset-0-1', bundle_file)
832
859
def get_raw(bundle_file):
833
860
return bundle_file.getvalue()
835
862
def test_unicode_bundle(self):
836
self.requireFeature(tests.UnicodeFilenameFeature)
863
self.requireFeature(features.UnicodeFilenameFeature)
837
864
# Handle international characters
839
866
f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
842
869
self.b1 = self.tree1.branch
844
871
f.write((u'A file\n'
845
u'With international man of mystery\n'
846
u'William Dod\xe9\n').encode('utf-8'))
872
u'With international man of mystery\n'
873
u'William Dod\xe9\n').encode('utf-8'))
849
self.tree1.add([u'with Dod\N{Euro Sign}'], ['withdod-id'])
876
self.tree1.add([u'with Dod\N{Euro Sign}'], [b'withdod-id'])
850
877
self.tree1.commit(u'i18n commit from William Dod\xe9',
851
rev_id='i18n-1', committer=u'William Dod\xe9')
878
rev_id=b'i18n-1', committer=u'William Dod\xe9')
854
bundle = self.get_valid_bundle('null:', 'i18n-1')
881
bundle = self.get_valid_bundle(b'null:', b'i18n-1')
857
884
f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
858
885
f.write(u'Modified \xb5\n'.encode('utf8'))
860
self.tree1.commit(u'modified', rev_id='i18n-2')
887
self.tree1.commit(u'modified', rev_id=b'i18n-2')
862
bundle = self.get_valid_bundle('i18n-1', 'i18n-2')
889
bundle = self.get_valid_bundle(b'i18n-1', b'i18n-2')
865
892
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',
893
self.tree1.commit(u'renamed, the new i18n man', rev_id=b'i18n-3',
867
894
committer=u'Erik B\xe5gfors')
869
bundle = self.get_valid_bundle('i18n-2', 'i18n-3')
896
bundle = self.get_valid_bundle(b'i18n-2', b'i18n-3')
872
899
self.tree1.remove([u'B\N{Euro Sign}gfors'])
873
self.tree1.commit(u'removed', rev_id='i18n-4')
900
self.tree1.commit(u'removed', rev_id=b'i18n-4')
875
bundle = self.get_valid_bundle('i18n-3', 'i18n-4')
902
bundle = self.get_valid_bundle(b'i18n-3', b'i18n-4')
878
bundle = self.get_valid_bundle('null:', 'i18n-4')
905
bundle = self.get_valid_bundle(b'null:', b'i18n-4')
881
907
def test_whitespace_bundle(self):
882
908
if sys.platform in ('win32', 'cygwin'):
941
968
def test_bundle_root_id(self):
942
969
self.tree1 = self.make_branch_and_tree('b1')
943
970
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)
971
self.tree1.commit('message', rev_id=b'revid1')
972
bundle = self.get_valid_bundle(b'null:', b'revid1')
973
tree = self.get_bundle_tree(bundle, b'revid1')
974
root_revision = tree.get_file_revision(u'')
975
self.assertEqual(b'revid1', root_revision)
949
977
def test_install_revisions(self):
950
978
self.tree1 = self.make_branch_and_tree('b1')
951
979
self.b1 = self.tree1.branch
952
self.tree1.commit('message', rev_id='rev2a')
953
bundle = self.get_valid_bundle('null:', 'rev2a')
980
self.tree1.commit('message', rev_id=b'rev2a')
981
bundle = self.get_valid_bundle(b'null:', b'rev2a')
954
982
branch2 = self.make_branch('b2')
955
self.assertFalse(branch2.repository.has_revision('rev2a'))
983
self.assertFalse(branch2.repository.has_revision(b'rev2a'))
956
984
target_revision = bundle.install_revisions(branch2.repository)
957
self.assertTrue(branch2.repository.has_revision('rev2a'))
958
self.assertEqual('rev2a', target_revision)
985
self.assertTrue(branch2.repository.has_revision(b'rev2a'))
986
self.assertEqual(b'rev2a', target_revision)
960
988
def test_bundle_empty_property(self):
961
989
"""Test serializing revision properties with an empty value."""
962
990
tree = self.make_branch_and_memory_tree('tree')
963
991
tree.lock_write()
964
992
self.addCleanup(tree.unlock)
965
tree.add([''], ['TREE_ROOT'])
966
tree.commit('One', revprops={'one':'two', 'empty':''}, rev_id='rev1')
993
tree.add([''], [b'TREE_ROOT'])
994
tree.commit('One', revprops={u'one': 'two',
995
u'empty': ''}, rev_id=b'rev1')
967
996
self.b1 = tree.branch
968
bundle_sio, revision_ids = self.create_bundle_text('null:', 'rev1')
997
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
969
998
bundle = read_bundle(bundle_sio)
970
999
revision_info = bundle.revisions[0]
971
self.assertEqual('rev1', revision_info.revision_id)
1000
self.assertEqual(b'rev1', revision_info.revision_id)
972
1001
rev = revision_info.as_revision()
973
self.assertEqual({'branch-nick':'tree', 'empty':'', 'one':'two'},
1002
self.assertEqual({'branch-nick': 'tree', 'empty': '', 'one': 'two'},
976
1005
def test_bundle_sorted_properties(self):
997
1026
tree.lock_write()
998
1027
self.addCleanup(tree.unlock)
1000
tree.add([''], ['TREE_ROOT'])
1029
tree.add([''], [b'TREE_ROOT'])
1001
1030
# Revisions themselves do not require anything about revision property
1002
1031
# keys, other than that they are a basestring, and do not contain
1004
1033
# However, Testaments assert than they are str(), and thus should not
1006
tree.commit('One', rev_id='rev1',
1007
revprops={'omega':u'\u03a9', 'alpha':u'\u03b1'})
1035
tree.commit('One', rev_id=b'rev1',
1036
revprops={u'omega': u'\u03a9', u'alpha': u'\u03b1'})
1008
1037
self.b1 = tree.branch
1009
bundle_sio, revision_ids = self.create_bundle_text('null:', 'rev1')
1038
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1010
1039
bundle = read_bundle(bundle_sio)
1011
1040
revision_info = bundle.revisions[0]
1012
self.assertEqual('rev1', revision_info.revision_id)
1041
self.assertEqual(b'rev1', revision_info.revision_id)
1013
1042
rev = revision_info.as_revision()
1014
self.assertEqual({'branch-nick':'tree', 'omega':u'\u03a9',
1015
'alpha':u'\u03b1'}, rev.properties)
1043
self.assertEqual({'branch-nick': 'tree', 'omega': u'\u03a9',
1044
'alpha': u'\u03b1'}, rev.properties)
1017
1046
def test_bundle_with_ghosts(self):
1018
1047
tree = self.make_branch_and_tree('tree')
1019
1048
self.b1 = tree.branch
1020
self.build_tree_contents([('tree/file', 'content1')])
1049
self.build_tree_contents([('tree/file', b'content1')])
1021
1050
tree.add(['file'])
1022
1051
tree.commit('rev1')
1023
self.build_tree_contents([('tree/file', 'content2')])
1024
tree.add_parent_tree_id('ghost')
1025
tree.commit('rev2', rev_id='rev2')
1026
bundle = self.get_valid_bundle('null:', 'rev2')
1052
self.build_tree_contents([('tree/file', b'content2')])
1053
tree.add_parent_tree_id(b'ghost')
1054
tree.commit('rev2', rev_id=b'rev2')
1055
bundle = self.get_valid_bundle(b'null:', b'rev2')
1028
1057
def make_simple_tree(self, format=None):
1029
1058
tree = self.make_branch_and_tree('b1', format=format)
1035
1064
def test_across_serializers(self):
1036
1065
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])
1066
tree.commit('hello', rev_id=b'rev1')
1067
tree.commit('hello', rev_id=b'rev2')
1068
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev2')[0])
1040
1069
repo = self.make_repository('repo', format='dirstate-with-subtree')
1041
1070
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"')
1071
inv_text = repo._get_inventory_xml(b'rev2')
1072
self.assertNotContainsRe(inv_text, b'format="5"')
1073
self.assertContainsRe(inv_text, b'format="7"')
1046
1075
def make_repo_with_installed_revisions(self):
1047
1076
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])
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])
1051
1080
repo = self.make_repository('repo', format='dirstate-with-subtree')
1052
1081
bundle.install_revisions(repo)
1055
1084
def test_across_models(self):
1056
1085
repo = self.make_repo_with_installed_revisions()
1057
inv = repo.get_inventory('rev2')
1058
self.assertEqual('rev2', inv.root.revision)
1086
inv = repo.get_inventory(b'rev2')
1087
self.assertEqual(b'rev2', inv.root.revision)
1059
1088
root_id = inv.root.file_id
1060
1089
repo.lock_read()
1061
1090
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')]))
1091
self.assertEqual({(root_id, b'rev1'): (),
1092
(root_id, b'rev2'): ((root_id, b'rev1'),)},
1093
repo.texts.get_parent_map([(root_id, b'rev1'), (root_id, b'rev2')]))
1066
1095
def test_inv_hash_across_serializers(self):
1067
1096
repo = self.make_repo_with_installed_revisions()
1068
recorded_inv_sha1 = repo.get_revision('rev2').inventory_sha1
1069
xml = repo._get_inventory_xml('rev2')
1097
recorded_inv_sha1 = repo.get_revision(b'rev2').inventory_sha1
1098
xml = repo._get_inventory_xml(b'rev2')
1070
1099
self.assertEqual(osutils.sha_string(xml), recorded_inv_sha1)
1072
1101
def test_across_models_incompatible(self):
1073
1102
tree = self.make_simple_tree('dirstate-with-subtree')
1074
tree.commit('hello', rev_id='rev1')
1075
tree.commit('hello', rev_id='rev2')
1103
tree.commit('hello', rev_id=b'rev1')
1104
tree.commit('hello', rev_id=b'rev2')
1077
bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
1106
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev1')[0])
1078
1107
except errors.IncompatibleBundleFormat:
1079
1108
raise tests.TestSkipped("Format 0.8 doesn't work with knit3")
1080
1109
repo = self.make_repository('repo', format='knit')
1081
1110
bundle.install_revisions(repo)
1083
bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
1112
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev2')[0])
1084
1113
self.assertRaises(errors.IncompatibleRevision,
1085
1114
bundle.install_revisions, repo)
1087
1116
def test_get_merge_request(self):
1088
1117
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])
1118
tree.commit('hello', rev_id=b'rev1')
1119
tree.commit('hello', rev_id=b'rev2')
1120
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev1')[0])
1092
1121
result = bundle.get_merge_request(tree.branch.repository)
1093
self.assertEqual((None, 'rev1', 'inapplicable'), result)
1122
self.assertEqual((None, b'rev1', 'inapplicable'), result)
1095
1124
def test_with_subtree(self):
1096
1125
tree = self.make_branch_and_tree('tree',
1116
1145
self.tree1 = self.make_branch_and_tree('tree')
1117
1146
self.b1 = self.tree1.branch
1119
self.tree1.commit('Revision/id/with/slashes', rev_id='rev/id')
1148
self.tree1.commit('Revision/id/with/slashes', rev_id=b'rev/id')
1120
1149
except ValueError:
1121
1150
raise tests.TestSkipped(
1122
1151
"Repository doesn't support revision ids with slashes")
1123
bundle = self.get_valid_bundle('null:', 'rev/id')
1152
bundle = self.get_valid_bundle(b'null:', b'rev/id')
1125
1154
def test_skip_file(self):
1126
1155
"""Make sure we don't accidentally write to the wrong versionedfile"""
1127
1156
self.tree1 = self.make_branch_and_tree('tree')
1128
1157
self.b1 = self.tree1.branch
1129
1158
# 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')])
1159
self.build_tree_contents([('tree/file2', b'contents1')])
1160
self.tree1.add('file2', b'file2-id')
1161
self.tree1.commit('rev1', rev_id=b'reva')
1162
self.build_tree_contents([('tree/file3', b'contents2')])
1134
1163
# rev2 is present in bundle, and done by fetch
1135
1164
# having file1 in the bunle causes file1's versionedfile to be opened.
1136
self.tree1.add('file3', 'file3-id')
1137
self.tree1.commit('rev2')
1165
self.tree1.add('file3', b'file3-id')
1166
rev2 = self.tree1.commit('rev2')
1138
1167
# 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')
1168
target = self.tree1.controldir.sprout('target').open_workingtree()
1169
self.build_tree_contents([('tree/file2', b'contents3')])
1170
self.tree1.commit('rev3', rev_id=b'rev3')
1171
bundle = self.get_valid_bundle(b'reva', b'rev3')
1143
1172
if getattr(bundle, 'get_bundle_reader', None) is None:
1144
1173
raise tests.TestSkipped('Bundle format cannot provide reader')
1145
# be sure that file1 comes before file2
1146
for b, m, k, r, f in bundle.get_bundle_reader().iter_records():
1149
self.assertNotEqual(f, 'file2-id')
1175
(f, r) for b, m, k, r, f in bundle.get_bundle_reader().iter_records()
1178
{(b'file2-id', b'rev3'), (b'file3-id', rev2)}, file_ids)
1150
1179
bundle.install_revisions(target.branch.repository)
1402
1435
line = bundle_file.readline()
1403
1436
line = bundle_file.readline()
1404
1437
lines = bundle_file.readlines()
1405
return ''.join(lines).decode('bz2')
1438
return bz2.decompress(b''.join(lines))
1407
1440
def test_copy_signatures(self):
1408
1441
tree_a = self.make_branch_and_tree('tree_a')
1410
import bzrlib.commit as commit
1411
oldstrategy = bzrlib.gpg.GPGStrategy
1443
import breezy.commit as commit
1444
oldstrategy = breezy.gpg.GPGStrategy
1412
1445
branch = tree_a.branch
1413
1446
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'))
1447
tree_a.commit("base", allow_pointless=True, rev_id=b'A')
1448
self.assertFalse(branch.repository.has_signature_for_revision_id(b'A'))
1417
from bzrlib.testament import Testament
1450
from ..bzr.testament import Testament
1418
1451
# 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",
1422
allow_pointless=True,
1424
working_tree=tree_a)
1452
breezy.gpg.GPGStrategy = breezy.gpg.LoopbackGPGStrategy
1453
new_config = test_commit.MustSignConfig()
1454
commit.Commit(config_stack=new_config).commit(message="base",
1455
allow_pointless=True,
1457
working_tree=tree_a)
1425
1459
def sign(text):
1426
return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
1427
self.assertTrue(repo_a.has_signature_for_revision_id('B'))
1460
return breezy.gpg.LoopbackGPGStrategy(None).sign(text)
1461
self.assertTrue(repo_a.has_signature_for_revision_id(b'B'))
1429
bzrlib.gpg.GPGStrategy = oldstrategy
1463
breezy.gpg.GPGStrategy = oldstrategy
1430
1464
tree_b = self.make_branch_and_tree('tree_b')
1431
1465
repo_b = tree_b.branch.repository
1433
1467
serializer = BundleSerializerV4('4')
1434
serializer.write(tree_a.branch.repository, ['A', 'B'], {}, s)
1468
with tree_a.lock_read():
1469
serializer.write_bundle(
1470
tree_a.branch.repository, b'B', b'null:', s)
1436
1472
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'))
1473
self.assertTrue(repo_b.has_signature_for_revision_id(b'B'))
1474
self.assertEqual(repo_b.get_signature_text(b'B'),
1475
repo_a.get_signature_text(b'B'))
1441
1477
# ensure repeat installs are harmless
1442
1478
install_bundle(repo_b, serializer.read(s))
1445
class V4WeaveBundleTester(V4BundleTester):
1447
def bzrdir_format(self):
1451
1481
class V4_2aBundleTester(V4BundleTester):
1453
1483
def bzrdir_format(self):
1480
1510
def make_merged_branch(self):
1481
1511
builder = self.make_branch_builder('source')
1482
1512
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')),
1513
builder.build_snapshot(None, [
1514
('add', ('', b'root-id', 'directory', None)),
1515
('add', ('file', b'file-id', 'file', b'original content\n')),
1516
], revision_id=b'a@cset-0-1')
1517
builder.build_snapshot([b'a@cset-0-1'], [
1518
('modify', ('file', b'new-content\n')),
1519
], revision_id=b'a@cset-0-2a')
1520
builder.build_snapshot([b'a@cset-0-1'], [
1521
('add', ('other-file', b'file2-id', 'file', b'file2-content\n')),
1522
], revision_id=b'a@cset-0-2b')
1523
builder.build_snapshot([b'a@cset-0-2a', b'a@cset-0-2b'], [
1524
('add', ('other-file', b'file2-id', 'file', b'file2-content\n')),
1525
], revision_id=b'a@cset-0-3')
1496
1526
builder.finish_series()
1497
1527
self.b1 = builder.get_branch()
1498
1528
self.b1.lock_read()
1513
1543
def test_single_inventory_multiple_parents_as_xml(self):
1514
1544
self.make_merged_branch()
1515
sio = self.make_bundle_just_inventories('a@cset-0-1', 'a@cset-0-3',
1545
sio = self.make_bundle_just_inventories(b'a@cset-0-1', b'a@cset-0-3',
1517
1547
reader = v4.BundleReader(sio, stream_input=False)
1518
1548
records = list(reader.iter_records())
1519
1549
self.assertEqual(1, len(records))
1520
1550
(bytes, metadata, repo_kind, revision_id,
1521
1551
file_id) = records[0]
1522
1552
self.assertIs(None, file_id)
1523
self.assertEqual('a@cset-0-3', revision_id)
1553
self.assertEqual(b'a@cset-0-3', revision_id)
1524
1554
self.assertEqual('inventory', repo_kind)
1525
self.assertEqual({'parents': ['a@cset-0-2a', 'a@cset-0-2b'],
1526
'sha1': '09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1527
'storage_kind': 'mpdiff',
1555
self.assertEqual({b'parents': [b'a@cset-0-2a', b'a@cset-0-2b'],
1556
b'sha1': b'09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1557
b'storage_kind': b'mpdiff',
1529
1559
# We should have an mpdiff that takes some lines from both parents.
1530
1560
self.assertEqualDiff(
1532
'<inventory format="10" revision_id="a@cset-0-3">\n'
1535
'c 1 3 3 2\n', bytes)
1562
b'<inventory format="10" revision_id="a@cset-0-3">\n'
1565
b'c 1 3 3 2\n', bytes)
1537
1567
def test_single_inv_no_parents_as_xml(self):
1538
1568
self.make_merged_branch()
1539
sio = self.make_bundle_just_inventories('null:', 'a@cset-0-1',
1569
sio = self.make_bundle_just_inventories(b'null:', b'a@cset-0-1',
1541
1571
reader = v4.BundleReader(sio, stream_input=False)
1542
1572
records = list(reader.iter_records())
1543
1573
self.assertEqual(1, len(records))
1544
1574
(bytes, metadata, repo_kind, revision_id,
1545
1575
file_id) = records[0]
1546
1576
self.assertIs(None, file_id)
1547
self.assertEqual('a@cset-0-1', revision_id)
1577
self.assertEqual(b'a@cset-0-1', revision_id)
1548
1578
self.assertEqual('inventory', repo_kind)
1549
self.assertEqual({'parents': [],
1550
'sha1': 'a13f42b142d544aac9b085c42595d304150e31a2',
1551
'storage_kind': 'mpdiff',
1579
self.assertEqual({b'parents': [],
1580
b'sha1': b'a13f42b142d544aac9b085c42595d304150e31a2',
1581
b'storage_kind': b'mpdiff',
1553
1583
# We should have an mpdiff that takes some lines from both parents.
1554
1584
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'
1586
b'<inventory format="10" revision_id="a@cset-0-1">\n'
1587
b'<directory file_id="root-id" name=""'
1588
b' revision="a@cset-0-1" />\n'
1589
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'
1566
1596
def test_multiple_inventories_as_xml(self):
1567
1597
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'])
1598
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'])
1570
1600
reader = v4.BundleReader(sio, stream_input=False)
1571
1601
records = list(reader.iter_records())
1572
1602
self.assertEqual(3, len(records))
1573
1603
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'],
1604
self.assertEqual([b'a@cset-0-2a', b'a@cset-0-2b', b'a@cset-0-3'],
1576
1606
metadata_2a = records[0][1]
1577
self.assertEqual({'parents': ['a@cset-0-1'],
1578
'sha1': '1e105886d62d510763e22885eec733b66f5f09bf',
1579
'storage_kind': 'mpdiff',
1607
self.assertEqual({b'parents': [b'a@cset-0-1'],
1608
b'sha1': b'1e105886d62d510763e22885eec733b66f5f09bf',
1609
b'storage_kind': b'mpdiff',
1581
1611
metadata_2b = records[1][1]
1582
self.assertEqual({'parents': ['a@cset-0-1'],
1583
'sha1': 'f03f12574bdb5ed2204c28636c98a8547544ccd8',
1584
'storage_kind': 'mpdiff',
1612
self.assertEqual({b'parents': [b'a@cset-0-1'],
1613
b'sha1': b'f03f12574bdb5ed2204c28636c98a8547544ccd8',
1614
b'storage_kind': b'mpdiff',
1586
1616
metadata_3 = records[2][1]
1587
self.assertEqual({'parents': ['a@cset-0-2a', 'a@cset-0-2b'],
1588
'sha1': '09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1589
'storage_kind': 'mpdiff',
1617
self.assertEqual({b'parents': [b'a@cset-0-2a', b'a@cset-0-2b'],
1618
b'sha1': b'09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1619
b'storage_kind': b'mpdiff',
1591
1621
bytes_2a = records[0][0]
1592
1622
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)
1624
b'<inventory format="10" revision_id="a@cset-0-2a">\n'
1628
b'<file file_id="file-id" name="file" parent_id="root-id"'
1629
b' revision="a@cset-0-2a"'
1630
b' text_sha1="50f545ff40e57b6924b1f3174b267ffc4576e9a9"'
1631
b' text_size="12" />\n'
1633
b'c 0 3 3 1\n', bytes_2a)
1604
1634
bytes_2b = records[1][0]
1605
1635
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)
1637
b'<inventory format="10" revision_id="a@cset-0-2b">\n'
1641
b'<file file_id="file2-id" name="other-file" parent_id="root-id"'
1642
b' revision="a@cset-0-2b"'
1643
b' text_sha1="b46c0c8ea1e5ef8e46fc8894bfd4752a88ec939e"'
1644
b' text_size="14" />\n'
1646
b'c 0 3 4 1\n', bytes_2b)
1617
1647
bytes_3 = records[2][0]
1618
1648
self.assertEqualDiff(
1620
'<inventory format="10" revision_id="a@cset-0-3">\n'
1623
'c 1 3 3 2\n', bytes_3)
1650
b'<inventory format="10" revision_id="a@cset-0-3">\n'
1653
b'c 1 3 3 2\n', bytes_3)
1625
1655
def test_creating_bundle_preserves_chk_pages(self):
1626
1656
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)
1657
target = self.b1.controldir.sprout('target',
1658
revision_id=b'a@cset-0-2a').open_branch()
1659
bundle_txt, rev_ids = self.create_bundle_text(b'a@cset-0-2a',
1661
self.assertEqual(set([b'a@cset-0-2b', b'a@cset-0-3']), set(rev_ids))
1632
1662
bundle = read_bundle(bundle_txt)
1633
1663
target.lock_write()
1634
1664
self.addCleanup(target.unlock)
1635
1665
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')
1666
inv1 = next(self.b1.repository.inventories.get_record_stream([
1667
(b'a@cset-0-3',)], 'unordered',
1668
True)).get_bytes_as('fulltext')
1669
inv2 = next(target.repository.inventories.get_record_stream([
1670
(b'a@cset-0-3',)], 'unordered',
1671
True)).get_bytes_as('fulltext')
1642
1672
self.assertEqualDiff(inv1, inv2)
1740
1770
class TestBundleWriterReader(tests.TestCase):
1742
1772
def test_roundtrip_record(self):
1743
fileobj = StringIO()
1744
1774
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')
1776
writer.add_info_record({b'foo': b'bar'})
1777
writer._add_record(b"Record body", {b'parents': [b'1', b'3'],
1778
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1750
1780
fileobj.seek(0)
1751
1781
reader = v4.BundleReader(fileobj, stream_input=True)
1752
1782
record_iter = reader.iter_records()
1753
record = record_iter.next()
1754
self.assertEqual((None, {'foo': 'bar', 'storage_kind': 'header'},
1755
'info', None, None), record)
1756
record = record_iter.next()
1757
self.assertEqual(("Record body", {'storage_kind': 'fulltext',
1758
'parents': ['1', '3']}, 'file', 'revid', 'fileid'),
1783
record = next(record_iter)
1784
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1785
'info', None, None), record)
1786
record = next(record_iter)
1787
self.assertEqual((b"Record body", {b'storage_kind': b'fulltext',
1788
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1761
1791
def test_roundtrip_record_memory_hungry(self):
1762
fileobj = StringIO()
1763
1793
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')
1795
writer.add_info_record({b'foo': b'bar'})
1796
writer._add_record(b"Record body", {b'parents': [b'1', b'3'],
1797
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1769
1799
fileobj.seek(0)
1770
1800
reader = v4.BundleReader(fileobj, stream_input=False)
1771
1801
record_iter = reader.iter_records()
1772
record = record_iter.next()
1773
self.assertEqual((None, {'foo': 'bar', 'storage_kind': 'header'},
1774
'info', None, None), record)
1775
record = record_iter.next()
1776
self.assertEqual(("Record body", {'storage_kind': 'fulltext',
1777
'parents': ['1', '3']}, 'file', 'revid', 'fileid'),
1802
record = next(record_iter)
1803
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1804
'info', None, None), record)
1805
record = next(record_iter)
1806
self.assertEqual((b"Record body", {b'storage_kind': b'fulltext',
1807
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1780
1810
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',
1786
v4.BundleWriter.encode_name('info', None, None))
1811
self.assertEqual(b'revision/rev1',
1812
v4.BundleWriter.encode_name('revision', b'rev1'))
1813
self.assertEqual(b'file/rev//1/file-id-1',
1814
v4.BundleWriter.encode_name('file', b'rev/1', b'file-id-1'))
1815
self.assertEqual(b'info',
1816
v4.BundleWriter.encode_name('info', None, None))
1788
1818
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'))
1819
self.assertEqual(('revision', b'rev1', None),
1820
v4.BundleReader.decode_name(b'revision/rev1'))
1821
self.assertEqual(('file', b'rev/1', b'file-id-1'),
1822
v4.BundleReader.decode_name(b'file/rev//1/file-id-1'))
1793
1823
self.assertEqual(('info', None, None),
1794
v4.BundleReader.decode_name('info'))
1824
v4.BundleReader.decode_name(b'info'))
1796
1826
def test_too_many_names(self):
1797
fileobj = StringIO()
1798
1828
writer = v4.BundleWriter(fileobj)
1800
writer.add_info_record(foo='bar')
1801
writer._container.add_bytes_record('blah', ['two', 'names'])
1830
writer.add_info_record({b'foo': b'bar'})
1831
writer._container.add_bytes_record(b'blah', [(b'two', ), (b'names', )])
1803
1833
fileobj.seek(0)
1804
1834
record_iter = v4.BundleReader(fileobj).iter_records()
1805
record = record_iter.next()
1806
self.assertEqual((None, {'foo': 'bar', 'storage_kind': 'header'},
1807
'info', None, None), record)
1808
self.assertRaises(errors.BadBundle, record_iter.next)
1835
record = next(record_iter)
1836
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1837
'info', None, None), record)
1838
self.assertRaises(errors.BadBundle, next, record_iter)
1811
1841
class TestReadMergeableFromUrl(tests.TestCaseWithTransport):