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
608
self.tree1 = self.make_branch_and_tree('b1')
575
609
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')
611
self.build_tree_contents([('b1/one', b'one\n')])
612
self.tree1.add('one', b'one-id')
613
self.tree1.set_root_id(b'root-id')
614
self.tree1.commit('add one', rev_id=b'a@cset-0-1')
582
bundle = self.get_valid_bundle('null:', 'a@cset-0-1')
616
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-1')
584
618
# Make sure we can handle files with spaces, tabs, other
585
619
# bogus characters
586
620
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')])
621
'b1/with space.txt', 'b1/dir/', 'b1/dir/filein subdir.c', 'b1/dir/WithCaps.txt', 'b1/dir/ pre space', 'b1/sub/', 'b1/sub/sub/', 'b1/sub/sub/nonempty.txt'
623
self.build_tree_contents([('b1/sub/sub/emptyfile.txt', b''),
624
('b1/dir/nolastnewline.txt', b'bloop')])
598
625
tt = TreeTransform(self.tree1)
599
tt.new_file('executable', tt.root, '#!/bin/sh\n', 'exe-1', True)
626
tt.new_file('executable', tt.root, [b'#!/bin/sh\n'], b'exe-1', True)
601
628
# have to fix length of file-id so that we can predictably rewrite
602
629
# a (length-prefixed) record containing it later.
603
self.tree1.add('with space.txt', 'withspace-id')
630
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')
632
'dir', 'dir/filein subdir.c', 'dir/WithCaps.txt', 'dir/ pre space', 'dir/nolastnewline.txt', 'sub', 'sub/sub', 'sub/sub/nonempty.txt', 'sub/sub/emptyfile.txt'
634
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')
636
bundle = self.get_valid_bundle(b'a@cset-0-1', b'a@cset-0-2')
619
638
# Check a rollup bundle
620
bundle = self.get_valid_bundle('null:', 'a@cset-0-2')
639
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-2')
622
641
# Now delete entries
623
642
self.tree1.remove(
624
['sub/sub/nonempty.txt'
625
, 'sub/sub/emptyfile.txt'
643
['sub/sub/nonempty.txt', 'sub/sub/emptyfile.txt', 'sub/sub'
628
645
tt = TreeTransform(self.tree1)
629
trans_id = tt.trans_id_tree_file_id('exe-1')
646
trans_id = tt.trans_id_tree_path('executable')
630
647
tt.set_executability(False, trans_id)
632
self.tree1.commit('removed', rev_id='a@cset-0-3')
649
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')
651
bundle = self.get_valid_bundle(b'a@cset-0-2', b'a@cset-0-3')
635
652
self.assertRaises((errors.TestamentMismatch,
636
errors.VersionedFileInvalidChecksum,
637
errors.BadBundle), self.get_invalid_bundle,
638
'a@cset-0-2', 'a@cset-0-3')
653
errors.VersionedFileInvalidChecksum,
654
errors.BadBundle), self.get_invalid_bundle,
655
b'a@cset-0-2', b'a@cset-0-3')
639
656
# Check a rollup bundle
640
bundle = self.get_valid_bundle('null:', 'a@cset-0-3')
657
bundle = self.get_valid_bundle(b'null:', b'a@cset-0-3')
642
659
# Now move the directory
643
660
self.tree1.rename_one('dir', 'sub/dir')
644
self.tree1.commit('rename dir', rev_id='a@cset-0-4')
661
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')
663
bundle = self.get_valid_bundle(b'a@cset-0-3', b'a@cset-0-4')
647
664
# Check a rollup bundle
648
bundle = self.get_valid_bundle('null:', 'a@cset-0-4')
665
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')
668
with open('b1/sub/dir/WithCaps.txt', 'ab') as f:
669
f.write(b'\nAdding some text\n')
670
with open('b1/sub/dir/ pre space', 'ab') as f:
672
b'\r\nAdding some\r\nDOS format lines\r\n')
673
with open('b1/sub/dir/nolastnewline.txt', 'ab') as f:
655
675
self.tree1.rename_one('sub/dir/ pre space',
656
676
'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')
677
self.tree1.commit('Modified files', rev_id=b'a@cset-0-5')
678
bundle = self.get_valid_bundle(b'a@cset-0-4', b'a@cset-0-5')
660
680
self.tree1.rename_one('sub/dir/WithCaps.txt', 'temp')
661
681
self.tree1.rename_one('with space.txt', 'WithCaps.txt')
662
682
self.tree1.rename_one('temp', 'with space.txt')
663
self.tree1.commit(u'swap filenames', rev_id='a@cset-0-6',
683
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')
685
bundle = self.get_valid_bundle(b'a@cset-0-5', b'a@cset-0-6')
686
other = self.get_checkout(b'a@cset-0-5')
667
687
tree1_inv = get_inventory_text(self.tree1.branch.repository,
669
689
tree2_inv = get_inventory_text(other.branch.repository,
671
691
self.assertEqualDiff(tree1_inv, tree2_inv)
672
692
other.rename_one('sub/dir/nolastnewline.txt', 'sub/nolastnewline.txt')
673
other.commit('rename file', rev_id='a@cset-0-6b')
693
other.commit('rename file', rev_id=b'a@cset-0-6b')
674
694
self.tree1.merge_from_branch(other.branch)
675
self.tree1.commit(u'Merge', rev_id='a@cset-0-7',
695
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')
697
bundle = self.get_valid_bundle(b'a@cset-0-6', b'a@cset-0-7')
679
699
def _test_symlink_bundle(self, link_name, link_target, new_link_target):
682
self.requireFeature(tests.SymlinkFeature)
702
self.requireFeature(features.SymlinkFeature)
683
703
self.tree1 = self.make_branch_and_tree('b1')
684
704
self.b1 = self.tree1.branch
686
706
tt = TreeTransform(self.tree1)
687
707
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:
709
self.tree1.commit('add symlink', rev_id=b'l@cset-0-1')
710
bundle = self.get_valid_bundle(b'null:', b'l@cset-0-1')
711
if getattr(bundle, 'revision_tree', None) is not None:
692
712
# 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))
713
bund_tree = bundle.revision_tree(self.b1.repository, b'l@cset-0-1')
715
link_target, bund_tree.get_symlink_target(link_name))
696
717
tt = TreeTransform(self.tree1)
697
trans_id = tt.trans_id_tree_file_id(link_id)
718
trans_id = tt.trans_id_tree_path(link_name)
698
719
tt.adjust_path('link2', tt.root, trans_id)
699
720
tt.delete_contents(trans_id)
700
721
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:
723
self.tree1.commit('rename and change symlink', rev_id=b'l@cset-0-2')
724
bundle = self.get_valid_bundle(b'l@cset-0-1', b'l@cset-0-2')
725
if getattr(bundle, 'revision_tree', None) is not None:
705
726
# Not all bundle formats supports revision_tree
706
bund_tree = bundle.revision_tree(self.b1.repository, 'l@cset-0-2')
727
bund_tree = bundle.revision_tree(self.b1.repository, b'l@cset-0-2')
707
728
self.assertEqual(new_link_target,
708
bund_tree.get_symlink_target(link_id))
729
bund_tree.get_symlink_target('link2'))
710
731
tt = TreeTransform(self.tree1)
711
trans_id = tt.trans_id_tree_file_id(link_id)
732
trans_id = tt.trans_id_tree_path('link2')
712
733
tt.delete_contents(trans_id)
713
734
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')
736
self.tree1.commit('just change symlink target', rev_id=b'l@cset-0-3')
737
bundle = self.get_valid_bundle(b'l@cset-0-2', b'l@cset-0-3')
718
739
tt = TreeTransform(self.tree1)
719
trans_id = tt.trans_id_tree_file_id(link_id)
740
trans_id = tt.trans_id_tree_path('link2')
720
741
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')
743
self.tree1.commit('Delete symlink', rev_id=b'l@cset-0-4')
744
bundle = self.get_valid_bundle(b'l@cset-0-3', b'l@cset-0-4')
725
746
def test_symlink_bundle(self):
726
747
self._test_symlink_bundle('link', 'bar/foo', 'mars')
728
749
def test_unicode_symlink_bundle(self):
729
self.requireFeature(tests.UnicodeFilenameFeature)
750
self.requireFeature(features.UnicodeFilenameFeature)
730
751
self._test_symlink_bundle(u'\N{Euro Sign}link',
731
752
u'bar/\N{Euro Sign}foo',
732
753
u'mars\N{Euro Sign}')
737
758
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',
761
tt.new_file('file', tt.root, [
762
b'\x00\n\x00\r\x01\n\x02\r\xff'], b'binary-1')
763
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')
766
self.tree1.commit('add binary', rev_id=b'b@cset-0-1')
767
self.get_valid_bundle(b'null:', b'b@cset-0-1')
748
770
tt = TreeTransform(self.tree1)
749
trans_id = tt.trans_id_tree_file_id('binary-1')
771
trans_id = tt.trans_id_tree_path('file')
750
772
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')
774
self.tree1.commit('delete binary', rev_id=b'b@cset-0-2')
775
self.get_valid_bundle(b'b@cset-0-1', b'b@cset-0-2')
755
777
# Rename & modify
756
778
tt = TreeTransform(self.tree1)
757
trans_id = tt.trans_id_tree_file_id('binary-2')
779
trans_id = tt.trans_id_tree_path('file2')
758
780
tt.adjust_path('file3', tt.root, trans_id)
759
781
tt.delete_contents(trans_id)
760
tt.create_file('file\rcontents\x00\n\x00', trans_id)
782
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')
784
self.tree1.commit('rename and modify binary', rev_id=b'b@cset-0-3')
785
self.get_valid_bundle(b'b@cset-0-2', b'b@cset-0-3')
766
788
tt = TreeTransform(self.tree1)
767
trans_id = tt.trans_id_tree_file_id('binary-2')
789
trans_id = tt.trans_id_tree_path('file3')
768
790
tt.delete_contents(trans_id)
769
tt.create_file('\x00file\rcontents', trans_id)
791
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')
793
self.tree1.commit('just modify binary', rev_id=b'b@cset-0-4')
794
self.get_valid_bundle(b'b@cset-0-3', b'b@cset-0-4')
775
self.get_valid_bundle('null:', 'b@cset-0-4')
797
self.get_valid_bundle(b'null:', b'b@cset-0-4')
777
799
def test_last_modified(self):
778
800
self.tree1 = self.make_branch_and_tree('b1')
779
801
self.b1 = self.tree1.branch
780
802
tt = TreeTransform(self.tree1)
781
tt.new_file('file', tt.root, 'file', 'file')
803
tt.new_file('file', tt.root, [b'file'], b'file')
783
self.tree1.commit('create file', rev_id='a@lmod-0-1')
805
self.tree1.commit('create file', rev_id=b'a@lmod-0-1')
785
807
tt = TreeTransform(self.tree1)
786
trans_id = tt.trans_id_tree_file_id('file')
808
trans_id = tt.trans_id_tree_path('file')
787
809
tt.delete_contents(trans_id)
788
tt.create_file('file2', trans_id)
810
tt.create_file([b'file2'], trans_id)
790
self.tree1.commit('modify text', rev_id='a@lmod-0-2a')
812
self.tree1.commit('modify text', rev_id=b'a@lmod-0-2a')
792
other = self.get_checkout('a@lmod-0-1')
814
other = self.get_checkout(b'a@lmod-0-1')
793
815
tt = TreeTransform(other)
794
trans_id = tt.trans_id_tree_file_id('file')
816
trans_id = tt.trans_id_tree_path('file2')
795
817
tt.delete_contents(trans_id)
796
tt.create_file('file2', trans_id)
818
tt.create_file([b'file2'], trans_id)
798
other.commit('modify text in another tree', rev_id='a@lmod-0-2b')
820
other.commit('modify text in another tree', rev_id=b'a@lmod-0-2b')
799
821
self.tree1.merge_from_branch(other.branch)
800
self.tree1.commit(u'Merge', rev_id='a@lmod-0-3',
822
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')
824
self.tree1.commit(u'Merge', rev_id=b'a@lmod-0-4')
825
bundle = self.get_valid_bundle(b'a@lmod-0-2a', b'a@lmod-0-4')
805
827
def test_hide_history(self):
806
828
self.tree1 = self.make_branch_and_tree('b1')
807
829
self.b1 = self.tree1.branch
809
open('b1/one', 'wb').write('one\n')
831
with open('b1/one', 'wb') as f:
810
833
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')
834
self.tree1.commit('add file', rev_id=b'a@cset-0-1')
835
with open('b1/one', 'wb') as f:
837
self.tree1.commit('modify', rev_id=b'a@cset-0-2')
838
with open('b1/one', 'wb') as f:
840
self.tree1.commit('modify', rev_id=b'a@cset-0-3')
841
bundle_file = BytesIO()
842
rev_ids = write_bundle(self.tree1.branch.repository, b'a@cset-0-3',
843
b'a@cset-0-1', bundle_file, format=self.format)
844
self.assertNotContainsRe(bundle_file.getvalue(), b'\btwo\b')
845
self.assertContainsRe(self.get_raw(bundle_file), b'one')
846
self.assertContainsRe(self.get_raw(bundle_file), b'three')
823
848
def test_bundle_same_basis(self):
824
849
"""Ensure using the basis as the target doesn't cause an error"""
825
850
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)
851
self.tree1.commit('add file', rev_id=b'a@cset-0-1')
852
bundle_file = BytesIO()
853
rev_ids = write_bundle(self.tree1.branch.repository, b'a@cset-0-1',
854
b'a@cset-0-1', bundle_file)
832
857
def get_raw(bundle_file):
833
858
return bundle_file.getvalue()
835
860
def test_unicode_bundle(self):
836
self.requireFeature(tests.UnicodeFilenameFeature)
861
self.requireFeature(features.UnicodeFilenameFeature)
837
862
# Handle international characters
839
864
f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
842
867
self.b1 = self.tree1.branch
844
869
f.write((u'A file\n'
845
u'With international man of mystery\n'
846
u'William Dod\xe9\n').encode('utf-8'))
870
u'With international man of mystery\n'
871
u'William Dod\xe9\n').encode('utf-8'))
849
self.tree1.add([u'with Dod\N{Euro Sign}'], ['withdod-id'])
874
self.tree1.add([u'with Dod\N{Euro Sign}'], [b'withdod-id'])
850
875
self.tree1.commit(u'i18n commit from William Dod\xe9',
851
rev_id='i18n-1', committer=u'William Dod\xe9')
876
rev_id=b'i18n-1', committer=u'William Dod\xe9')
854
bundle = self.get_valid_bundle('null:', 'i18n-1')
879
bundle = self.get_valid_bundle(b'null:', b'i18n-1')
857
882
f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
858
883
f.write(u'Modified \xb5\n'.encode('utf8'))
860
self.tree1.commit(u'modified', rev_id='i18n-2')
885
self.tree1.commit(u'modified', rev_id=b'i18n-2')
862
bundle = self.get_valid_bundle('i18n-1', 'i18n-2')
887
bundle = self.get_valid_bundle(b'i18n-1', b'i18n-2')
865
890
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',
891
self.tree1.commit(u'renamed, the new i18n man', rev_id=b'i18n-3',
867
892
committer=u'Erik B\xe5gfors')
869
bundle = self.get_valid_bundle('i18n-2', 'i18n-3')
894
bundle = self.get_valid_bundle(b'i18n-2', b'i18n-3')
872
897
self.tree1.remove([u'B\N{Euro Sign}gfors'])
873
self.tree1.commit(u'removed', rev_id='i18n-4')
898
self.tree1.commit(u'removed', rev_id=b'i18n-4')
875
bundle = self.get_valid_bundle('i18n-3', 'i18n-4')
900
bundle = self.get_valid_bundle(b'i18n-3', b'i18n-4')
878
bundle = self.get_valid_bundle('null:', 'i18n-4')
903
bundle = self.get_valid_bundle(b'null:', b'i18n-4')
881
905
def test_whitespace_bundle(self):
882
906
if sys.platform in ('win32', 'cygwin'):
941
966
def test_bundle_root_id(self):
942
967
self.tree1 = self.make_branch_and_tree('b1')
943
968
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)
969
self.tree1.commit('message', rev_id=b'revid1')
970
bundle = self.get_valid_bundle(b'null:', b'revid1')
971
tree = self.get_bundle_tree(bundle, b'revid1')
972
root_revision = tree.get_file_revision(u'')
973
self.assertEqual(b'revid1', root_revision)
949
975
def test_install_revisions(self):
950
976
self.tree1 = self.make_branch_and_tree('b1')
951
977
self.b1 = self.tree1.branch
952
self.tree1.commit('message', rev_id='rev2a')
953
bundle = self.get_valid_bundle('null:', 'rev2a')
978
self.tree1.commit('message', rev_id=b'rev2a')
979
bundle = self.get_valid_bundle(b'null:', b'rev2a')
954
980
branch2 = self.make_branch('b2')
955
self.assertFalse(branch2.repository.has_revision('rev2a'))
981
self.assertFalse(branch2.repository.has_revision(b'rev2a'))
956
982
target_revision = bundle.install_revisions(branch2.repository)
957
self.assertTrue(branch2.repository.has_revision('rev2a'))
958
self.assertEqual('rev2a', target_revision)
983
self.assertTrue(branch2.repository.has_revision(b'rev2a'))
984
self.assertEqual(b'rev2a', target_revision)
960
986
def test_bundle_empty_property(self):
961
987
"""Test serializing revision properties with an empty value."""
962
988
tree = self.make_branch_and_memory_tree('tree')
963
989
tree.lock_write()
964
990
self.addCleanup(tree.unlock)
965
tree.add([''], ['TREE_ROOT'])
966
tree.commit('One', revprops={'one':'two', 'empty':''}, rev_id='rev1')
991
tree.add([''], [b'TREE_ROOT'])
992
tree.commit('One', revprops={u'one': 'two',
993
u'empty': ''}, rev_id=b'rev1')
967
994
self.b1 = tree.branch
968
bundle_sio, revision_ids = self.create_bundle_text('null:', 'rev1')
995
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
969
996
bundle = read_bundle(bundle_sio)
970
997
revision_info = bundle.revisions[0]
971
self.assertEqual('rev1', revision_info.revision_id)
998
self.assertEqual(b'rev1', revision_info.revision_id)
972
999
rev = revision_info.as_revision()
973
self.assertEqual({'branch-nick':'tree', 'empty':'', 'one':'two'},
1000
self.assertEqual({'branch-nick': 'tree', 'empty': '', 'one': 'two'},
976
1003
def test_bundle_sorted_properties(self):
997
1024
tree.lock_write()
998
1025
self.addCleanup(tree.unlock)
1000
tree.add([''], ['TREE_ROOT'])
1027
tree.add([''], [b'TREE_ROOT'])
1001
1028
# Revisions themselves do not require anything about revision property
1002
1029
# keys, other than that they are a basestring, and do not contain
1004
1031
# 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'})
1033
tree.commit('One', rev_id=b'rev1',
1034
revprops={u'omega': u'\u03a9', u'alpha': u'\u03b1'})
1008
1035
self.b1 = tree.branch
1009
bundle_sio, revision_ids = self.create_bundle_text('null:', 'rev1')
1036
bundle_sio, revision_ids = self.create_bundle_text(b'null:', b'rev1')
1010
1037
bundle = read_bundle(bundle_sio)
1011
1038
revision_info = bundle.revisions[0]
1012
self.assertEqual('rev1', revision_info.revision_id)
1039
self.assertEqual(b'rev1', revision_info.revision_id)
1013
1040
rev = revision_info.as_revision()
1014
self.assertEqual({'branch-nick':'tree', 'omega':u'\u03a9',
1015
'alpha':u'\u03b1'}, rev.properties)
1041
self.assertEqual({'branch-nick': 'tree', 'omega': u'\u03a9',
1042
'alpha': u'\u03b1'}, rev.properties)
1017
1044
def test_bundle_with_ghosts(self):
1018
1045
tree = self.make_branch_and_tree('tree')
1019
1046
self.b1 = tree.branch
1020
self.build_tree_contents([('tree/file', 'content1')])
1047
self.build_tree_contents([('tree/file', b'content1')])
1021
1048
tree.add(['file'])
1022
1049
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')
1050
self.build_tree_contents([('tree/file', b'content2')])
1051
tree.add_parent_tree_id(b'ghost')
1052
tree.commit('rev2', rev_id=b'rev2')
1053
bundle = self.get_valid_bundle(b'null:', b'rev2')
1028
1055
def make_simple_tree(self, format=None):
1029
1056
tree = self.make_branch_and_tree('b1', format=format)
1035
1062
def test_across_serializers(self):
1036
1063
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])
1064
tree.commit('hello', rev_id=b'rev1')
1065
tree.commit('hello', rev_id=b'rev2')
1066
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev2')[0])
1040
1067
repo = self.make_repository('repo', format='dirstate-with-subtree')
1041
1068
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"')
1069
inv_text = repo._get_inventory_xml(b'rev2')
1070
self.assertNotContainsRe(inv_text, b'format="5"')
1071
self.assertContainsRe(inv_text, b'format="7"')
1046
1073
def make_repo_with_installed_revisions(self):
1047
1074
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])
1075
tree.commit('hello', rev_id=b'rev1')
1076
tree.commit('hello', rev_id=b'rev2')
1077
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev2')[0])
1051
1078
repo = self.make_repository('repo', format='dirstate-with-subtree')
1052
1079
bundle.install_revisions(repo)
1055
1082
def test_across_models(self):
1056
1083
repo = self.make_repo_with_installed_revisions()
1057
inv = repo.get_inventory('rev2')
1058
self.assertEqual('rev2', inv.root.revision)
1084
inv = repo.get_inventory(b'rev2')
1085
self.assertEqual(b'rev2', inv.root.revision)
1059
1086
root_id = inv.root.file_id
1060
1087
repo.lock_read()
1061
1088
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')]))
1089
self.assertEqual({(root_id, b'rev1'): (),
1090
(root_id, b'rev2'): ((root_id, b'rev1'),)},
1091
repo.texts.get_parent_map([(root_id, b'rev1'), (root_id, b'rev2')]))
1066
1093
def test_inv_hash_across_serializers(self):
1067
1094
repo = self.make_repo_with_installed_revisions()
1068
recorded_inv_sha1 = repo.get_revision('rev2').inventory_sha1
1069
xml = repo._get_inventory_xml('rev2')
1095
recorded_inv_sha1 = repo.get_revision(b'rev2').inventory_sha1
1096
xml = repo._get_inventory_xml(b'rev2')
1070
1097
self.assertEqual(osutils.sha_string(xml), recorded_inv_sha1)
1072
1099
def test_across_models_incompatible(self):
1073
1100
tree = self.make_simple_tree('dirstate-with-subtree')
1074
tree.commit('hello', rev_id='rev1')
1075
tree.commit('hello', rev_id='rev2')
1101
tree.commit('hello', rev_id=b'rev1')
1102
tree.commit('hello', rev_id=b'rev2')
1077
bundle = read_bundle(self.create_bundle_text('null:', 'rev1')[0])
1104
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev1')[0])
1078
1105
except errors.IncompatibleBundleFormat:
1079
1106
raise tests.TestSkipped("Format 0.8 doesn't work with knit3")
1080
1107
repo = self.make_repository('repo', format='knit')
1081
1108
bundle.install_revisions(repo)
1083
bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
1110
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev2')[0])
1084
1111
self.assertRaises(errors.IncompatibleRevision,
1085
1112
bundle.install_revisions, repo)
1087
1114
def test_get_merge_request(self):
1088
1115
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])
1116
tree.commit('hello', rev_id=b'rev1')
1117
tree.commit('hello', rev_id=b'rev2')
1118
bundle = read_bundle(self.create_bundle_text(b'null:', b'rev1')[0])
1092
1119
result = bundle.get_merge_request(tree.branch.repository)
1093
self.assertEqual((None, 'rev1', 'inapplicable'), result)
1120
self.assertEqual((None, b'rev1', 'inapplicable'), result)
1095
1122
def test_with_subtree(self):
1096
1123
tree = self.make_branch_and_tree('tree',
1116
1143
self.tree1 = self.make_branch_and_tree('tree')
1117
1144
self.b1 = self.tree1.branch
1119
self.tree1.commit('Revision/id/with/slashes', rev_id='rev/id')
1146
self.tree1.commit('Revision/id/with/slashes', rev_id=b'rev/id')
1120
1147
except ValueError:
1121
1148
raise tests.TestSkipped(
1122
1149
"Repository doesn't support revision ids with slashes")
1123
bundle = self.get_valid_bundle('null:', 'rev/id')
1150
bundle = self.get_valid_bundle(b'null:', b'rev/id')
1125
1152
def test_skip_file(self):
1126
1153
"""Make sure we don't accidentally write to the wrong versionedfile"""
1127
1154
self.tree1 = self.make_branch_and_tree('tree')
1128
1155
self.b1 = self.tree1.branch
1129
1156
# 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')])
1157
self.build_tree_contents([('tree/file2', b'contents1')])
1158
self.tree1.add('file2', b'file2-id')
1159
self.tree1.commit('rev1', rev_id=b'reva')
1160
self.build_tree_contents([('tree/file3', b'contents2')])
1134
1161
# rev2 is present in bundle, and done by fetch
1135
1162
# having file1 in the bunle causes file1's versionedfile to be opened.
1136
self.tree1.add('file3', 'file3-id')
1137
self.tree1.commit('rev2')
1163
self.tree1.add('file3', b'file3-id')
1164
rev2 = self.tree1.commit('rev2')
1138
1165
# 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')
1166
target = self.tree1.controldir.sprout('target').open_workingtree()
1167
self.build_tree_contents([('tree/file2', b'contents3')])
1168
self.tree1.commit('rev3', rev_id=b'rev3')
1169
bundle = self.get_valid_bundle(b'reva', b'rev3')
1143
1170
if getattr(bundle, 'get_bundle_reader', None) is None:
1144
1171
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')
1173
(f, r) for b, m, k, r, f in bundle.get_bundle_reader().iter_records()
1176
{(b'file2-id', b'rev3'), (b'file3-id', rev2)}, file_ids)
1150
1177
bundle.install_revisions(target.branch.repository)
1402
1433
line = bundle_file.readline()
1403
1434
line = bundle_file.readline()
1404
1435
lines = bundle_file.readlines()
1405
return ''.join(lines).decode('bz2')
1436
return bz2.decompress(b''.join(lines))
1407
1438
def test_copy_signatures(self):
1408
1439
tree_a = self.make_branch_and_tree('tree_a')
1410
import bzrlib.commit as commit
1411
oldstrategy = bzrlib.gpg.GPGStrategy
1441
import breezy.commit as commit
1442
oldstrategy = breezy.gpg.GPGStrategy
1412
1443
branch = tree_a.branch
1413
1444
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'))
1445
tree_a.commit("base", allow_pointless=True, rev_id=b'A')
1446
self.assertFalse(branch.repository.has_signature_for_revision_id(b'A'))
1417
from bzrlib.testament import Testament
1448
from ..testament import Testament
1418
1449
# 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)
1450
breezy.gpg.GPGStrategy = breezy.gpg.LoopbackGPGStrategy
1451
new_config = test_commit.MustSignConfig()
1452
commit.Commit(config_stack=new_config).commit(message="base",
1453
allow_pointless=True,
1455
working_tree=tree_a)
1425
1457
def sign(text):
1426
return bzrlib.gpg.LoopbackGPGStrategy(None).sign(text)
1427
self.assertTrue(repo_a.has_signature_for_revision_id('B'))
1458
return breezy.gpg.LoopbackGPGStrategy(None).sign(text)
1459
self.assertTrue(repo_a.has_signature_for_revision_id(b'B'))
1429
bzrlib.gpg.GPGStrategy = oldstrategy
1461
breezy.gpg.GPGStrategy = oldstrategy
1430
1462
tree_b = self.make_branch_and_tree('tree_b')
1431
1463
repo_b = tree_b.branch.repository
1433
1465
serializer = BundleSerializerV4('4')
1434
serializer.write(tree_a.branch.repository, ['A', 'B'], {}, s)
1466
with tree_a.lock_read():
1467
serializer.write_bundle(
1468
tree_a.branch.repository, b'B', b'null:', s)
1436
1470
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'))
1471
self.assertTrue(repo_b.has_signature_for_revision_id(b'B'))
1472
self.assertEqual(repo_b.get_signature_text(b'B'),
1473
repo_a.get_signature_text(b'B'))
1441
1475
# ensure repeat installs are harmless
1442
1476
install_bundle(repo_b, serializer.read(s))
1445
class V4WeaveBundleTester(V4BundleTester):
1447
def bzrdir_format(self):
1451
1479
class V4_2aBundleTester(V4BundleTester):
1453
1481
def bzrdir_format(self):
1480
1508
def make_merged_branch(self):
1481
1509
builder = self.make_branch_builder('source')
1482
1510
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')),
1511
builder.build_snapshot(None, [
1512
('add', ('', b'root-id', 'directory', None)),
1513
('add', ('file', b'file-id', 'file', b'original content\n')),
1514
], revision_id=b'a@cset-0-1')
1515
builder.build_snapshot([b'a@cset-0-1'], [
1516
('modify', ('file', b'new-content\n')),
1517
], revision_id=b'a@cset-0-2a')
1518
builder.build_snapshot([b'a@cset-0-1'], [
1519
('add', ('other-file', b'file2-id', 'file', b'file2-content\n')),
1520
], revision_id=b'a@cset-0-2b')
1521
builder.build_snapshot([b'a@cset-0-2a', b'a@cset-0-2b'], [
1522
('add', ('other-file', b'file2-id', 'file', b'file2-content\n')),
1523
], revision_id=b'a@cset-0-3')
1496
1524
builder.finish_series()
1497
1525
self.b1 = builder.get_branch()
1498
1526
self.b1.lock_read()
1513
1541
def test_single_inventory_multiple_parents_as_xml(self):
1514
1542
self.make_merged_branch()
1515
sio = self.make_bundle_just_inventories('a@cset-0-1', 'a@cset-0-3',
1543
sio = self.make_bundle_just_inventories(b'a@cset-0-1', b'a@cset-0-3',
1517
1545
reader = v4.BundleReader(sio, stream_input=False)
1518
1546
records = list(reader.iter_records())
1519
1547
self.assertEqual(1, len(records))
1520
1548
(bytes, metadata, repo_kind, revision_id,
1521
1549
file_id) = records[0]
1522
1550
self.assertIs(None, file_id)
1523
self.assertEqual('a@cset-0-3', revision_id)
1551
self.assertEqual(b'a@cset-0-3', revision_id)
1524
1552
self.assertEqual('inventory', repo_kind)
1525
self.assertEqual({'parents': ['a@cset-0-2a', 'a@cset-0-2b'],
1526
'sha1': '09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1527
'storage_kind': 'mpdiff',
1553
self.assertEqual({b'parents': [b'a@cset-0-2a', b'a@cset-0-2b'],
1554
b'sha1': b'09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1555
b'storage_kind': b'mpdiff',
1529
1557
# We should have an mpdiff that takes some lines from both parents.
1530
1558
self.assertEqualDiff(
1532
'<inventory format="10" revision_id="a@cset-0-3">\n'
1535
'c 1 3 3 2\n', bytes)
1560
b'<inventory format="10" revision_id="a@cset-0-3">\n'
1563
b'c 1 3 3 2\n', bytes)
1537
1565
def test_single_inv_no_parents_as_xml(self):
1538
1566
self.make_merged_branch()
1539
sio = self.make_bundle_just_inventories('null:', 'a@cset-0-1',
1567
sio = self.make_bundle_just_inventories(b'null:', b'a@cset-0-1',
1541
1569
reader = v4.BundleReader(sio, stream_input=False)
1542
1570
records = list(reader.iter_records())
1543
1571
self.assertEqual(1, len(records))
1544
1572
(bytes, metadata, repo_kind, revision_id,
1545
1573
file_id) = records[0]
1546
1574
self.assertIs(None, file_id)
1547
self.assertEqual('a@cset-0-1', revision_id)
1575
self.assertEqual(b'a@cset-0-1', revision_id)
1548
1576
self.assertEqual('inventory', repo_kind)
1549
self.assertEqual({'parents': [],
1550
'sha1': 'a13f42b142d544aac9b085c42595d304150e31a2',
1551
'storage_kind': 'mpdiff',
1577
self.assertEqual({b'parents': [],
1578
b'sha1': b'a13f42b142d544aac9b085c42595d304150e31a2',
1579
b'storage_kind': b'mpdiff',
1553
1581
# We should have an mpdiff that takes some lines from both parents.
1554
1582
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'
1584
b'<inventory format="10" revision_id="a@cset-0-1">\n'
1585
b'<directory file_id="root-id" name=""'
1586
b' revision="a@cset-0-1" />\n'
1587
b'<file file_id="file-id" name="file" parent_id="root-id"'
1588
b' revision="a@cset-0-1"'
1589
b' text_sha1="09c2f8647e14e49e922b955c194102070597c2d1"'
1590
b' text_size="17" />\n'
1566
1594
def test_multiple_inventories_as_xml(self):
1567
1595
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'])
1596
sio = self.make_bundle_just_inventories(b'a@cset-0-1', b'a@cset-0-3',
1597
[b'a@cset-0-2a', b'a@cset-0-2b', b'a@cset-0-3'])
1570
1598
reader = v4.BundleReader(sio, stream_input=False)
1571
1599
records = list(reader.iter_records())
1572
1600
self.assertEqual(3, len(records))
1573
1601
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'],
1602
self.assertEqual([b'a@cset-0-2a', b'a@cset-0-2b', b'a@cset-0-3'],
1576
1604
metadata_2a = records[0][1]
1577
self.assertEqual({'parents': ['a@cset-0-1'],
1578
'sha1': '1e105886d62d510763e22885eec733b66f5f09bf',
1579
'storage_kind': 'mpdiff',
1605
self.assertEqual({b'parents': [b'a@cset-0-1'],
1606
b'sha1': b'1e105886d62d510763e22885eec733b66f5f09bf',
1607
b'storage_kind': b'mpdiff',
1581
1609
metadata_2b = records[1][1]
1582
self.assertEqual({'parents': ['a@cset-0-1'],
1583
'sha1': 'f03f12574bdb5ed2204c28636c98a8547544ccd8',
1584
'storage_kind': 'mpdiff',
1610
self.assertEqual({b'parents': [b'a@cset-0-1'],
1611
b'sha1': b'f03f12574bdb5ed2204c28636c98a8547544ccd8',
1612
b'storage_kind': b'mpdiff',
1586
1614
metadata_3 = records[2][1]
1587
self.assertEqual({'parents': ['a@cset-0-2a', 'a@cset-0-2b'],
1588
'sha1': '09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1589
'storage_kind': 'mpdiff',
1615
self.assertEqual({b'parents': [b'a@cset-0-2a', b'a@cset-0-2b'],
1616
b'sha1': b'09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1617
b'storage_kind': b'mpdiff',
1591
1619
bytes_2a = records[0][0]
1592
1620
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)
1622
b'<inventory format="10" revision_id="a@cset-0-2a">\n'
1626
b'<file file_id="file-id" name="file" parent_id="root-id"'
1627
b' revision="a@cset-0-2a"'
1628
b' text_sha1="50f545ff40e57b6924b1f3174b267ffc4576e9a9"'
1629
b' text_size="12" />\n'
1631
b'c 0 3 3 1\n', bytes_2a)
1604
1632
bytes_2b = records[1][0]
1605
1633
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)
1635
b'<inventory format="10" revision_id="a@cset-0-2b">\n'
1639
b'<file file_id="file2-id" name="other-file" parent_id="root-id"'
1640
b' revision="a@cset-0-2b"'
1641
b' text_sha1="b46c0c8ea1e5ef8e46fc8894bfd4752a88ec939e"'
1642
b' text_size="14" />\n'
1644
b'c 0 3 4 1\n', bytes_2b)
1617
1645
bytes_3 = records[2][0]
1618
1646
self.assertEqualDiff(
1620
'<inventory format="10" revision_id="a@cset-0-3">\n'
1623
'c 1 3 3 2\n', bytes_3)
1648
b'<inventory format="10" revision_id="a@cset-0-3">\n'
1651
b'c 1 3 3 2\n', bytes_3)
1625
1653
def test_creating_bundle_preserves_chk_pages(self):
1626
1654
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)
1655
target = self.b1.controldir.sprout('target',
1656
revision_id=b'a@cset-0-2a').open_branch()
1657
bundle_txt, rev_ids = self.create_bundle_text(b'a@cset-0-2a',
1659
self.assertEqual(set([b'a@cset-0-2b', b'a@cset-0-3']), set(rev_ids))
1632
1660
bundle = read_bundle(bundle_txt)
1633
1661
target.lock_write()
1634
1662
self.addCleanup(target.unlock)
1635
1663
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')
1664
inv1 = next(self.b1.repository.inventories.get_record_stream([
1665
(b'a@cset-0-3',)], 'unordered',
1666
True)).get_bytes_as('fulltext')
1667
inv2 = next(target.repository.inventories.get_record_stream([
1668
(b'a@cset-0-3',)], 'unordered',
1669
True)).get_bytes_as('fulltext')
1642
1670
self.assertEqualDiff(inv1, inv2)
1740
1768
class TestBundleWriterReader(tests.TestCase):
1742
1770
def test_roundtrip_record(self):
1743
fileobj = StringIO()
1744
1772
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')
1774
writer.add_info_record({b'foo': b'bar'})
1775
writer._add_record(b"Record body", {b'parents': [b'1', b'3'],
1776
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1750
1778
fileobj.seek(0)
1751
1779
reader = v4.BundleReader(fileobj, stream_input=True)
1752
1780
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'),
1781
record = next(record_iter)
1782
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1783
'info', None, None), record)
1784
record = next(record_iter)
1785
self.assertEqual((b"Record body", {b'storage_kind': b'fulltext',
1786
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1761
1789
def test_roundtrip_record_memory_hungry(self):
1762
fileobj = StringIO()
1763
1791
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')
1793
writer.add_info_record({b'foo': b'bar'})
1794
writer._add_record(b"Record body", {b'parents': [b'1', b'3'],
1795
b'storage_kind': b'fulltext'}, 'file', b'revid', b'fileid')
1769
1797
fileobj.seek(0)
1770
1798
reader = v4.BundleReader(fileobj, stream_input=False)
1771
1799
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'),
1800
record = next(record_iter)
1801
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1802
'info', None, None), record)
1803
record = next(record_iter)
1804
self.assertEqual((b"Record body", {b'storage_kind': b'fulltext',
1805
b'parents': [b'1', b'3']}, 'file', b'revid', b'fileid'),
1780
1808
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))
1809
self.assertEqual(b'revision/rev1',
1810
v4.BundleWriter.encode_name('revision', b'rev1'))
1811
self.assertEqual(b'file/rev//1/file-id-1',
1812
v4.BundleWriter.encode_name('file', b'rev/1', b'file-id-1'))
1813
self.assertEqual(b'info',
1814
v4.BundleWriter.encode_name('info', None, None))
1788
1816
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'))
1817
self.assertEqual(('revision', b'rev1', None),
1818
v4.BundleReader.decode_name(b'revision/rev1'))
1819
self.assertEqual(('file', b'rev/1', b'file-id-1'),
1820
v4.BundleReader.decode_name(b'file/rev//1/file-id-1'))
1793
1821
self.assertEqual(('info', None, None),
1794
v4.BundleReader.decode_name('info'))
1822
v4.BundleReader.decode_name(b'info'))
1796
1824
def test_too_many_names(self):
1797
fileobj = StringIO()
1798
1826
writer = v4.BundleWriter(fileobj)
1800
writer.add_info_record(foo='bar')
1801
writer._container.add_bytes_record('blah', ['two', 'names'])
1828
writer.add_info_record({b'foo': b'bar'})
1829
writer._container.add_bytes_record(b'blah', [(b'two', ), (b'names', )])
1803
1831
fileobj.seek(0)
1804
1832
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)
1833
record = next(record_iter)
1834
self.assertEqual((None, {b'foo': b'bar', b'storage_kind': b'header'},
1835
'info', None, None), record)
1836
self.assertRaises(errors.BadBundle, next, record_iter)
1811
1839
class TestReadMergeableFromUrl(tests.TestCaseWithTransport):