167
169
if self._last_revision is NULL_REVISION:
168
170
# explicit limit of no revisions needed
170
if (self._last_revision is not None and
171
self.to_repository.has_revision(self._last_revision)):
174
return self.to_repository.search_missing_revision_ids(
175
self.from_repository, self._last_revision,
176
find_ghosts=self.find_ghosts)
177
except errors.NoSuchRevision, e:
178
raise InstallFailed([self._last_revision])
172
return self.to_repository.search_missing_revision_ids(
173
self.from_repository, self._last_revision,
174
find_ghosts=self.find_ghosts)
176
def _parent_inventories(self, revision_ids):
177
# Find all the parent revisions referenced by the stream, but
178
# not present in the stream, and make sure we send their
180
parent_maps = self.to_repository.get_parent_map(revision_ids)
182
map(parents.update, parent_maps.itervalues())
183
parents.discard(NULL_REVISION)
184
parents.difference_update(revision_ids)
185
missing_keys = set(('inventories', rev_id) for rev_id in parents)
181
189
class Inter1and2Helper(object):
248
253
# yet, and are unlikely to in non-rich-root environments anyway.
249
254
root_id_order.sort(key=operator.itemgetter(0))
250
255
# Create a record stream containing the roots to create.
252
for key in root_id_order:
253
root_id, rev_id = key
254
rev_parents = parent_map[rev_id]
255
# We drop revision parents with different file-ids, because
256
# that represents a rename of the root to a different location
257
# - its not actually a parent for us. (We could look for that
258
# file id in the revision tree at considerably more expense,
259
# but for now this is sufficient (and reconcile will catch and
260
# correct this anyway).
261
# When a parent revision is a ghost, we guess that its root id
262
# was unchanged (rather than trimming it from the parent list).
263
parent_keys = tuple((root_id, parent) for parent in rev_parents
264
if parent != NULL_REVISION and
265
rev_id_to_root_id.get(parent, root_id) == root_id)
266
yield FulltextContentFactory(key, parent_keys, None, '')
267
return [('texts', yield_roots())]
257
# XXX: not covered by tests, should have a flag to always run
258
# this. -- mbp 20100129
259
graph = _get_rich_root_heads_graph(self.source, revs)
260
new_roots_stream = _new_root_data_stream(
261
root_id_order, rev_id_to_root_id, parent_map, self.source, graph)
262
return [('texts', new_roots_stream)]
265
def _get_rich_root_heads_graph(source_repo, revision_ids):
266
"""Get a Graph object suitable for asking heads() for new rich roots."""
267
st = static_tuple.StaticTuple
268
revision_keys = [st(r_id).intern() for r_id in revision_ids]
269
known_graph = source_repo.revisions.get_known_graph_ancestry(
271
return _mod_graph.GraphThunkIdsToKeys(known_graph)
274
def _new_root_data_stream(
275
root_keys_to_create, rev_id_to_root_id_map, parent_map, repo, graph=None):
276
"""Generate a texts substream of synthesised root entries.
278
Used in fetches that do rich-root upgrades.
280
:param root_keys_to_create: iterable of (root_id, rev_id) pairs describing
281
the root entries to create.
282
:param rev_id_to_root_id_map: dict of known rev_id -> root_id mappings for
283
calculating the parents. If a parent rev_id is not found here then it
284
will be recalculated.
285
:param parent_map: a parent map for all the revisions in
287
:param graph: a graph to use instead of repo.get_graph().
289
for root_key in root_keys_to_create:
290
root_id, rev_id = root_key
291
parent_keys = _parent_keys_for_root_version(
292
root_id, rev_id, rev_id_to_root_id_map, parent_map, repo, graph)
293
yield versionedfile.FulltextContentFactory(
294
root_key, parent_keys, None, '')
297
def _parent_keys_for_root_version(
298
root_id, rev_id, rev_id_to_root_id_map, parent_map, repo, graph=None):
299
"""Get the parent keys for a given root id.
301
A helper function for _new_root_data_stream.
303
# Include direct parents of the revision, but only if they used the same
304
# root_id and are heads.
305
rev_parents = parent_map[rev_id]
307
for parent_id in rev_parents:
308
if parent_id == NULL_REVISION:
310
if parent_id not in rev_id_to_root_id_map:
311
# We probably didn't read this revision, go spend the extra effort
314
tree = repo.revision_tree(parent_id)
315
except errors.NoSuchRevision:
316
# Ghost, fill out rev_id_to_root_id in case we encounter this
318
# But set parent_root_id to None since we don't really know
319
parent_root_id = None
321
parent_root_id = tree.get_root_id()
322
rev_id_to_root_id_map[parent_id] = None
324
# rev_id_to_root_id_map[parent_id] = parent_root_id
325
# memory consumption maybe?
327
parent_root_id = rev_id_to_root_id_map[parent_id]
328
if root_id == parent_root_id:
329
# With stacking we _might_ want to refer to a non-local revision,
330
# but this code path only applies when we have the full content
331
# available, so ghosts really are ghosts, not just the edge of
333
parent_ids.append(parent_id)
335
# root_id may be in the parent anyway.
337
tree = repo.revision_tree(parent_id)
338
except errors.NoSuchRevision:
339
# ghost, can't refer to it.
343
parent_ids.append(tree.inventory[root_id].revision)
344
except errors.NoSuchId:
347
# Drop non-head parents
349
graph = repo.get_graph()
350
heads = graph.heads(parent_ids)
352
for parent_id in parent_ids:
353
if parent_id in heads and parent_id not in selected_ids:
354
selected_ids.append(parent_id)
355
parent_keys = [(root_id, parent_id) for parent_id in selected_ids]