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