3735
def _get_delta_for_revision(self, tree, parent_ids, basis_id, cache):
3735
def _get_trees(self, revision_ids, cache):
3737
for rev_id in revision_ids:
3739
possible_trees.append((rev_id, cache[rev_id]))
3741
# Not cached, but inventory might be present anyway.
3743
tree = self.source.revision_tree(rev_id)
3744
except errors.NoSuchRevision:
3745
# Nope, parent is ghost.
3748
cache[rev_id] = tree
3749
possible_trees.append((rev_id, tree))
3750
return possible_trees
3752
def _get_delta_for_revision(self, tree, parent_ids, possible_trees):
3736
3753
"""Get the best delta and base for this revision.
3738
3755
:return: (basis_id, delta)
3740
possible_trees = [(parent_id, cache[parent_id])
3741
for parent_id in parent_ids
3742
if parent_id in cache]
3743
if len(possible_trees) == 0:
3744
# There either aren't any parents, or the parents aren't in the
3745
# cache, so just use the last converted tree
3746
possible_trees.append((basis_id, cache[basis_id]))
3758
# Generate deltas against each tree, to find the shortest.
3759
texts_possibly_new_in_tree = set()
3748
3760
for basis_id, basis_tree in possible_trees:
3749
3761
delta = tree.inventory._make_delta(basis_tree.inventory)
3762
for old_path, new_path, file_id, new_entry in delta:
3763
if new_path is None:
3764
# This file_id isn't present in the new rev, so we don't
3768
# Rich roots are handled elsewhere...
3770
kind = new_entry.kind
3771
if kind != 'directory' and kind != 'file':
3772
# No text record associated with this inventory entry.
3774
# This is a directory or file that has changed somehow.
3775
texts_possibly_new_in_tree.add((file_id, new_entry.revision))
3750
3776
deltas.append((len(delta), basis_id, delta))
3752
3778
return deltas[0][1:]
3780
def _fetch_parent_invs_for_stacking(self, parent_map, cache):
3781
"""Find all parent revisions that are absent, but for which the
3782
inventory is present, and copy those inventories.
3784
This is necessary to preserve correctness when the source is stacked
3785
without fallbacks configured. (Note that in cases like upgrade the
3786
source may be not have _fallback_repositories even though it is
3790
for parents in parent_map.values():
3791
parent_revs.update(parents)
3792
present_parents = self.source.get_parent_map(parent_revs)
3793
absent_parents = set(parent_revs).difference(present_parents)
3794
parent_invs_keys_for_stacking = self.source.inventories.get_parent_map(
3795
(rev_id,) for rev_id in absent_parents)
3796
parent_inv_ids = [key[-1] for key in parent_invs_keys_for_stacking]
3797
for parent_tree in self.source.revision_trees(parent_inv_ids):
3798
current_revision_id = parent_tree.get_revision_id()
3799
parents_parents_keys = parent_invs_keys_for_stacking[
3800
(current_revision_id,)]
3801
parents_parents = [key[-1] for key in parents_parents_keys]
3802
basis_id = _mod_revision.NULL_REVISION
3803
basis_tree = self.source.revision_tree(basis_id)
3804
delta = parent_tree.inventory._make_delta(basis_tree.inventory)
3805
self.target.add_inventory_by_delta(
3806
basis_id, delta, current_revision_id, parents_parents)
3807
cache[current_revision_id] = parent_tree
3754
3809
def _fetch_batch(self, revision_ids, basis_id, cache):
3755
3810
"""Fetch across a few revisions.
3769
3824
pending_deltas = []
3770
3825
pending_revisions = []
3771
3826
parent_map = self.source.get_parent_map(revision_ids)
3827
self._fetch_parent_invs_for_stacking(parent_map, cache)
3772
3828
for tree in self.source.revision_trees(revision_ids):
3829
# Find a inventory delta for this revision.
3830
# Find text entries that need to be copied, too.
3773
3831
current_revision_id = tree.get_revision_id()
3774
3832
parent_ids = parent_map.get(current_revision_id, ())
3833
parent_trees = self._get_trees(parent_ids, cache)
3834
possible_trees = list(parent_trees)
3835
if len(possible_trees) == 0:
3836
# There either aren't any parents, or the parents are ghosts,
3837
# so just use the last converted tree.
3838
possible_trees.append((basis_id, cache[basis_id]))
3775
3839
basis_id, delta = self._get_delta_for_revision(tree, parent_ids,
3777
3841
if self._converting_to_rich_root:
3778
3842
self._revision_id_to_root_id[current_revision_id] = \
3779
3843
tree.get_root_id()
3780
# Find text entries that need to be copied
3844
# Determine which texts are in present in this revision but not in
3845
# any of the available parents.
3846
texts_possibly_new_in_tree = set()
3781
3847
for old_path, new_path, file_id, entry in delta:
3782
if new_path is not None:
3785
if not self.target.supports_rich_root():
3786
# The target doesn't support rich root, so we don't
3789
if self._converting_to_rich_root:
3790
# This can't be copied normally, we have to insert
3792
root_keys_to_create.add((file_id, entry.revision))
3794
text_keys.add((file_id, entry.revision))
3848
if new_path is None:
3849
# This file_id isn't present in the new rev
3853
if not self.target.supports_rich_root():
3854
# The target doesn't support rich root, so we don't
3857
if self._converting_to_rich_root:
3858
# This can't be copied normally, we have to insert
3860
root_keys_to_create.add((file_id, entry.revision))
3863
texts_possibly_new_in_tree.add((file_id, entry.revision))
3864
for basis_id, basis_tree in possible_trees:
3865
basis_inv = basis_tree.inventory
3866
for file_key in list(texts_possibly_new_in_tree):
3867
file_id, file_revision = file_key
3869
entry = basis_inv[file_id]
3870
except errors.NoSuchId:
3872
if entry.revision == file_revision:
3873
texts_possibly_new_in_tree.remove(file_key)
3874
text_keys.update(texts_possibly_new_in_tree)
3795
3875
revision = self.source.get_revision(current_revision_id)
3796
3876
pending_deltas.append((basis_id, delta,
3797
3877
current_revision_id, revision.parent_ids))
3833
3913
for parent_tree in self.source.revision_trees(parent_map):
3834
3914
current_revision_id = parent_tree.get_revision_id()
3835
3915
parents_parents = parent_map[current_revision_id]
3916
possible_trees = self._get_trees(parents_parents, cache)
3917
if len(possible_trees) == 0:
3918
# There either aren't any parents, or the parents are
3919
# ghosts, so just use the last converted tree.
3920
possible_trees.append((basis_id, cache[basis_id]))
3836
3921
basis_id, delta = self._get_delta_for_revision(parent_tree,
3837
parents_parents, basis_id, cache)
3922
parents_parents, possible_trees)
3838
3923
self.target.add_inventory_by_delta(
3839
3924
basis_id, delta, current_revision_id, parents_parents)
3840
3925
# insert signatures and revisions