1424
1424
def suspend_write_group(self):
1425
1425
raise errors.UnsuspendableWriteGroup(self)
1427
def get_missing_parent_inventories(self):
1428
"""Return the keys of missing inventory parents for revisions added in
1431
A revision is not complete if the inventory delta for that revision
1432
cannot be calculated. Therefore if the parent inventories of a
1433
revision are not present, the revision is incomplete, and e.g. cannot
1434
be streamed by a smart server. This method finds missing inventory
1435
parents for revisions added in this write group.
1437
if not self._format.supports_external_lookups:
1438
# This is only an issue for stacked repositories
1440
if not self.is_in_write_group():
1441
raise AssertionError('not in a write group')
1443
# XXX: We assume that every added revision already has its
1444
# corresponding inventory, so we only check for parent inventories that
1445
# might be missing, rather than all inventories.
1446
parents = set(self.revisions._index.get_missing_parents())
1447
parents.discard(_mod_revision.NULL_REVISION)
1448
unstacked_inventories = self.inventories._index
1449
present_inventories = unstacked_inventories.get_parent_map(
1450
key[-1:] for key in parents)
1451
if len(parents.difference(present_inventories)) == 0:
1452
# No missing parent inventories.
1454
# Ok, now we have a list of missing inventories. But these only matter
1455
# if the inventories that reference them are missing some texts they
1456
# appear to introduce.
1457
# XXX: Texts referenced by all added inventories need to be present,
1458
# but at the moment we're only checking for texts referenced by
1459
# inventories at the graph's edge.
1460
key_deps = self.revisions._index._key_dependencies
1461
key_deps.add_keys(present_inventories)
1462
referrers = frozenset(r[0] for r in key_deps.get_referrers())
1463
file_ids = self.fileids_altered_by_revision_ids(referrers)
1464
missing_texts = set()
1465
for file_id, version_ids in file_ids.iteritems():
1466
missing_texts.update(
1467
(file_id, version_id) for version_id in version_ids)
1468
present_texts = self.texts.get_parent_map(missing_texts)
1469
missing_texts.difference_update(present_texts)
1470
if not missing_texts:
1471
# No texts are missing, so all revisions and their deltas are
1474
# Alternatively the text versions could be returned as the missing
1475
# keys, but this is likely to be less data.
1476
missing_keys = set(('inventories', rev_id) for (rev_id,) in parents)
1427
1479
def refresh_data(self):
1428
1480
"""Re-read any data needed to to synchronise with disk.
3068
3120
target_graph = self.target.get_graph()
3069
3121
revision_ids = frozenset(revision_ids)
3070
# Fast path for the case where all the revisions are already in the
3072
# (Although this does incur an extra round trip for the
3073
# fairly common case where the target doesn't already have the revision
3075
if set(target_graph.get_parent_map(revision_ids)) == revision_ids:
3076
return graph.SearchResult(revision_ids, set(), 0, set())
3077
3122
missing_revs = set()
3078
3123
source_graph = self.source.get_graph()
3079
3124
# ensure we don't pay silly lookup costs.
3435
3480
# nothing to do:
3439
revision_ids = self.search_missing_revision_ids(revision_id,
3440
find_ghosts=find_ghosts).get_keys()
3441
except errors.NoSuchRevision:
3442
raise errors.InstallFailed([revision_id])
3483
revision_ids = self.search_missing_revision_ids(revision_id,
3484
find_ghosts=find_ghosts).get_keys()
3443
3485
if len(revision_ids) == 0:
3445
3487
return self._pack(self.source, self.target, revision_ids)
3542
3584
"""Get the parent keys for a given root id."""
3543
3585
root_id, rev_id = root_key
3544
3586
# Include direct parents of the revision, but only if they used
3587
# the same root_id and are heads.
3546
3588
parent_keys = []
3547
3589
for parent_id in parent_map[rev_id]:
3548
3590
if parent_id == _mod_revision.NULL_REVISION:
3562
3604
self._revision_id_to_root_id[parent_id] = None
3564
3606
parent_root_id = self._revision_id_to_root_id[parent_id]
3565
if root_id == parent_root_id or parent_root_id is None:
3566
parent_keys.append((root_id, parent_id))
3567
return tuple(parent_keys)
3607
if root_id == parent_root_id:
3608
# With stacking we _might_ want to refer to a non-local
3609
# revision, but this code path only applies when we have the
3610
# full content available, so ghosts really are ghosts, not just
3611
# the edge of local data.
3612
parent_keys.append((parent_id,))
3614
# root_id may be in the parent anyway.
3616
tree = self.source.revision_tree(parent_id)
3617
except errors.NoSuchRevision:
3618
# ghost, can't refer to it.
3622
parent_keys.append((tree.inventory[root_id].revision,))
3623
except errors.NoSuchId:
3626
g = graph.Graph(self.source.revisions)
3627
heads = g.heads(parent_keys)
3629
for key in parent_keys:
3630
if key in heads and key not in selected_keys:
3631
selected_keys.append(key)
3632
return tuple([(root_id,)+ key for key in selected_keys])
3569
3634
def _new_root_data_stream(self, root_keys_to_create, parent_map):
3570
3635
for root_key in root_keys_to_create:
3629
3694
to_texts.insert_record_stream(from_texts.get_record_stream(
3630
3695
text_keys, self.target._format._fetch_order,
3631
3696
not self.target._format._fetch_uses_deltas))
3697
# insert inventory deltas
3633
3698
for delta in pending_deltas:
3634
3699
self.target.add_inventory_by_delta(*delta)
3700
if self.target._fallback_repositories:
3701
# Make sure this stacked repository has all the parent inventories
3702
# for the new revisions that we are about to insert. We do this
3703
# before adding the revisions so that no revision is added until
3704
# all the inventories it may depend on are added.
3706
revision_ids = set()
3707
for revision in pending_revisions:
3708
revision_ids.add(revision.revision_id)
3709
parent_ids.update(revision.parent_ids)
3710
parent_ids.difference_update(revision_ids)
3711
parent_ids.discard(_mod_revision.NULL_REVISION)
3712
parent_map = self.source.get_parent_map(parent_ids)
3713
for parent_tree in self.source.revision_trees(parent_ids):
3714
basis_id, delta = self._get_delta_for_revision(tree, parent_ids, basis_id, cache)
3715
current_revision_id = parent_tree.get_revision_id()
3716
parents_parents = parent_map[current_revision_id]
3717
self.target.add_inventory_by_delta(
3718
basis_id, delta, current_revision_id, parents_parents)
3635
3719
# insert signatures and revisions
3636
3720
for revision in pending_revisions:
3936
4020
def _locked_insert_stream(self, stream, src_format):
3937
4021
to_serializer = self.target_repo._format._serializer
3938
4022
src_serializer = src_format._serializer
3939
4024
if to_serializer == src_serializer:
3940
4025
# If serializers match and the target is a pack repository, set the
3941
4026
# write cache size on the new pack. This avoids poor performance
3982
4067
self.target_repo.signatures.insert_record_stream(substream)
3984
4069
raise AssertionError('kaboom! %s' % (substream_type,))
4070
# Done inserting data, and the missing_keys calculations will try to
4071
# read back from the inserted data, so flush the writes to the new pack
4072
# (if this is pack format).
4073
if new_pack is not None:
4074
new_pack._write_data('', flush=True)
4075
# Find all the new revisions (including ones from resume_tokens)
4076
missing_keys = self.target_repo.get_missing_parent_inventories()
3986
missing_keys = set()
3987
4078
for prefix, versioned_file in (
3988
4079
('texts', self.target_repo.texts),
3989
4080
('inventories', self.target_repo.inventories),