2464
2465
return wrong_parents, unused_keys
2467
class InterDifferingSerializer(InterRepository):
2468
class InterVersionedFileRepository(InterRepository):
2470
_walk_to_common_revisions_batch_size = 50
2473
def fetch(self, revision_id=None, find_ghosts=False,
2475
"""Fetch the content required to construct revision_id.
2477
The content is copied from self.source to self.target.
2479
:param revision_id: if None all content is copied, if NULL_REVISION no
2483
ui.ui_factory.warn_experimental_format_fetch(self)
2484
from bzrlib.fetch import RepoFetcher
2485
# See <https://launchpad.net/bugs/456077> asking for a warning here
2486
if self.source._format.network_name() != self.target._format.network_name():
2487
ui.ui_factory.show_user_warning('cross_format_fetch',
2488
from_format=self.source._format,
2489
to_format=self.target._format)
2490
f = RepoFetcher(to_repository=self.target,
2491
from_repository=self.source,
2492
last_revision=revision_id,
2493
fetch_spec=fetch_spec,
2494
find_ghosts=find_ghosts)
2496
def _walk_to_common_revisions(self, revision_ids, if_present_ids=None):
2497
"""Walk out from revision_ids in source to revisions target has.
2499
:param revision_ids: The start point for the search.
2500
:return: A set of revision ids.
2502
target_graph = self.target.get_graph()
2503
revision_ids = frozenset(revision_ids)
2505
all_wanted_revs = revision_ids.union(if_present_ids)
2507
all_wanted_revs = revision_ids
2508
missing_revs = set()
2509
source_graph = self.source.get_graph()
2510
# ensure we don't pay silly lookup costs.
2511
searcher = source_graph._make_breadth_first_searcher(all_wanted_revs)
2512
null_set = frozenset([_mod_revision.NULL_REVISION])
2513
searcher_exhausted = False
2517
# Iterate the searcher until we have enough next_revs
2518
while len(next_revs) < self._walk_to_common_revisions_batch_size:
2520
next_revs_part, ghosts_part = searcher.next_with_ghosts()
2521
next_revs.update(next_revs_part)
2522
ghosts.update(ghosts_part)
2523
except StopIteration:
2524
searcher_exhausted = True
2526
# If there are ghosts in the source graph, and the caller asked for
2527
# them, make sure that they are present in the target.
2528
# We don't care about other ghosts as we can't fetch them and
2529
# haven't been asked to.
2530
ghosts_to_check = set(revision_ids.intersection(ghosts))
2531
revs_to_get = set(next_revs).union(ghosts_to_check)
2533
have_revs = set(target_graph.get_parent_map(revs_to_get))
2534
# we always have NULL_REVISION present.
2535
have_revs = have_revs.union(null_set)
2536
# Check if the target is missing any ghosts we need.
2537
ghosts_to_check.difference_update(have_revs)
2539
# One of the caller's revision_ids is a ghost in both the
2540
# source and the target.
2541
raise errors.NoSuchRevision(
2542
self.source, ghosts_to_check.pop())
2543
missing_revs.update(next_revs - have_revs)
2544
# Because we may have walked past the original stop point, make
2545
# sure everything is stopped
2546
stop_revs = searcher.find_seen_ancestors(have_revs)
2547
searcher.stop_searching_any(stop_revs)
2548
if searcher_exhausted:
2550
return searcher.get_result()
2553
def search_missing_revision_ids(self,
2554
revision_id=symbol_versioning.DEPRECATED_PARAMETER,
2555
find_ghosts=True, revision_ids=None, if_present_ids=None):
2556
"""Return the revision ids that source has that target does not.
2558
:param revision_id: only return revision ids included by this
2560
:param revision_ids: return revision ids included by these
2561
revision_ids. NoSuchRevision will be raised if any of these
2562
revisions are not present.
2563
:param if_present_ids: like revision_ids, but will not cause
2564
NoSuchRevision if any of these are absent, instead they will simply
2565
not be in the result. This is useful for e.g. finding revisions
2566
to fetch for tags, which may reference absent revisions.
2567
:param find_ghosts: If True find missing revisions in deep history
2568
rather than just finding the surface difference.
2569
:return: A bzrlib.graph.SearchResult.
2571
if symbol_versioning.deprecated_passed(revision_id):
2572
symbol_versioning.warn(
2573
'search_missing_revision_ids(revision_id=...) was '
2574
'deprecated in 2.4. Use revision_ids=[...] instead.',
2575
DeprecationWarning, stacklevel=2)
2576
if revision_ids is not None:
2577
raise AssertionError(
2578
'revision_ids is mutually exclusive with revision_id')
2579
if revision_id is not None:
2580
revision_ids = [revision_id]
2582
# stop searching at found target revisions.
2583
if not find_ghosts and (revision_ids is not None or if_present_ids is
2585
return self._walk_to_common_revisions(revision_ids,
2586
if_present_ids=if_present_ids)
2587
# generic, possibly worst case, slow code path.
2588
target_ids = set(self.target.all_revision_ids())
2589
source_ids = self._present_source_revisions_for(
2590
revision_ids, if_present_ids)
2591
result_set = set(source_ids).difference(target_ids)
2592
return self.source.revision_ids_to_search_result(result_set)
2594
def _present_source_revisions_for(self, revision_ids, if_present_ids=None):
2595
"""Returns set of all revisions in ancestry of revision_ids present in
2598
:param revision_ids: if None, all revisions in source are returned.
2599
:param if_present_ids: like revision_ids, but if any/all of these are
2600
absent no error is raised.
2602
if revision_ids is not None or if_present_ids is not None:
2603
# First, ensure all specified revisions exist. Callers expect
2604
# NoSuchRevision when they pass absent revision_ids here.
2605
if revision_ids is None:
2606
revision_ids = set()
2607
if if_present_ids is None:
2608
if_present_ids = set()
2609
revision_ids = set(revision_ids)
2610
if_present_ids = set(if_present_ids)
2611
all_wanted_ids = revision_ids.union(if_present_ids)
2612
graph = self.source.get_graph()
2613
present_revs = set(graph.get_parent_map(all_wanted_ids))
2614
missing = revision_ids.difference(present_revs)
2616
raise errors.NoSuchRevision(self.source, missing.pop())
2617
found_ids = all_wanted_ids.intersection(present_revs)
2618
source_ids = [rev_id for (rev_id, parents) in
2619
graph.iter_ancestry(found_ids)
2620
if rev_id != _mod_revision.NULL_REVISION
2621
and parents is not None]
2623
source_ids = self.source.all_revision_ids()
2624
return set(source_ids)
2627
def _get_repo_format_to_test(self):
2631
def is_compatible(cls, source, target):
2632
# The default implementation is compatible with everything
2633
return (source._format.supports_full_versioned_files and
2634
target._format.supports_full_versioned_files)
2637
class InterDifferingSerializer(InterVersionedFileRepository):
2470
2640
def _get_repo_format_to_test(self):