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(), " |
|
|
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 |
|
|
|
183 |
|
|
|
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 |
|
|
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 |
|
|
198 | 175 |
|
|
199 | 176 |
|
|
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 |
|
|
|
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 |
|
@@ -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 |
|
|
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