262
349
if unique_are_common_nodes:
263
350
ancestors = unique_searcher.find_seen_ancestors(
264
351
unique_are_common_nodes)
352
# TODO: This is a bit overboard, we only really care about
353
# the ancestors of the tips because the rest we
354
# already know. This is *correct* but causes us to
355
# search too much ancestry.
265
356
ancestors.update(common_searcher.find_seen_ancestors(ancestors))
266
357
unique_searcher.stop_searching_any(ancestors)
267
358
common_searcher.start_searching(ancestors)
269
unique_nodes = unique_searcher.seen.difference(common_searcher.seen)
360
return unique_searcher, common_searcher
362
def _make_unique_searchers(self, unique_nodes, unique_searcher,
364
"""Create a searcher for all the unique search tips (step 4).
366
As a side effect, the common_searcher will stop searching any nodes
367
that are ancestors of the unique searcher tips.
369
:return: (all_unique_searcher, unique_tip_searchers)
272
371
unique_tips = self._remove_simple_descendants(unique_nodes,
273
372
self.get_parent_map(unique_nodes))
275
374
if len(unique_tips) == 1:
276
unique_searchers = []
375
unique_tip_searchers = []
277
376
ancestor_all_unique = unique_searcher.find_seen_ancestors(unique_tips)
279
unique_searchers = []
378
unique_tip_searchers = []
280
379
for tip in unique_tips:
281
380
revs_to_search = unique_searcher.find_seen_ancestors([tip])
381
revs_to_search.update(
382
common_searcher.find_seen_ancestors(revs_to_search))
282
383
searcher = self._make_breadth_first_searcher(revs_to_search)
283
384
# We don't care about the starting nodes.
284
385
searcher._label = tip
286
unique_searchers.append(searcher)
387
unique_tip_searchers.append(searcher)
288
389
ancestor_all_unique = None
289
for searcher in unique_searchers:
390
for searcher in unique_tip_searchers:
290
391
if ancestor_all_unique is None:
291
392
ancestor_all_unique = set(searcher.seen)
293
394
ancestor_all_unique = ancestor_all_unique.intersection(
295
396
# Collapse all the common nodes into a single searcher
296
all_unique_searcher = self._make_breadth_first_searcher(ancestor_all_unique)
397
all_unique_searcher = self._make_breadth_first_searcher(
297
399
if ancestor_all_unique:
400
# We've seen these nodes in all the searchers, so we'll just go to
298
402
all_unique_searcher.step()
300
404
# Stop any search tips that are already known as ancestors of the
302
common_searcher.stop_searching_any(
406
stopped_common = common_searcher.stop_searching_any(
303
407
common_searcher.find_seen_ancestors(ancestor_all_unique))
305
409
total_stopped = 0
306
for searcher in unique_searchers:
410
for searcher in unique_tip_searchers:
307
411
total_stopped += len(searcher.stop_searching_any(
308
412
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
413
if 'graph' in debug.debug_flags:
414
trace.mutter('For %d unique nodes, created %d + 1 unique searchers'
415
' (%d stopped search tips, %d common ancestors'
416
' (%d stopped common)',
417
len(unique_nodes), len(unique_tip_searchers),
418
total_stopped, len(ancestor_all_unique),
420
return all_unique_searcher, unique_tip_searchers
422
def _step_unique_and_common_searchers(self, common_searcher,
423
unique_tip_searchers,
425
"""Step all the searchers"""
426
newly_seen_common = set(common_searcher.step())
427
newly_seen_unique = set()
428
for searcher in unique_tip_searchers:
429
next = set(searcher.step())
430
next.update(unique_searcher.find_seen_ancestors(next))
431
next.update(common_searcher.find_seen_ancestors(next))
432
for alt_searcher in unique_tip_searchers:
433
if alt_searcher is searcher:
435
next.update(alt_searcher.find_seen_ancestors(next))
436
searcher.start_searching(next)
437
newly_seen_unique.update(next)
438
return newly_seen_common, newly_seen_unique
440
def _find_nodes_common_to_all_unique(self, unique_tip_searchers,
442
newly_seen_unique, step_all_unique):
443
"""Find nodes that are common to all unique_tip_searchers.
445
If it is time, step the all_unique_searcher, and add its nodes to the
448
common_to_all_unique_nodes = newly_seen_unique.copy()
449
for searcher in unique_tip_searchers:
450
common_to_all_unique_nodes.intersection_update(searcher.seen)
451
common_to_all_unique_nodes.intersection_update(
452
all_unique_searcher.seen)
453
# Step all-unique less frequently than the other searchers.
454
# In the common case, we don't need to spider out far here, so
455
# avoid doing extra work.
457
tstart = time.clock()
458
nodes = all_unique_searcher.step()
459
common_to_all_unique_nodes.update(nodes)
460
if 'graph' in debug.debug_flags:
461
tdelta = time.clock() - tstart
462
trace.mutter('all_unique_searcher step() took %.3fs'
463
'for %d nodes (%d total), iteration: %s',
464
tdelta, len(nodes), len(all_unique_searcher.seen),
465
all_unique_searcher._iterations)
466
return common_to_all_unique_nodes
468
def _collapse_unique_searchers(self, unique_tip_searchers,
469
common_to_all_unique_nodes):
470
"""Combine searchers that are searching the same tips.
472
When two searchers are searching the same tips, we can stop one of the
473
searchers. We also know that the maximal set of common ancestors is the
474
intersection of the two original searchers.
476
:return: A list of searchers that are searching unique nodes.
478
# Filter out searchers that don't actually search different
479
# nodes. We already have the ancestry intersection for them
480
unique_search_tips = {}
481
for searcher in unique_tip_searchers:
482
stopped = searcher.stop_searching_any(common_to_all_unique_nodes)
483
will_search_set = frozenset(searcher._next_query)
484
if not will_search_set:
485
if 'graph' in debug.debug_flags:
486
trace.mutter('Unique searcher %s was stopped.'
487
' (%s iterations) %d nodes stopped',
489
searcher._iterations,
491
elif will_search_set not in unique_search_tips:
492
# This searcher is searching a unique set of nodes, let it
493
unique_search_tips[will_search_set] = [searcher]
495
unique_search_tips[will_search_set].append(searcher)
496
# TODO: it might be possible to collapse searchers faster when they
497
# only have *some* search tips in common.
498
next_unique_searchers = []
499
for searchers in unique_search_tips.itervalues():
500
if len(searchers) == 1:
501
# Searching unique tips, go for it
502
next_unique_searchers.append(searchers[0])
504
# These searchers have started searching the same tips, we
505
# don't need them to cover the same ground. The
506
# intersection of their ancestry won't change, so create a
507
# new searcher, combining their histories.
508
next_searcher = searchers[0]
509
for searcher in searchers[1:]:
510
next_searcher.seen.intersection_update(searcher.seen)
511
if 'graph' in debug.debug_flags:
512
trace.mutter('Combining %d searchers into a single'
513
' searcher searching %d nodes with'
516
len(next_searcher._next_query),
517
len(next_searcher.seen))
518
next_unique_searchers.append(next_searcher)
519
return next_unique_searchers
521
def _refine_unique_nodes(self, unique_searcher, all_unique_searcher,
522
unique_tip_searchers, common_searcher):
523
"""Steps 5-8 of find_unique_ancestors.
525
This function returns when common_searcher has stopped searching for
528
# We step the ancestor_all_unique searcher only every
529
# STEP_UNIQUE_SEARCHER_EVERY steps.
530
step_all_unique_counter = 0
315
531
# While we still have common nodes to search
316
532
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())
534
newly_seen_unique) = self._step_unique_and_common_searchers(
535
common_searcher, unique_tip_searchers, unique_searcher)
321
536
# 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())
537
common_to_all_unique_nodes = self._find_nodes_common_to_all_unique(
538
unique_tip_searchers, all_unique_searcher, newly_seen_unique,
539
step_all_unique_counter==0)
540
step_all_unique_counter = ((step_all_unique_counter + 1)
541
% STEP_UNIQUE_SEARCHER_EVERY)
329
543
if newly_seen_common:
330
544
# If a 'common' node is an ancestor of all unique searchers, we
331
545
# can stop searching it.
332
546
common_searcher.stop_searching_any(
333
547
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))
548
if common_to_all_unique_nodes:
549
common_to_all_unique_nodes.update(
550
common_searcher.find_seen_ancestors(
551
common_to_all_unique_nodes))
346
552
# The all_unique searcher can start searching the common nodes
347
553
# 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)
554
# This is the sort of thing where we would like to not have it
555
# start_searching all of the nodes, but only mark all of them
556
# as seen, and have it search only the actual tips. Otherwise
557
# it is another get_parent_map() traversal for it to figure out
558
# what we already should know.
559
all_unique_searcher.start_searching(common_to_all_unique_nodes)
560
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)
562
next_unique_searchers = self._collapse_unique_searchers(
563
unique_tip_searchers, common_to_all_unique_nodes)
564
if len(unique_tip_searchers) != len(next_unique_searchers):
565
if 'graph' in debug.debug_flags:
566
trace.mutter('Collapsed %d unique searchers => %d'
568
len(unique_tip_searchers),
569
len(next_unique_searchers),
570
all_unique_searcher._iterations)
571
unique_tip_searchers = next_unique_searchers
380
573
@symbol_versioning.deprecated_method(symbol_versioning.one_one)
381
574
def get_parents(self, revisions):