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