68
68
descendants are common ancestors. (This is not quite the standard
69
69
graph theory definition)
71
candidate_mca = self._find_candidate_mca(revisions)
72
return self._filter_candidate_mca(candidate_mca)
74
def _find_candidate_mca(self, revisions):
71
75
walkers = [_AncestryWalker(r, self) for r in revisions]
72
76
active_walkers = walkers[:]
73
77
maybe_minimal_common = set()
74
78
seen_ancestors = set()
76
80
if len(active_walkers) == 0:
77
maybe_minimal_common.difference_update(seen_ancestors)
78
81
return maybe_minimal_common
80
83
new_active_walkers = []
95
98
for walker in walkers:
96
99
w_seen_ancestors = walker.find_seen_ancestors(revision)
97
100
walker.stop_tracing_any(w_seen_ancestors)
98
w_seen_ancestors.discard(revision)
99
seen_ancestors.update(w_seen_ancestors)
102
def _filter_candidate_mca(self, candidate_mca):
103
"""Remove candidates which are ancestors of other candidates"""
104
walkers = dict((c, _AncestryWalker(c, self)) for c in candidate_mca)
105
active_walkers = dict(walkers)
106
# skip over the actual candidate for each walker
107
for walker in active_walkers.itervalues():
109
while len(active_walkers) > 0:
110
for candidate, walker in list(active_walkers.iteritems()):
112
ancestors = walker.next()
113
except StopIteration:
114
del active_walkers[candidate]
116
for ancestor in ancestors:
117
if ancestor in candidate_mca:
118
candidate_mca.remove(ancestor)
119
del walkers[ancestor]
120
if ancestor in active_walkers:
121
del active_walkers[ancestor]
122
for walker in walkers.itervalues():
123
if ancestor not in walker.seen:
126
# if this revision was seen by all walkers, then it
127
# is a descendant of all candidates, so we can stop
128
# tracing it, and any seen ancestors
129
for walker in walkers.itervalues():
131
walker.find_seen_ancestors(ancestor)
132
walker.stop_tracing_any(seen_ancestors)
101
135
def unique_common(self, left_revision, right_revision):
102
136
"""Find a unique minimal common ancestor.