##// END OF EJS Templates
Abstract ancestor algorithm into generic function...
Matt Mackall -
r3135:b1db258e default
parent child Browse files
Show More
@@ -0,0 +1,83 b''
1 # ancestor.py - generic DAG ancestor algorithm for mercurial
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7
8 import heapq
9
10 def ancestor(a, b, pfunc):
11 """
12 return the least common ancestor of nodes a and b or None if there
13 is no such ancestor.
14
15 pfunc must return a list of parent vertices
16 """
17
18 if a == b:
19 return a
20
21 # find depth from root of all ancestors
22 visit = [a, b]
23 depth = {}
24 while visit:
25 vertex = visit[-1]
26 pl = pfunc(vertex)
27 if not pl:
28 depth[vertex] = 0
29 visit.pop()
30 else:
31 for p in pl:
32 if p == a or p == b: # did we find a or b as a parent?
33 return p # we're done
34 if p not in depth:
35 visit.append(p)
36 if visit[-1] == vertex:
37 depth[vertex] = min([depth[p] for p in pl]) - 1
38 visit.pop()
39
40 # traverse ancestors in order of decreasing distance from root
41 def ancestors(vertex):
42 h = [(depth[vertex], vertex)]
43 seen = {}
44 while h:
45 d, n = heapq.heappop(h)
46 if n not in seen:
47 seen[n] = 1
48 yield (d, n)
49 for p in pfunc(n):
50 heapq.heappush(h, (depth[p], p))
51
52 def generations(vertex):
53 sg, s = None, {}
54 for g,v in ancestors(vertex):
55 if g != sg:
56 if sg:
57 yield sg, s
58 sg, s = g, {v:1}
59 else:
60 s[v] = 1
61 yield sg, s
62
63 x = generations(a)
64 y = generations(b)
65 gx = x.next()
66 gy = y.next()
67
68 # increment each ancestor list until it is closer to root than
69 # the other, or they match
70 try:
71 while 1:
72 if gx[0] == gy[0]:
73 for v in gx[1]:
74 if v in gy[1]:
75 return v
76 gy = y.next()
77 gx = x.next()
78 elif gx[0] > gy[0]:
79 gy = y.next()
80 else:
81 gx = x.next()
82 except StopIteration:
83 return None
@@ -7,7 +7,7 b''
7
7
8 from node import *
8 from node import *
9 from demandload import demandload
9 from demandload import demandload
10 demandload(globals(), "heapq")
10 demandload(globals(), "ancestor")
11
11
12 class changectx(object):
12 class changectx(object):
13 """A changecontext object makes access to data related to a particular
13 """A changecontext object makes access to data related to a particular
@@ -161,103 +161,26 b' class filectx(object):'
161 find the common ancestor file context, if any, of self, and fc2
161 find the common ancestor file context, if any, of self, and fc2
162 """
162 """
163
163
164 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
164 acache = {}
165
166 if a == b:
167 return self
168
169 if a[0] == b[0]:
170 n = self._filelog.ancestor(a[1], b[1])
171 if n != nullid:
172 return filectx(self._repo, self._path,
173 fileid=n, filelog=self._filelog)
174
175 # build a graph of all ancestors, crossing renames
176 ag = {}
177 fv = [a, b]
178 flcache = {self._path:self._filelog, fc2._path:fc2._filelog}
165 flcache = {self._path:self._filelog, fc2._path:fc2._filelog}
179
166 def parents(vertex):
180 while fv:
167 if vertex in acache:
181 f,n = fv.pop()
168 return acache[vertex]
182 try:
169 f, n = vertex
183 fl = flcache[f]
170 if f not in flcache:
184 except KeyError:
185 flcache[f] = self._repo.file(f)
171 flcache[f] = self._repo.file(f)
186 fl = flcache[f]
172 fl = flcache[f]
187 v = [n]
173 pl = [ (f,p) for p in fl.parents(n) if p != nullid ]
188 while v:
174 re = fl.renamed(n)
189 n = v.pop()
175 if re:
190 c = (f, n)
176 pl.append(re)
191 if c in ag:
177 acache[vertex]=pl
192 continue
178 return pl
193
194 pl = [ n for n in fl.parents(n) if n != nullid ]
195 v += pl
196 pl = [ (f, n) for n in pl ]
197 re = fl.renamed(n)
198 if re:
199 pl.append(re)
200 if re not in ag:
201 fv.append(re)
202 ag[c] = pl
203
179
204 dist = {}
180 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
205 def depth(node):
181 v = ancestor.ancestor(a, b, parents)
206 try:
182 if v:
207 return dist[node]
183 f,n = v
208 except KeyError:
184 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
209 pl = ag[node]
210 if not pl:
211 dist[node] = 0
212 else:
213 dist[node] = max([depth(p) for p in pl]) + 1
214 return dist[node]
215
216 # traverse ancestors in order of decreasing distance from root
217 def ancestors(vertex):
218 h = [(-depth(vertex), vertex)]
219 seen = {}
220 while h:
221 d, v = heapq.heappop(h)
222 if v not in seen:
223 seen[v] = 1
224 yield (-d, v)
225 for p in ag[v]:
226 heapq.heappush(h, (-depth(p), p))
227
185
228 def generations(vertex):
186 return None
229 sg, s = None, {}
230 for g,v in ancestors(vertex):
231 if g != sg:
232 if sg:
233 yield sg, s
234 sg, s = g, {v:1}
235 else:
236 s[v] = 1
237 yield sg, s
238
239 x = generations(a)
240 y = generations(b)
241 gx = x.next()
242 gy = y.next()
243
244 # increment each ancestor list until it is closer to root than
245 # the other, or they match
246 try:
247 while 1:
248 if gx[0] == gy[0]:
249 # find the intersection
250 i = [ n for n in gx[1] if n in gy[1] ]
251 if i:
252 fp,fn = i[0]
253 fl = flcache[fp]
254 return filectx(self._repo, fp, fileid=fn, filelog=fl)
255 else:
256 gy = y.next()
257 gx = x.next()
258 elif gx[0] < gy[0]:
259 gy = y.next()
260 else:
261 gx = x.next()
262 except StopIteration:
263 return None
@@ -13,7 +13,7 b' of the GNU General Public License, incor'
13 from node import *
13 from node import *
14 from i18n import gettext as _
14 from i18n import gettext as _
15 from demandload import demandload
15 from demandload import demandload
16 demandload(globals(), "binascii changegroup errno heapq mdiff os")
16 demandload(globals(), "binascii changegroup errno ancestor mdiff os")
17 demandload(globals(), "sha struct util zlib")
17 demandload(globals(), "sha struct util zlib")
18
18
19 # revlog version strings
19 # revlog version strings
@@ -1016,78 +1016,10 b' class revlog(object):'
1016 def ancestor(self, a, b):
1016 def ancestor(self, a, b):
1017 """calculate the least common ancestor of nodes a and b"""
1017 """calculate the least common ancestor of nodes a and b"""
1018
1018
1019 # start with some short cuts for the linear cases
1019 def parents(node):
1020 if a == b:
1020 return [p for p in self.parents(node) if p != nullid]
1021 return a
1022 ra = self.rev(a)
1023 rb = self.rev(b)
1024 if ra < rb:
1025 last = b
1026 first = a
1027 else:
1028 last = a
1029 first = b
1030
1031 # reachable won't include stop in the list, so we have to use a parent
1032 reachable = self.reachable(last, stop=self.parents(first)[0])
1033 if first in reachable:
1034 return first
1035
1036 # calculate the distance of every node from root
1037 dist = {nullid: 0}
1038 for i in xrange(self.count()):
1039 n = self.node(i)
1040 p1, p2 = self.parents(n)
1041 dist[n] = max(dist[p1], dist[p2]) + 1
1042
1021
1043 # traverse ancestors in order of decreasing distance from root
1022 return ancestor.ancestor(a, b, parents) or nullid
1044 def ancestors(node):
1045 # we store negative distances because heap returns smallest member
1046 h = [(-dist[node], node)]
1047 seen = {}
1048 while h:
1049 d, n = heapq.heappop(h)
1050 if n not in seen:
1051 seen[n] = 1
1052 yield (-d, n)
1053 for p in self.parents(n):
1054 heapq.heappush(h, (-dist[p], p))
1055
1056 def generations(node):
1057 sg, s = None, {}
1058 for g,n in ancestors(node):
1059 if g != sg:
1060 if sg:
1061 yield sg, s
1062 sg, s = g, {n:1}
1063 else:
1064 s[n] = 1
1065 yield sg, s
1066
1067 x = generations(a)
1068 y = generations(b)
1069 gx = x.next()
1070 gy = y.next()
1071
1072 # increment each ancestor list until it is closer to root than
1073 # the other, or they match
1074 while 1:
1075 #print "ancestor gen %s %s" % (gx[0], gy[0])
1076 if gx[0] == gy[0]:
1077 # find the intersection
1078 i = [ n for n in gx[1] if n in gy[1] ]
1079 if i:
1080 return i[0]
1081 else:
1082 #print "next"
1083 gy = y.next()
1084 gx = x.next()
1085 elif gx[0] < gy[0]:
1086 #print "next y"
1087 gy = y.next()
1088 else:
1089 #print "next x"
1090 gx = x.next()
1091
1023
1092 def group(self, nodelist, lookup, infocollect=None):
1024 def group(self, nodelist, lookup, infocollect=None):
1093 """calculate a delta group
1025 """calculate a delta group
General Comments 0
You need to be logged in to leave comments. Login now