262
296
if unique_are_common_nodes:
263
297
ancestors = unique_searcher.find_seen_ancestors(
264
298
unique_are_common_nodes)
299
# TODO: This is a bit overboard, we only really care about
300
# the ancestors of the tips because the rest we
301
# already know. This is *correct* but causes us to
302
# search too much ancestry.
265
303
ancestors.update(common_searcher.find_seen_ancestors(ancestors))
266
304
unique_searcher.stop_searching_any(ancestors)
267
305
common_searcher.start_searching(ancestors)
269
unique_nodes = unique_searcher.seen.difference(common_searcher.seen)
307
return unique_searcher, common_searcher
309
def _make_unique_searchers(self, unique_nodes, unique_searcher,
311
"""Create a searcher for all the unique search tips (step 4).
313
As a side effect, the common_searcher will stop searching any nodes
314
that are ancestors of the unique searcher tips.
316
:return: (all_unique_searcher, unique_tip_searchers)
272
318
unique_tips = self._remove_simple_descendants(unique_nodes,
273
319
self.get_parent_map(unique_nodes))
275
321
if len(unique_tips) == 1:
276
unique_searchers = []
322
unique_tip_searchers = []
277
323
ancestor_all_unique = unique_searcher.find_seen_ancestors(unique_tips)
279
unique_searchers = []
325
unique_tip_searchers = []
280
326
for tip in unique_tips:
281
327
revs_to_search = unique_searcher.find_seen_ancestors([tip])
328
revs_to_search.update(
329
common_searcher.find_seen_ancestors(revs_to_search))
282
330
searcher = self._make_breadth_first_searcher(revs_to_search)
283
331
# We don't care about the starting nodes.
284
332
searcher._label = tip
286
unique_searchers.append(searcher)
334
unique_tip_searchers.append(searcher)
288
336
ancestor_all_unique = None
289
for searcher in unique_searchers:
337
for searcher in unique_tip_searchers:
290
338
if ancestor_all_unique is None:
291
339
ancestor_all_unique = set(searcher.seen)
293
341
ancestor_all_unique = ancestor_all_unique.intersection(
295
343
# Collapse all the common nodes into a single searcher
296
all_unique_searcher = self._make_breadth_first_searcher(ancestor_all_unique)
344
all_unique_searcher = self._make_breadth_first_searcher(
297
346
if ancestor_all_unique:
347
# We've seen these nodes in all the searchers, so we'll just go to
298
349
all_unique_searcher.step()
300
351
# Stop any search tips that are already known as ancestors of the
302
common_searcher.stop_searching_any(
353
stopped_common = common_searcher.stop_searching_any(
303
354
common_searcher.find_seen_ancestors(ancestor_all_unique))
305
356
total_stopped = 0
306
for searcher in unique_searchers:
357
for searcher in unique_tip_searchers:
307
358
total_stopped += len(searcher.stop_searching_any(
308
359
searcher.find_seen_ancestors(ancestor_all_unique)))
309
_mutter('For %s unique nodes, created %s + 1 unique searchers'
310
' (%s stopped search tips, %s common ancestors)',
311
len(unique_nodes), len(unique_searchers), total_stopped,
312
len(ancestor_all_unique))
313
del ancestor_all_unique
360
if 'graph' in debug.debug_flags:
361
trace.mutter('For %d unique nodes, created %d + 1 unique searchers'
362
' (%d stopped search tips, %d common ancestors'
363
' (%d stopped common)',
364
len(unique_nodes), len(unique_tip_searchers),
365
total_stopped, len(ancestor_all_unique),
367
return all_unique_searcher, unique_tip_searchers
369
def _step_unique_and_common_searchers(self, common_searcher,
370
unique_tip_searchers,
372
"""Step all the searchers"""
373
newly_seen_common = set(common_searcher.step())
374
newly_seen_unique = set()
375
for searcher in unique_tip_searchers:
376
next = set(searcher.step())
377
next.update(unique_searcher.find_seen_ancestors(next))
378
next.update(common_searcher.find_seen_ancestors(next))
379
for alt_searcher in unique_tip_searchers:
380
if alt_searcher is searcher:
382
next.update(alt_searcher.find_seen_ancestors(next))
383
searcher.start_searching(next)
384
newly_seen_unique.update(next)
385
return newly_seen_common, newly_seen_unique
387
def _find_nodes_common_to_all_unique(self, unique_tip_searchers,
389
newly_seen_unique, step_all_unique):
390
"""Find nodes that are common to all unique_tip_searchers.
392
If it is time, step the all_unique_searcher, and add its nodes to the
395
common_to_all_unique_nodes = newly_seen_unique.copy()
396
for searcher in unique_tip_searchers:
397
common_to_all_unique_nodes.intersection_update(searcher.seen)
398
common_to_all_unique_nodes.intersection_update(
399
all_unique_searcher.seen)
400
# Step all-unique less frequently than the other searchers.
401
# In the common case, we don't need to spider out far here, so
402
# avoid doing extra work.
404
tstart = time.clock()
405
nodes = all_unique_searcher.step()
406
common_to_all_unique_nodes.update(nodes)
407
if 'graph' in debug.debug_flags:
408
tdelta = time.clock() - tstart
409
trace.mutter('all_unique_searcher step() took %.3fs'
410
'for %d nodes (%d total), iteration: %s',
411
tdelta, len(nodes), len(all_unique_searcher.seen),
412
all_unique_searcher._iterations)
413
return common_to_all_unique_nodes
415
def _collapse_unique_searchers(self, unique_tip_searchers,
416
common_to_all_unique_nodes):
417
"""Combine searchers that are searching the same tips.
419
When two searchers are searching the same tips, we can stop one of the
420
searchers. We also know that the maximal set of common ancestors is the
421
intersection of the two original searchers.
423
:return: A list of searchers that are searching unique nodes.
425
# Filter out searchers that don't actually search different
426
# nodes. We already have the ancestry intersection for them
427
unique_search_tips = {}
428
for searcher in unique_tip_searchers:
429
stopped = searcher.stop_searching_any(common_to_all_unique_nodes)
430
will_search_set = frozenset(searcher._next_query)
431
if not will_search_set:
432
if 'graph' in debug.debug_flags:
433
trace.mutter('Unique searcher %s was stopped.'
434
' (%s iterations) %d nodes stopped',
436
searcher._iterations,
438
elif will_search_set not in unique_search_tips:
439
# This searcher is searching a unique set of nodes, let it
440
unique_search_tips[will_search_set] = [searcher]
442
unique_search_tips[will_search_set].append(searcher)
443
# TODO: it might be possible to collapse searchers faster when they
444
# only have *some* search tips in common.
445
next_unique_searchers = []
446
for searchers in unique_search_tips.itervalues():
447
if len(searchers) == 1:
448
# Searching unique tips, go for it
449
next_unique_searchers.append(searchers[0])
451
# These searchers have started searching the same tips, we
452
# don't need them to cover the same ground. The
453
# intersection of their ancestry won't change, so create a
454
# new searcher, combining their histories.
455
next_searcher = searchers[0]
456
for searcher in searchers[1:]:
457
next_searcher.seen.intersection_update(searcher.seen)
458
if 'graph' in debug.debug_flags:
459
trace.mutter('Combining %d searchers into a single'
460
' searcher searching %d nodes with'
463
len(next_searcher._next_query),
464
len(next_searcher.seen))
465
next_unique_searchers.append(next_searcher)
466
return next_unique_searchers
468
def _refine_unique_nodes(self, unique_searcher, all_unique_searcher,
469
unique_tip_searchers, common_searcher):
470
"""Steps 5-8 of find_unique_ancestors.
472
This function returns when common_searcher has stopped searching for
475
# We step the ancestor_all_unique searcher only every
476
# STEP_UNIQUE_SEARCHER_EVERY steps.
477
step_all_unique_counter = 0
315
478
# While we still have common nodes to search
316
479
while common_searcher._next_query:
317
newly_seen_common = set(common_searcher.step())
318
newly_seen_unique = set()
319
for searcher in unique_searchers:
320
newly_seen_unique.update(searcher.step())
481
newly_seen_unique) = self._step_unique_and_common_searchers(
482
common_searcher, unique_tip_searchers, unique_searcher)
321
483
# These nodes are common ancestors of all unique nodes
322
unique_are_common_nodes = newly_seen_unique.copy()
323
for searcher in unique_searchers:
324
unique_are_common_nodes = unique_are_common_nodes.intersection(
326
unique_are_common_nodes = unique_are_common_nodes.intersection(
327
all_unique_searcher.seen)
328
unique_are_common_nodes.update(all_unique_searcher.step())
484
common_to_all_unique_nodes = self._find_nodes_common_to_all_unique(
485
unique_tip_searchers, all_unique_searcher, newly_seen_unique,
486
step_all_unique_counter==0)
487
step_all_unique_counter = ((step_all_unique_counter + 1)
488
% STEP_UNIQUE_SEARCHER_EVERY)
329
490
if newly_seen_common:
330
491
# If a 'common' node is an ancestor of all unique searchers, we
331
492
# can stop searching it.
332
493
common_searcher.stop_searching_any(
333
494
all_unique_searcher.seen.intersection(newly_seen_common))
334
if unique_are_common_nodes:
335
# We have new common-to-all-unique-searchers nodes
336
for searcher in unique_searchers:
337
unique_are_common_nodes.update(
338
searcher.find_seen_ancestors(unique_are_common_nodes))
339
unique_are_common_nodes.update(
340
all_unique_searcher.find_seen_ancestors(unique_are_common_nodes))
341
# Since these are common, we can grab another set of ancestors
343
unique_are_common_nodes.update(
344
common_searcher.find_seen_ancestors(unique_are_common_nodes))
495
if common_to_all_unique_nodes:
496
common_to_all_unique_nodes.update(
497
common_searcher.find_seen_ancestors(
498
common_to_all_unique_nodes))
346
499
# The all_unique searcher can start searching the common nodes
347
500
# but everyone else can stop.
348
all_unique_searcher.start_searching(unique_are_common_nodes)
349
common_searcher.stop_searching_any(unique_are_common_nodes)
501
# This is the sort of thing where we would like to not have it
502
# start_searching all of the nodes, but only mark all of them
503
# as seen, and have it search only the actual tips. Otherwise
504
# it is another get_parent_map() traversal for it to figure out
505
# what we already should know.
506
all_unique_searcher.start_searching(common_to_all_unique_nodes)
507
common_searcher.stop_searching_any(common_to_all_unique_nodes)
351
# Filter out searchers that don't actually search different
352
# nodes. We already have the ancestry intersection for them
353
next_unique_searchers = []
354
unique_search_sets = set()
355
for searcher in unique_searchers:
356
stopped = searcher.stop_searching_any(unique_are_common_nodes)
357
will_search_set = frozenset(searcher._next_query)
358
if not will_search_set:
359
_mutter('Unique searcher %s was stopped.'
360
' (%s iterations) %d nodes stopped',
362
searcher._iterations,
364
elif will_search_set not in unique_search_sets:
365
# This searcher is searching a unique set of nodes, let it
366
unique_search_sets.add(will_search_set)
367
next_unique_searchers.append(searcher)
369
_mutter('Unique searcher %s stopped for repeated'
370
' search of %s nodes',
371
searcher._label, len(will_search_set))
372
if len(unique_searchers) != len(next_unique_searchers):
373
_mutter('Collapsed %s unique searchers => %s'
375
len(unique_searchers), len(next_unique_searchers),
376
all_unique_searcher._iterations)
377
unique_searchers = next_unique_searchers
378
return unique_nodes.difference(common_searcher.seen)
509
next_unique_searchers = self._collapse_unique_searchers(
510
unique_tip_searchers, common_to_all_unique_nodes)
511
if len(unique_tip_searchers) != len(next_unique_searchers):
512
if 'graph' in debug.debug_flags:
513
trace.mutter('Collapsed %d unique searchers => %d'
515
len(unique_tip_searchers),
516
len(next_unique_searchers),
517
all_unique_searcher._iterations)
518
unique_tip_searchers = next_unique_searchers
380
520
@symbol_versioning.deprecated_method(symbol_versioning.one_one)
381
521
def get_parents(self, revisions):