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(), " |
|
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 |
|
|
169 | f, n = vertex | |
183 |
|
|
170 | if f not in flcache: | |
184 | except KeyError: |
|
|||
185 | flcache[f] = self._repo.file(f) |
|
171 | flcache[f] = self._repo.file(f) | |
186 |
|
|
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 |
|
|
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 |
|
|
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 |
|
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