509
509
self._iterations = 0
510
510
self._next_query = set(revisions)
511
511
self.seen = set()
512
self._started_keys = set(self._next_query)
513
self._stopped_keys = set()
512
514
self._parents_provider = parents_provider
513
515
self._returning = 'next_with_ghosts'
521
523
return ('_BreadthFirstSearcher(iterations=%d, %s,'
522
524
' seen=%r)' % (self._iterations, search, list(self.seen)))
526
def get_recipe(self):
527
"""Return a recipe that can be used to replay this search.
529
:return: A tuple (start_keys_set, exclude_keys_set). To recreate the
530
results of this search, create a breadth first searcher on the same
531
graph starting at start_keys. Then call next() (or
532
next_with_ghosts()) repeatedly, and on every result, call
533
stop_searching_any on any keys from the exclude_keys set.
535
if self._returning == 'next':
536
# We have to know the current nodes children to be able to list the
537
# exclude keys for them. However, while we could have a second
538
# look-ahead result buffer and shuffle things around, this method
539
# is typically only called once per search - when memoising the
540
# results of the search.
541
found, ghosts, next, parents = self._do_query(self._next_query)
542
# pretend we didn't query: perhaps we should tweak _do_query to be
543
# entirely stateless?
544
self.seen.difference_update(next)
547
next_query = self._next_query
548
return self._started_keys, self._stopped_keys.union(next_query)
525
551
"""Return the next ancestors of this revision.
538
564
# switch to returning the query, not the results.
539
565
self._returning = 'next'
540
566
self._iterations += 1
541
self.seen.update(self._next_query)
544
569
if len(self._next_query) == 0:
545
570
raise StopIteration()
571
# We have seen what we're querying at this point as we are returning
572
# the query, not the results.
573
self.seen.update(self._next_query)
546
574
return self._next_query
548
576
def next_with_ghosts(self):
587
615
found_parents = set()
588
616
parents_of_found = set()
617
# revisions may contain nodes that point to other nodes in revisions:
618
# we want to filter them out.
619
self.seen.update(revisions)
589
620
parent_map = self._parents_provider.get_parent_map(revisions)
590
621
for rev_id, parents in parent_map.iteritems():
591
622
found_parents.add(rev_id)
592
623
parents_of_found.update(p for p in parents if p not in self.seen)
593
624
ghost_parents = revisions - found_parents
594
self.seen.update(parents_of_found)
595
625
return found_parents, ghost_parents, parents_of_found, parent_map
597
627
def __iter__(self):