86 lines
2.3 KiB
Python
86 lines
2.3 KiB
Python
|
# ancestor.py - generic DAG ancestor algorithm for mercurial
|
||
|
#
|
||
|
# Copyright 2006 Matt Mackall <mpm@selenic.com>
|
||
|
#
|
||
|
# This software may be used and distributed according to the terms of the
|
||
|
# GNU General Public License version 2, incorporated herein by reference.
|
||
|
|
||
|
import heapq
|
||
|
|
||
|
def ancestor(a, b, pfunc):
|
||
|
"""
|
||
|
return the least common ancestor of nodes a and b or None if there
|
||
|
is no such ancestor.
|
||
|
|
||
|
pfunc must return a list of parent vertices
|
||
|
"""
|
||
|
|
||
|
if a == b:
|
||
|
return a
|
||
|
|
||
|
# find depth from root of all ancestors
|
||
|
parentcache = {}
|
||
|
visit = [a, b]
|
||
|
depth = {}
|
||
|
while visit:
|
||
|
vertex = visit[-1]
|
||
|
pl = pfunc(vertex)
|
||
|
parentcache[vertex] = pl
|
||
|
if not pl:
|
||
|
depth[vertex] = 0
|
||
|
visit.pop()
|
||
|
else:
|
||
|
for p in pl:
|
||
|
if p == a or p == b: # did we find a or b as a parent?
|
||
|
return p # we're done
|
||
|
if p not in depth:
|
||
|
visit.append(p)
|
||
|
if visit[-1] == vertex:
|
||
|
depth[vertex] = min([depth[p] for p in pl]) - 1
|
||
|
visit.pop()
|
||
|
|
||
|
# traverse ancestors in order of decreasing distance from root
|
||
|
def ancestors(vertex):
|
||
|
h = [(depth[vertex], vertex)]
|
||
|
seen = set()
|
||
|
while h:
|
||
|
d, n = heapq.heappop(h)
|
||
|
if n not in seen:
|
||
|
seen.add(n)
|
||
|
yield (d, n)
|
||
|
for p in parentcache[n]:
|
||
|
heapq.heappush(h, (depth[p], p))
|
||
|
|
||
|
def generations(vertex):
|
||
|
sg, s = None, set()
|
||
|
for g, v in ancestors(vertex):
|
||
|
if g != sg:
|
||
|
if sg:
|
||
|
yield sg, s
|
||
|
sg, s = g, set((v,))
|
||
|
else:
|
||
|
s.add(v)
|
||
|
yield sg, s
|
||
|
|
||
|
x = generations(a)
|
||
|
y = generations(b)
|
||
|
gx = x.next()
|
||
|
gy = y.next()
|
||
|
|
||
|
# increment each ancestor list until it is closer to root than
|
||
|
# the other, or they match
|
||
|
try:
|
||
|
while 1:
|
||
|
if gx[0] == gy[0]:
|
||
|
for v in gx[1]:
|
||
|
if v in gy[1]:
|
||
|
return v
|
||
|
gy = y.next()
|
||
|
gx = x.next()
|
||
|
elif gx[0] > gy[0]:
|
||
|
gy = y.next()
|
||
|
else:
|
||
|
gx = x.next()
|
||
|
except StopIteration:
|
||
|
return None
|