124
134
to_repo.texts.get_record_stream([('foo', revid)],
125
135
'unordered', True).next().get_bytes_as('fulltext'))
137
def test_fetch_from_stacked_smart(self):
138
self.setup_smart_server_with_call_log()
139
self.test_fetch_from_stacked()
141
def test_fetch_from_stacked_smart_old(self):
142
self.setup_smart_server_with_call_log()
143
self.disable_verb('Repository.get_stream_1.19')
144
self.test_fetch_from_stacked()
146
def test_fetch_from_stacked(self):
147
"""Fetch from a stacked branch succeeds."""
148
if not self.repository_format.supports_external_lookups:
149
raise TestNotApplicable("Need stacking support in the source.")
150
builder = self.make_branch_builder('full-branch')
151
builder.start_series()
152
builder.build_snapshot('first', None, [
153
('add', ('', 'root-id', 'directory', '')),
154
('add', ('file', 'file-id', 'file', 'content\n'))])
155
builder.build_snapshot('second', ['first'], [
156
('modify', ('file-id', 'second content\n'))])
157
builder.build_snapshot('third', ['second'], [
158
('modify', ('file-id', 'third content\n'))])
159
builder.finish_series()
160
branch = builder.get_branch()
161
repo = self.make_repository('stacking-base')
162
trunk = repo.bzrdir.create_branch()
163
trunk.repository.fetch(branch.repository, 'second')
164
repo = self.make_repository('stacked')
165
stacked_branch = repo.bzrdir.create_branch()
166
stacked_branch.set_stacked_on_url(trunk.base)
167
stacked_branch.repository.fetch(branch.repository, 'third')
168
target = self.make_to_repository('target')
169
target.fetch(stacked_branch.repository, 'third')
171
self.addCleanup(target.unlock)
172
all_revs = set(['first', 'second', 'third'])
173
self.assertEqual(all_revs, set(target.get_parent_map(all_revs)))
175
def test_fetch_parent_inventories_at_stacking_boundary_smart(self):
176
self.setup_smart_server_with_call_log()
177
self.test_fetch_parent_inventories_at_stacking_boundary()
179
def test_fetch_parent_inventories_at_stacking_boundary_smart_old(self):
180
self.setup_smart_server_with_call_log()
181
self.disable_verb('Repository.insert_stream_1.19')
182
self.test_fetch_parent_inventories_at_stacking_boundary()
127
184
def test_fetch_parent_inventories_at_stacking_boundary(self):
128
185
"""Fetch to a stacked branch copies inventories for parents of
129
186
revisions at the stacking boundary.
132
189
altered by all revisions it contains, which means that it needs both
133
190
the inventory for any revision it has, and the inventories of all that
134
191
revision's parents.
193
However, we should also skip any revisions which are ghosts in the
136
to_repo = self.make_to_repository('to')
137
if not to_repo._format.supports_external_lookups:
196
if not self.repository_format_to.supports_external_lookups:
138
197
raise TestNotApplicable("Need stacking support in the target.")
139
198
builder = self.make_branch_builder('branch')
140
199
builder.start_series()
141
200
builder.build_snapshot('base', None, [
142
('add', ('', 'root-id', 'directory', ''))])
143
builder.build_snapshot('left', ['base'], [])
144
builder.build_snapshot('right', ['base'], [])
145
builder.build_snapshot('merge', ['left', 'right'], [])
201
('add', ('', 'root-id', 'directory', '')),
202
('add', ('file', 'file-id', 'file', 'content\n'))])
203
builder.build_snapshot('left', ['base'], [
204
('modify', ('file-id', 'left content\n'))])
205
builder.build_snapshot('right', ['base'], [
206
('modify', ('file-id', 'right content\n'))])
207
builder.build_snapshot('merge', ['left', 'right'], [
208
('modify', ('file-id', 'left and right content\n'))])
146
209
builder.finish_series()
147
210
branch = builder.get_branch()
148
211
repo = self.make_to_repository('trunk')
161
224
self.assertEqual(
162
225
set([('left',), ('right',), ('merge',)]),
163
226
unstacked_repo.inventories.keys())
227
# And the basis inventories have been copied correctly
229
self.addCleanup(trunk.unlock)
230
left_tree, right_tree = trunk.repository.revision_trees(
232
stacked_branch.lock_read()
233
self.addCleanup(stacked_branch.unlock)
235
stacked_right_tree) = stacked_branch.repository.revision_trees(
237
self.assertEqual(left_tree.inventory, stacked_left_tree.inventory)
238
self.assertEqual(right_tree.inventory, stacked_right_tree.inventory)
240
# Finally, it's not enough to see that the basis inventories are
241
# present. The texts introduced in merge (and only those) should be
242
# present, and also generating a stream should succeed without blowing
244
self.assertTrue(unstacked_repo.has_revision('merge'))
245
expected_texts = set([('file-id', 'merge')])
246
if stacked_branch.repository.texts.get_parent_map([('root-id',
248
# If a (root-id,merge) text exists, it should be in the stacked
250
expected_texts.add(('root-id', 'merge'))
251
self.assertEqual(expected_texts, unstacked_repo.texts.keys())
252
self.assertCanStreamRevision(unstacked_repo, 'merge')
254
def assertCanStreamRevision(self, repo, revision_id):
255
exclude_keys = set(repo.all_revision_ids()) - set([revision_id])
256
search = SearchResult([revision_id], exclude_keys, 1, [revision_id])
257
source = repo._get_source(repo._format)
258
for substream_kind, substream in source.get_stream(search):
259
# Consume the substream
262
def test_fetch_across_stacking_boundary_ignores_ghost(self):
263
if not self.repository_format_to.supports_external_lookups:
264
raise TestNotApplicable("Need stacking support in the target.")
265
to_repo = self.make_to_repository('to')
266
builder = self.make_branch_builder('branch')
267
builder.start_series()
268
builder.build_snapshot('base', None, [
269
('add', ('', 'root-id', 'directory', '')),
270
('add', ('file', 'file-id', 'file', 'content\n'))])
271
builder.build_snapshot('second', ['base'], [
272
('modify', ('file-id', 'second content\n'))])
273
builder.build_snapshot('third', ['second', 'ghost'], [
274
('modify', ('file-id', 'third content\n'))])
275
builder.finish_series()
276
branch = builder.get_branch()
277
repo = self.make_to_repository('trunk')
278
trunk = repo.bzrdir.create_branch()
279
trunk.repository.fetch(branch.repository, 'second')
280
repo = self.make_to_repository('stacked')
281
stacked_branch = repo.bzrdir.create_branch()
282
stacked_branch.set_stacked_on_url(trunk.base)
283
stacked_branch.repository.fetch(branch.repository, 'third')
284
unstacked_repo = stacked_branch.bzrdir.open_repository()
285
unstacked_repo.lock_read()
286
self.addCleanup(unstacked_repo.unlock)
287
self.assertFalse(unstacked_repo.has_revision('second'))
288
self.assertFalse(unstacked_repo.has_revision('ghost'))
290
set([('second',), ('third',)]),
291
unstacked_repo.inventories.keys())
292
# And the basis inventories have been copied correctly
294
self.addCleanup(trunk.unlock)
295
second_tree = trunk.repository.revision_tree('second')
296
stacked_branch.lock_read()
297
self.addCleanup(stacked_branch.unlock)
298
stacked_second_tree = stacked_branch.repository.revision_tree('second')
299
self.assertEqual(second_tree.inventory, stacked_second_tree.inventory)
300
# Finally, it's not enough to see that the basis inventories are
301
# present. The texts introduced in merge (and only those) should be
302
# present, and also generating a stream should succeed without blowing
304
self.assertTrue(unstacked_repo.has_revision('third'))
305
expected_texts = set([('file-id', 'third')])
306
if stacked_branch.repository.texts.get_parent_map([('root-id',
308
# If a (root-id,third) text exists, it should be in the stacked
310
expected_texts.add(('root-id', 'third'))
311
self.assertEqual(expected_texts, unstacked_repo.texts.keys())
312
self.assertCanStreamRevision(unstacked_repo, 'third')
314
def test_fetch_from_stacked_to_stacked_copies_parent_inventories(self):
315
"""Fetch from a stacked branch copies inventories for parents of
316
revisions at the stacking boundary.
318
Specifically, fetch will copy the parent inventories from the
319
source for which the corresponding revisions are not present. This
320
will happen even when the source repository has no fallbacks configured
321
(as is the case during upgrade).
323
if not self.repository_format.supports_external_lookups:
324
raise TestNotApplicable("Need stacking support in the source.")
325
if not self.repository_format_to.supports_external_lookups:
326
raise TestNotApplicable("Need stacking support in the target.")
327
builder = self.make_branch_builder('branch')
328
builder.start_series()
329
builder.build_snapshot('base', None, [
330
('add', ('', 'root-id', 'directory', '')),
331
('add', ('file', 'file-id', 'file', 'content\n'))])
332
builder.build_snapshot('left', ['base'], [
333
('modify', ('file-id', 'left content\n'))])
334
builder.build_snapshot('right', ['base'], [
335
('modify', ('file-id', 'right content\n'))])
336
builder.build_snapshot('merge', ['left', 'right'], [
337
('modify', ('file-id', 'left and right content\n'))])
338
builder.finish_series()
339
branch = builder.get_branch()
340
repo = self.make_repository('old-trunk')
341
# Make a pair of equivalent trunk repos in the from and to formats.
342
old_trunk = repo.bzrdir.create_branch()
343
old_trunk.repository.fetch(branch.repository, 'left')
344
old_trunk.repository.fetch(branch.repository, 'right')
345
repo = self.make_to_repository('new-trunk')
346
new_trunk = repo.bzrdir.create_branch()
347
new_trunk.repository.fetch(branch.repository, 'left')
348
new_trunk.repository.fetch(branch.repository, 'right')
349
# Make the source; a repo stacked on old_trunk contained just the data
351
repo = self.make_repository('old-stacked')
352
old_stacked_branch = repo.bzrdir.create_branch()
353
old_stacked_branch.set_stacked_on_url(old_trunk.base)
354
old_stacked_branch.repository.fetch(branch.repository, 'merge')
355
# Make the target, a repo stacked on new_trunk.
356
repo = self.make_to_repository('new-stacked')
357
new_stacked_branch = repo.bzrdir.create_branch()
358
new_stacked_branch.set_stacked_on_url(new_trunk.base)
359
old_unstacked_repo = old_stacked_branch.bzrdir.open_repository()
360
new_unstacked_repo = new_stacked_branch.bzrdir.open_repository()
361
# Reopen the source and target repos without any fallbacks, and fetch
363
new_unstacked_repo.fetch(old_unstacked_repo, 'merge')
364
# Now check the results. new_unstacked_repo should contain all the
365
# data necessary to stream 'merge' (i.e. the parent inventories).
366
new_unstacked_repo.lock_read()
367
self.addCleanup(new_unstacked_repo.unlock)
368
self.assertFalse(new_unstacked_repo.has_revision('left'))
369
self.assertFalse(new_unstacked_repo.has_revision('right'))
371
set([('left',), ('right',), ('merge',)]),
372
new_unstacked_repo.inventories.keys())
373
# And the basis inventories have been copied correctly
374
new_trunk.lock_read()
375
self.addCleanup(new_trunk.unlock)
376
left_tree, right_tree = new_trunk.repository.revision_trees(
378
new_stacked_branch.lock_read()
379
self.addCleanup(new_stacked_branch.unlock)
381
stacked_right_tree) = new_stacked_branch.repository.revision_trees(
383
self.assertEqual(left_tree.inventory, stacked_left_tree.inventory)
384
self.assertEqual(right_tree.inventory, stacked_right_tree.inventory)
385
# Finally, it's not enough to see that the basis inventories are
386
# present. The texts introduced in merge (and only those) should be
387
# present, and also generating a stream should succeed without blowing
389
self.assertTrue(new_unstacked_repo.has_revision('merge'))
390
expected_texts = set([('file-id', 'merge')])
391
if new_stacked_branch.repository.texts.get_parent_map([('root-id',
393
# If a (root-id,merge) text exists, it should be in the stacked
395
expected_texts.add(('root-id', 'merge'))
396
self.assertEqual(expected_texts, new_unstacked_repo.texts.keys())
397
self.assertCanStreamRevision(new_unstacked_repo, 'merge')
165
399
def test_fetch_missing_basis_text(self):
166
400
"""If fetching a delta, we should die if a basis is not present."""