3235
def _fetch_batch(self, revision_ids, basis_id, basis_tree):
3235
def _get_delta_for_revision(self, tree, parent_ids, basis_id, cache):
3236
"""Get the best delta and base for this revision.
3238
:return: (basis_id, delta)
3240
possible_trees = [(parent_id, cache[parent_id])
3241
for parent_id in parent_ids
3242
if parent_id in cache]
3243
if len(possible_trees) == 0:
3244
# There either aren't any parents, or the parents aren't in the
3245
# cache, so just use the last converted tree
3246
possible_trees.append((basis_id, cache[basis_id]))
3248
for basis_id, basis_tree in possible_trees:
3249
delta = tree.inventory._make_delta(basis_tree.inventory)
3250
deltas.append((len(delta), basis_id, delta))
3252
return deltas[0][1:]
3254
def _fetch_batch(self, revision_ids, basis_id, cache):
3236
3255
"""Fetch across a few revisions.
3238
3257
:param revision_ids: The revisions to copy
3239
:param basis_id: The revision_id of basis_tree
3240
:param basis_tree: A tree that is not in revision_ids which should
3241
already exist in the target.
3242
:return: (basis_id, basis_tree) A new basis to use now that these trees
3258
:param basis_id: The revision_id of a tree that must be in cache, used
3259
as a basis for delta when no other base is available
3260
:param cache: A cache of RevisionTrees that we can use.
3261
:return: The revision_id of the last converted tree. The RevisionTree
3262
for it will be in cache
3245
3264
# Walk though all revisions; get inventory deltas, copy referenced
3246
3265
# texts that delta references, insert the delta, revision and
3248
3267
text_keys = set()
3249
3268
pending_deltas = []
3250
3269
pending_revisions = []
3270
parent_map = self.source.get_parent_map(revision_ids)
3251
3271
for tree in self.source.revision_trees(revision_ids):
3252
3272
current_revision_id = tree.get_revision_id()
3253
delta = tree.inventory._make_delta(basis_tree.inventory)
3273
parent_ids = parent_map.get(current_revision_id, ())
3274
basis_id, delta = self._get_delta_for_revision(tree, parent_ids,
3276
# Find text entries that need to be copied
3254
3277
for old_path, new_path, file_id, entry in delta:
3255
3278
if new_path is not None:
3256
3279
if not (new_path or self.target.supports_rich_root()):
3257
# We leave the inventory delta in, because that
3258
# will have the deserialised inventory root
3280
# We don't copy the text for the root node unless the
3281
# target supports_rich_root.
3261
3283
# TODO: Do we need:
3262
3284
# "if entry.revision == current_revision_id" ?
3266
3288
pending_deltas.append((basis_id, delta,
3267
3289
current_revision_id, revision.parent_ids))
3268
3290
pending_revisions.append(revision)
3291
cache[current_revision_id] = tree
3269
3292
basis_id = current_revision_id
3271
3293
# Copy file texts
3272
3294
from_texts = self.source.texts
3273
3295
to_texts = self.target.texts
3287
3309
except errors.NoSuchRevision:
3289
3311
self.target.add_revision(revision.revision_id, revision)
3290
return basis_id, basis_tree
3292
3314
def _fetch_all_revisions(self, revision_ids, pb):
3293
3315
"""Fetch everything for the list of revisions.
3300
3322
basis_id, basis_tree = self._get_basis(revision_ids[0])
3301
3323
batch_size = 100
3324
cache = lru_cache.LRUCache(100)
3325
cache[basis_id] = basis_tree
3326
del basis_tree # We don't want to hang on to it here
3302
3327
for offset in range(0, len(revision_ids), batch_size):
3303
3328
self.target.start_write_group()
3305
3330
pb.update('Transferring revisions', offset,
3306
3331
len(revision_ids))
3307
3332
batch = revision_ids[offset:offset+batch_size]
3308
basis_id, basis_tree = self._fetch_batch(batch,
3309
basis_id, basis_tree)
3333
basis_id = self._fetch_batch(batch, basis_id, cache)
3311
3335
self.target.abort_write_group()