##// 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 8 from node import *
9 9 from demandload import demandload
10 demandload(globals(), "heapq")
10 demandload(globals(), "ancestor")
11 11
12 12 class changectx(object):
13 13 """A changecontext object makes access to data related to a particular
@@ -161,103 +161,26 b' class filectx(object):'
161 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)
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]
164 acache = {}
178 165 flcache = {self._path:self._filelog, fc2._path:fc2._filelog}
179
180 while fv:
181 f,n = fv.pop()
182 try:
183 fl = flcache[f]
184 except KeyError:
166 def parents(vertex):
167 if vertex in acache:
168 return acache[vertex]
169 f, n = vertex
170 if f not in flcache:
185 171 flcache[f] = self._repo.file(f)
186 172 fl = flcache[f]
187 v = [n]
188 while v:
189 n = v.pop()
190 c = (f, n)
191 if c in ag:
192 continue
193
194 pl = [ n for n in fl.parents(n) if n != nullid ]
195 v += pl
196 pl = [ (f, n) for n in pl ]
173 pl = [ (f,p) for p in fl.parents(n) if p != nullid ]
197 174 re = fl.renamed(n)
198 175 if re:
199 176 pl.append(re)
200 if re not in ag:
201 fv.append(re)
202 ag[c] = pl
203
204 dist = {}
205 def depth(node):
206 try:
207 return dist[node]
208 except KeyError:
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))
177 acache[vertex]=pl
178 return pl
227 179
228 def generations(vertex):
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()
180 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
181 v = ancestor.ancestor(a, b, parents)
182 if v:
183 f,n = v
184 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
243 185
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 186 return None
@@ -13,7 +13,7 b' of the GNU General Public License, incor'
13 13 from node import *
14 14 from i18n import gettext as _
15 15 from demandload import demandload
16 demandload(globals(), "binascii changegroup errno heapq mdiff os")
16 demandload(globals(), "binascii changegroup errno ancestor mdiff os")
17 17 demandload(globals(), "sha struct util zlib")
18 18
19 19 # revlog version strings
@@ -1016,78 +1016,10 b' class revlog(object):'
1016 1016 def ancestor(self, a, b):
1017 1017 """calculate the least common ancestor of nodes a and b"""
1018 1018
1019 # start with some short cuts for the linear cases
1020 if a == b:
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
1019 def parents(node):
1020 return [p for p in self.parents(node) if p != nullid]
1042 1021
1043 # traverse ancestors in order of decreasing distance from root
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()
1022 return ancestor.ancestor(a, b, parents) or nullid
1091 1023
1092 1024 def group(self, nodelist, lookup, infocollect=None):
1093 1025 """calculate a delta group
General Comments 0
You need to be logged in to leave comments. Login now