132
151
altered by all revisions it contains, which means that it needs both
133
152
the inventory for any revision it has, and the inventories of all that
134
153
revision's parents.
155
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:
158
if not self.repository_format_to.supports_external_lookups:
138
159
raise TestNotApplicable("Need stacking support in the target.")
139
160
builder = self.make_branch_builder('branch')
140
161
builder.start_series()
141
162
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'], [])
163
('add', ('', 'root-id', 'directory', '')),
164
('add', ('file', 'file-id', 'file', 'content\n'))])
165
builder.build_snapshot('left', ['base'], [
166
('modify', ('file-id', 'left content\n'))])
167
builder.build_snapshot('right', ['base'], [
168
('modify', ('file-id', 'right content\n'))])
169
builder.build_snapshot('merge', ['left', 'right'], [
170
('modify', ('file-id', 'left and right content\n'))])
146
171
builder.finish_series()
147
172
branch = builder.get_branch()
148
173
repo = self.make_to_repository('trunk')
161
186
self.assertEqual(
162
187
set([('left',), ('right',), ('merge',)]),
163
188
unstacked_repo.inventories.keys())
189
# And the basis inventories have been copied correctly
191
self.addCleanup(trunk.unlock)
192
left_tree, right_tree = trunk.repository.revision_trees(
194
stacked_branch.lock_read()
195
self.addCleanup(stacked_branch.unlock)
197
stacked_right_tree) = stacked_branch.repository.revision_trees(
199
self.assertEqual(left_tree.inventory, stacked_left_tree.inventory)
200
self.assertEqual(right_tree.inventory, stacked_right_tree.inventory)
202
# Finally, it's not enough to see that the basis inventories are
203
# present. The texts introduced in merge (and only those) should be
204
# present, and also generating a stream should succeed without blowing
206
self.assertTrue(unstacked_repo.has_revision('merge'))
207
expected_texts = set([('file-id', 'merge')])
208
if stacked_branch.repository.texts.get_parent_map([('root-id',
210
# If a (root-id,merge) text exists, it should be in the stacked
212
expected_texts.add(('root-id', 'merge'))
213
self.assertEqual(expected_texts, unstacked_repo.texts.keys())
214
self.assertCanStreamRevision(unstacked_repo, 'merge')
216
def assertCanStreamRevision(self, repo, revision_id):
217
exclude_keys = set(repo.all_revision_ids()) - set([revision_id])
218
search = SearchResult([revision_id], exclude_keys, 1, [revision_id])
219
source = repo._get_source(repo._format)
220
for substream_kind, substream in source.get_stream(search):
221
# Consume the substream
224
def test_fetch_across_stacking_boundary_ignores_ghost(self):
225
if not self.repository_format_to.supports_external_lookups:
226
raise TestNotApplicable("Need stacking support in the target.")
227
to_repo = self.make_to_repository('to')
228
builder = self.make_branch_builder('branch')
229
builder.start_series()
230
builder.build_snapshot('base', None, [
231
('add', ('', 'root-id', 'directory', '')),
232
('add', ('file', 'file-id', 'file', 'content\n'))])
233
builder.build_snapshot('second', ['base'], [
234
('modify', ('file-id', 'second content\n'))])
235
builder.build_snapshot('third', ['second', 'ghost'], [
236
('modify', ('file-id', 'third content\n'))])
237
builder.finish_series()
238
branch = builder.get_branch()
239
repo = self.make_to_repository('trunk')
240
trunk = repo.bzrdir.create_branch()
241
trunk.repository.fetch(branch.repository, 'second')
242
repo = self.make_to_repository('stacked')
243
stacked_branch = repo.bzrdir.create_branch()
244
stacked_branch.set_stacked_on_url(trunk.base)
245
stacked_branch.repository.fetch(branch.repository, 'third')
246
unstacked_repo = stacked_branch.bzrdir.open_repository()
247
unstacked_repo.lock_read()
248
self.addCleanup(unstacked_repo.unlock)
249
self.assertFalse(unstacked_repo.has_revision('second'))
250
self.assertFalse(unstacked_repo.has_revision('ghost'))
252
set([('second',), ('third',)]),
253
unstacked_repo.inventories.keys())
254
# And the basis inventories have been copied correctly
256
self.addCleanup(trunk.unlock)
257
second_tree = trunk.repository.revision_tree('second')
258
stacked_branch.lock_read()
259
self.addCleanup(stacked_branch.unlock)
260
stacked_second_tree = stacked_branch.repository.revision_tree('second')
261
self.assertEqual(second_tree.inventory, stacked_second_tree.inventory)
262
# Finally, it's not enough to see that the basis inventories are
263
# present. The texts introduced in merge (and only those) should be
264
# present, and also generating a stream should succeed without blowing
266
self.assertTrue(unstacked_repo.has_revision('third'))
267
expected_texts = set([('file-id', 'third')])
268
if stacked_branch.repository.texts.get_parent_map([('root-id',
270
# If a (root-id,third) text exists, it should be in the stacked
272
expected_texts.add(('root-id', 'third'))
273
self.assertEqual(expected_texts, unstacked_repo.texts.keys())
274
self.assertCanStreamRevision(unstacked_repo, 'third')
276
def test_fetch_from_stacked_to_stacked_copies_parent_inventories(self):
277
"""Fetch from a stacked branch copies inventories for parents of
278
revisions at the stacking boundary.
280
Specifically, fetch will copy the parent inventories from the
281
source for which the corresponding revisions are not present. This
282
will happen even when the source repository has no fallbacks configured
283
(as is the case during upgrade).
285
if not self.repository_format.supports_external_lookups:
286
raise TestNotApplicable("Need stacking support in the source.")
287
if not self.repository_format_to.supports_external_lookups:
288
raise TestNotApplicable("Need stacking support in the target.")
289
builder = self.make_branch_builder('branch')
290
builder.start_series()
291
builder.build_snapshot('base', None, [
292
('add', ('', 'root-id', 'directory', '')),
293
('add', ('file', 'file-id', 'file', 'content\n'))])
294
builder.build_snapshot('left', ['base'], [
295
('modify', ('file-id', 'left content\n'))])
296
builder.build_snapshot('right', ['base'], [
297
('modify', ('file-id', 'right content\n'))])
298
builder.build_snapshot('merge', ['left', 'right'], [
299
('modify', ('file-id', 'left and right content\n'))])
300
builder.finish_series()
301
branch = builder.get_branch()
302
repo = self.make_repository('old-trunk')
303
# Make a pair of equivalent trunk repos in the from and to formats.
304
old_trunk = repo.bzrdir.create_branch()
305
old_trunk.repository.fetch(branch.repository, 'left')
306
old_trunk.repository.fetch(branch.repository, 'right')
307
repo = self.make_to_repository('new-trunk')
308
new_trunk = repo.bzrdir.create_branch()
309
new_trunk.repository.fetch(branch.repository, 'left')
310
new_trunk.repository.fetch(branch.repository, 'right')
311
# Make the source; a repo stacked on old_trunk contained just the data
313
repo = self.make_repository('old-stacked')
314
old_stacked_branch = repo.bzrdir.create_branch()
315
old_stacked_branch.set_stacked_on_url(old_trunk.base)
316
old_stacked_branch.repository.fetch(branch.repository, 'merge')
317
# Make the target, a repo stacked on new_trunk.
318
repo = self.make_to_repository('new-stacked')
319
new_stacked_branch = repo.bzrdir.create_branch()
320
new_stacked_branch.set_stacked_on_url(new_trunk.base)
321
old_unstacked_repo = old_stacked_branch.bzrdir.open_repository()
322
new_unstacked_repo = new_stacked_branch.bzrdir.open_repository()
323
# Reopen the source and target repos without any fallbacks, and fetch
325
new_unstacked_repo.fetch(old_unstacked_repo, 'merge')
326
# Now check the results. new_unstacked_repo should contain all the
327
# data necessary to stream 'merge' (i.e. the parent inventories).
328
new_unstacked_repo.lock_read()
329
self.addCleanup(new_unstacked_repo.unlock)
330
self.assertFalse(new_unstacked_repo.has_revision('left'))
331
self.assertFalse(new_unstacked_repo.has_revision('right'))
333
set([('left',), ('right',), ('merge',)]),
334
new_unstacked_repo.inventories.keys())
335
# And the basis inventories have been copied correctly
336
new_trunk.lock_read()
337
self.addCleanup(new_trunk.unlock)
338
left_tree, right_tree = new_trunk.repository.revision_trees(
340
new_stacked_branch.lock_read()
341
self.addCleanup(new_stacked_branch.unlock)
343
stacked_right_tree) = new_stacked_branch.repository.revision_trees(
345
self.assertEqual(left_tree.inventory, stacked_left_tree.inventory)
346
self.assertEqual(right_tree.inventory, stacked_right_tree.inventory)
347
# Finally, it's not enough to see that the basis inventories are
348
# present. The texts introduced in merge (and only those) should be
349
# present, and also generating a stream should succeed without blowing
351
self.assertTrue(new_unstacked_repo.has_revision('merge'))
352
expected_texts = set([('file-id', 'merge')])
353
if new_stacked_branch.repository.texts.get_parent_map([('root-id',
355
# If a (root-id,merge) text exists, it should be in the stacked
357
expected_texts.add(('root-id', 'merge'))
358
self.assertEqual(expected_texts, new_unstacked_repo.texts.keys())
359
self.assertCanStreamRevision(new_unstacked_repo, 'merge')
165
361
def test_fetch_missing_basis_text(self):
166
362
"""If fetching a delta, we should die if a basis is not present."""