766
766
common_walker.start_searching(new_common)
767
767
return candidate_heads
769
def find_merge_order(self, tip_revision_id, lca_revision_ids):
770
"""Find the order that each revision was merged into tip.
772
This basically just walks backwards with a stack, and walks left-first
773
until it finds a node to stop.
775
if len(lca_revision_ids) == 1:
776
return list(lca_revision_ids)
777
looking_for = set(lca_revision_ids)
778
# TODO: Is there a way we could do this "faster" by batching up the
779
# get_parent_map requests?
780
# TODO: Should we also be culling the ancestry search right away? We
781
# could add looking_for to the "stop" list, and walk their
782
# ancestry in batched mode. The flip side is it might mean we walk a
783
# lot of "stop" nodes, rather than only the minimum.
784
# Then again, without it we may trace back into ancestry we could have
786
stack = [tip_revision_id]
789
while stack and looking_for:
792
if next in looking_for:
794
looking_for.remove(next)
795
if len(looking_for) == 1:
796
found.append(looking_for.pop())
799
parent_ids = self.get_parent_map([next]).get(next, None)
800
if not parent_ids: # Ghost, nothing to search here
802
for parent_id in reversed(parent_ids):
803
# TODO: (performance) We see the parent at this point, but we
804
# wait to mark it until later to make sure we get left
805
# parents before right parents. However, instead of
806
# waiting until we have traversed enough parents, we
807
# could instead note that we've found it, and once all
808
# parents are in the stack, just reverse iterate the
810
if parent_id not in stop:
811
# this will need to be searched
812
stack.append(parent_id)
769
816
def find_unique_lca(self, left_revision, right_revision,
770
817
count_steps=False):
771
818
"""Find a unique LCA.
1228
1275
parent_map = self._parents_provider.get_parent_map(revisions)
1229
1276
found_revisions.update(parent_map)
1230
1277
for rev_id, parents in parent_map.iteritems():
1231
1280
new_found_parents = [p for p in parents if p not in self.seen]
1232
1281
if new_found_parents:
1233
1282
# Calling set.update() with an empty generator is actually
1398
1447
return self._keys
1450
def collapse_linear_regions(parent_map):
1451
"""Collapse regions of the graph that are 'linear'.
1457
can be collapsed by removing B and getting::
1461
:param parent_map: A dictionary mapping children to their parents
1462
:return: Another dictionary with 'linear' chains collapsed
1464
# Note: this isn't a strictly minimal collapse. For example:
1472
# Will not have 'D' removed, even though 'E' could fit. Also:
1478
# A and C are both kept because they are edges of the graph. We *could* get
1479
# rid of A if we wanted.
1487
# Will not have any nodes removed, even though you do have an
1488
# 'uninteresting' linear D->B and E->C
1490
for child, parents in parent_map.iteritems():
1491
children.setdefault(child, [])
1493
children.setdefault(p, []).append(child)
1495
orig_children = dict(children)
1497
result = dict(parent_map)
1498
for node in parent_map:
1499
parents = result[node]
1500
if len(parents) == 1:
1501
parent_children = children[parents[0]]
1502
if len(parent_children) != 1:
1503
# This is not the only child
1505
node_children = children[node]
1506
if len(node_children) != 1:
1508
child_parents = result.get(node_children[0], None)
1509
if len(child_parents) != 1:
1510
# This is not its only parent
1512
# The child of this node only points at it, and the parent only has
1513
# this as a child. remove this node, and join the others together
1514
result[node_children[0]] = parents
1515
children[parents[0]] = node_children