69
80
if tree.inventory[file_id].kind == "file":
70
81
tree.get_file(file_id).read()
72
# makes a target version repo
83
# makes a target version repo
73
84
repo_b = self.make_to_repository('b')
74
85
check_push_rev1(repo_b)
87
def test_fetch_inconsistent_last_changed_entries(self):
88
"""If an inventory has odd data we should still get what it references.
90
This test tests that we do fetch a file text created in a revision not
91
being fetched, but referenced from the revision we are fetching when the
92
adjacent revisions to the one being fetched do not reference that text.
94
tree = self.make_branch_and_tree('source')
95
revid = tree.commit('old')
96
to_repo = self.make_to_repository('to_repo')
97
to_repo.fetch(tree.branch.repository, revid)
98
# Make a broken revision and fetch it.
99
source = tree.branch.repository
101
self.addCleanup(source.unlock)
102
source.start_write_group()
104
# We need two revisions: OLD and NEW. NEW will claim to need a file
105
# 'FOO' changed in 'OLD'. OLD will not have that file at all.
106
source.texts.insert_record_stream([
107
versionedfile.FulltextContentFactory(('foo', revid), (), None,
109
basis = source.revision_tree(revid)
110
parent_id = basis.path2id('')
111
entry = inventory.make_entry('file', 'foo-path', parent_id, 'foo')
112
entry.revision = revid
113
entry.text_size = len('contents')
114
entry.text_sha1 = osutils.sha_string('contents')
115
inv_sha1, _ = source.add_inventory_by_delta(revid, [
116
(None, 'foo-path', 'foo', entry)], 'new', [revid])
117
rev = Revision(timestamp=0,
119
committer="Foo Bar <foo@example.com>",
121
inventory_sha1=inv_sha1,
124
source.add_revision(rev.revision_id, rev)
126
source.abort_write_group()
129
source.commit_write_group()
130
to_repo.fetch(source, 'new')
132
self.addCleanup(to_repo.unlock)
133
self.assertEqual('contents',
134
to_repo.texts.get_record_stream([('foo', revid)],
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()
184
def test_fetch_parent_inventories_at_stacking_boundary(self):
185
"""Fetch to a stacked branch copies inventories for parents of
186
revisions at the stacking boundary.
188
This is necessary so that the server is able to determine the file-ids
189
altered by all revisions it contains, which means that it needs both
190
the inventory for any revision it has, and the inventories of all that
193
However, we should also skip any revisions which are ghosts in the
196
if not self.repository_format_to.supports_external_lookups:
197
raise TestNotApplicable("Need stacking support in the target.")
198
builder = self.make_branch_builder('branch')
199
builder.start_series()
200
builder.build_snapshot('base', None, [
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'))])
209
builder.finish_series()
210
branch = builder.get_branch()
211
repo = self.make_to_repository('trunk')
212
trunk = repo.bzrdir.create_branch()
213
trunk.repository.fetch(branch.repository, 'left')
214
trunk.repository.fetch(branch.repository, 'right')
215
repo = self.make_to_repository('stacked')
216
stacked_branch = repo.bzrdir.create_branch()
217
stacked_branch.set_stacked_on_url(trunk.base)
218
stacked_branch.repository.fetch(branch.repository, 'merge')
219
unstacked_repo = stacked_branch.bzrdir.open_repository()
220
unstacked_repo.lock_read()
221
self.addCleanup(unstacked_repo.unlock)
222
self.assertFalse(unstacked_repo.has_revision('left'))
223
self.assertFalse(unstacked_repo.has_revision('right'))
225
set([('left',), ('right',), ('merge',)]),
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')
76
399
def test_fetch_missing_basis_text(self):
77
400
"""If fetching a delta, we should die if a basis is not present."""
78
401
tree = self.make_branch_and_tree('tree')