##// END OF EJS Templates
ancestor: fix a comment (followup to 0b03454abae7)
Siddharth Agarwal -
r17976:09d5681d default
parent child Browse files
Show More
@@ -1,255 +1,255
1 1 # ancestor.py - generic DAG ancestor algorithm for mercurial
2 2 #
3 3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import heapq
9 9 from node import nullrev
10 10
11 11 def ancestor(a, b, pfunc):
12 12 """
13 13 Returns the common ancestor of a and b that is furthest from a
14 14 root (as measured by longest path) or None if no ancestor is
15 15 found. If there are multiple common ancestors at the same
16 16 distance, the first one found is returned.
17 17
18 18 pfunc must return a list of parent vertices for a given vertex
19 19 """
20 20
21 21 if a == b:
22 22 return a
23 23
24 24 a, b = sorted([a, b])
25 25
26 26 # find depth from root of all ancestors
27 27 # depth is stored as a negative for heapq
28 28 parentcache = {}
29 29 visit = [a, b]
30 30 depth = {}
31 31 while visit:
32 32 vertex = visit[-1]
33 33 pl = pfunc(vertex)
34 34 parentcache[vertex] = pl
35 35 if not pl:
36 36 depth[vertex] = 0
37 37 visit.pop()
38 38 else:
39 39 for p in pl:
40 40 if p == a or p == b: # did we find a or b as a parent?
41 41 return p # we're done
42 42 if p not in depth:
43 43 visit.append(p)
44 44 if visit[-1] == vertex:
45 45 # -(maximum distance of parents + 1)
46 46 depth[vertex] = min([depth[p] for p in pl]) - 1
47 47 visit.pop()
48 48
49 49 # traverse ancestors in order of decreasing distance from root
50 50 def ancestors(vertex):
51 51 h = [(depth[vertex], vertex)]
52 52 seen = set()
53 53 while h:
54 54 d, n = heapq.heappop(h)
55 55 if n not in seen:
56 56 seen.add(n)
57 57 yield (d, n)
58 58 for p in parentcache[n]:
59 59 heapq.heappush(h, (depth[p], p))
60 60
61 61 def generations(vertex):
62 62 sg, s = None, set()
63 63 for g, v in ancestors(vertex):
64 64 if g != sg:
65 65 if sg:
66 66 yield sg, s
67 67 sg, s = g, set((v,))
68 68 else:
69 69 s.add(v)
70 70 yield sg, s
71 71
72 72 x = generations(a)
73 73 y = generations(b)
74 74 gx = x.next()
75 75 gy = y.next()
76 76
77 77 # increment each ancestor list until it is closer to root than
78 78 # the other, or they match
79 79 try:
80 80 while True:
81 81 if gx[0] == gy[0]:
82 82 for v in gx[1]:
83 83 if v in gy[1]:
84 84 return v
85 85 gy = y.next()
86 86 gx = x.next()
87 87 elif gx[0] > gy[0]:
88 88 gy = y.next()
89 89 else:
90 90 gx = x.next()
91 91 except StopIteration:
92 92 return None
93 93
94 94 def missingancestors(revs, bases, pfunc):
95 95 """Return all the ancestors of revs that are not ancestors of bases.
96 96
97 97 This may include elements from revs.
98 98
99 99 Equivalent to the revset (::revs - ::bases). Revs are returned in
100 100 revision number order, which is a topological order.
101 101
102 102 revs and bases should both be iterables. pfunc must return a list of
103 103 parent revs for a given revs.
104 104
105 105 graph is a dict of child->parent adjacency lists for this graph:
106 106 o 13
107 107 |
108 108 | o 12
109 109 | |
110 110 | | o 11
111 111 | | |\
112 112 | | | | o 10
113 113 | | | | |
114 114 | o---+ | 9
115 115 | | | | |
116 116 o | | | | 8
117 117 / / / /
118 118 | | o | 7
119 119 | | | |
120 120 o---+ | 6
121 121 / / /
122 122 | | o 5
123 123 | |/
124 124 | o 4
125 125 | |
126 126 o | 3
127 127 | |
128 128 | o 2
129 129 |/
130 130 o 1
131 131 |
132 132 o 0
133 133 >>> graph = {0: [-1], 1: [0], 2: [1], 3: [1], 4: [2], 5: [4], 6: [4],
134 134 ... 7: [4], 8: [-1], 9: [6, 7], 10: [5], 11: [3, 7], 12: [9],
135 135 ... 13: [8]}
136 136 >>> pfunc = graph.get
137 137
138 138 Empty revs
139 139 >>> missingancestors([], [1], pfunc)
140 140 []
141 141 >>> missingancestors([], [], pfunc)
142 142 []
143 143
144 144 If bases is empty, it's the same as if it were [nullrev]
145 145 >>> missingancestors([12], [], pfunc)
146 146 [0, 1, 2, 4, 6, 7, 9, 12]
147 147
148 148 Trivial case: revs == bases
149 149 >>> missingancestors([0], [0], pfunc)
150 150 []
151 151 >>> missingancestors([4, 5, 6], [6, 5, 4], pfunc)
152 152 []
153 153
154 154 With nullrev
155 155 >>> missingancestors([-1], [12], pfunc)
156 156 []
157 157 >>> missingancestors([12], [-1], pfunc)
158 158 [0, 1, 2, 4, 6, 7, 9, 12]
159 159
160 160 9 is a parent of 12. 7 is a parent of 9, so an ancestor of 12. 6 is an
161 161 ancestor of 12 but not of 7.
162 162 >>> missingancestors([12], [9], pfunc)
163 163 [12]
164 164 >>> missingancestors([9], [12], pfunc)
165 165 []
166 166 >>> missingancestors([12, 9], [7], pfunc)
167 167 [6, 9, 12]
168 168 >>> missingancestors([7, 6], [12], pfunc)
169 169 []
170 170
171 171 More complex cases
172 172 >>> missingancestors([10], [11, 12], pfunc)
173 173 [5, 10]
174 174 >>> missingancestors([11], [10], pfunc)
175 175 [3, 7, 11]
176 176 >>> missingancestors([11], [10, 12], pfunc)
177 177 [3, 11]
178 178 >>> missingancestors([12], [10], pfunc)
179 179 [6, 7, 9, 12]
180 180 >>> missingancestors([12], [11], pfunc)
181 181 [6, 9, 12]
182 182 >>> missingancestors([10, 11, 12], [13], pfunc)
183 183 [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12]
184 184 >>> missingancestors([13], [10, 11, 12], pfunc)
185 185 [8, 13]
186 186 """
187 187
188 188 revsvisit = set(revs)
189 189 basesvisit = set(bases)
190 190 if not revsvisit:
191 191 return []
192 192 if not basesvisit:
193 193 basesvisit.add(nullrev)
194 194 start = max(max(revsvisit), max(basesvisit))
195 195 bothvisit = revsvisit.intersection(basesvisit)
196 196 revsvisit.difference_update(bothvisit)
197 197 basesvisit.difference_update(bothvisit)
198 198 # At this point, we hold the invariants that:
199 199 # - revsvisit is the set of nodes we know are an ancestor of at least one
200 200 # of the nodes in revs
201 201 # - basesvisit is the same for bases
202 202 # - bothvisit is the set of nodes we know are ancestors of at least one of
203 203 # the nodes in revs and one of the nodes in bases
204 204 # - a node may be in none or one, but not more, of revsvisit, basesvisit
205 205 # and bothvisit at any given time
206 206 # Now we walk down in reverse topo order, adding parents of nodes already
207 207 # visited to the sets while maintaining the invariants. When a node is
208 208 # found in both revsvisit and basesvisit, it is removed from them and
209 209 # added to bothvisit instead. When revsvisit becomes empty, there are no
210 210 # more ancestors of revs that aren't also ancestors of bases, so exit.
211 211
212 212 missing = []
213 213 for curr in xrange(start, nullrev, -1):
214 214 if not revsvisit:
215 215 break
216 216
217 217 if curr in bothvisit:
218 218 bothvisit.remove(curr)
219 219 # curr's parents might have made it into revsvisit or basesvisit
220 220 # through another path
221 221 for p in pfunc(curr):
222 222 revsvisit.discard(p)
223 223 basesvisit.discard(p)
224 224 bothvisit.add(p)
225 225 continue
226 226
227 227 # curr will never be in both revsvisit and basesvisit, since if it
228 228 # were it'd have been pushed to bothvisit
229 229 if curr in revsvisit:
230 230 missing.append(curr)
231 231 thisvisit = revsvisit
232 232 othervisit = basesvisit
233 233 elif curr in basesvisit:
234 234 thisvisit = basesvisit
235 235 othervisit = revsvisit
236 236 else:
237 # not an ancestor of a or b: ignore
237 # not an ancestor of revs or bases: ignore
238 238 continue
239 239
240 240 thisvisit.remove(curr)
241 241 for p in pfunc(curr):
242 242 if p == nullrev:
243 243 pass
244 244 elif p in othervisit or p in bothvisit:
245 245 # p is implicitly in thisvisit. This means p is or should be
246 246 # in bothvisit
247 247 revsvisit.discard(p)
248 248 basesvisit.discard(p)
249 249 bothvisit.add(p)
250 250 else:
251 251 # visit later
252 252 thisvisit.add(p)
253 253
254 254 missing.reverse()
255 255 return missing
General Comments 0
You need to be logged in to leave comments. Login now