##// END OF EJS Templates
ancestor.missingancestors: turn into a state-keeping class...
Siddharth Agarwal -
r23334:59e6e5dd default
parent child Browse files
Show More
@@ -134,83 +134,95 b' def ancestors(pfunc, *orignodes):'
134 134 return gca
135 135 return deepest(gca)
136 136
137 def missingancestors(revs, bases, pfunc):
138 """Return all the ancestors of revs that are not ancestors of bases.
139
140 This may include elements from revs.
137 class incrementalmissingancestors(object):
138 '''persistent state used to calculate missing ancestors incrementally
141 139
142 Equivalent to the revset (::revs - ::bases). Revs are returned in
143 revision number order, which is a topological order.
144
145 revs and bases should both be iterables. pfunc must return a list of
146 parent revs for a given revs.
147 """
140 Although similar in spirit to lazyancestors below, this is a separate class
141 because trying to support contains and missingancestors operations with the
142 same internal data structures adds needless complexity.'''
143 def __init__(self, pfunc, bases):
144 self.bases = set(bases)
145 if not self.bases:
146 self.bases.add(nullrev)
147 self.pfunc = pfunc
148 148
149 revsvisit = set(revs)
150 basesvisit = set(bases)
151 if not basesvisit:
152 basesvisit.add(nullrev)
153 bothvisit = revsvisit.intersection(basesvisit)
154 revsvisit.difference_update(bothvisit)
155 if not revsvisit:
156 return []
157 start = max(max(revsvisit), max(basesvisit))
158 # At this point, we hold the invariants that:
159 # - revsvisit is the set of nodes we know are an ancestor of at least one
160 # of the nodes in revs
161 # - basesvisit is the same for bases
162 # - bothvisit is the set of nodes we know are ancestors of at least one of
163 # the nodes in revs and one of the nodes in bases, and that are smaller
164 # than curr. bothvisit and revsvisit are mutually exclusive, but bothvisit
165 # is a subset of basesvisit.
166 # Now we walk down in reverse topo order, adding parents of nodes already
167 # visited to the sets while maintaining the invariants. When a node is found
168 # in both revsvisit and basesvisit, it is removed from revsvisit and added
169 # to bothvisit. When revsvisit becomes empty, there are no more ancestors of
170 # revs that aren't also ancestors of bases, so exit.
149 def missingancestors(self, revs):
150 '''return all the ancestors of revs that are not ancestors of self.bases
151
152 This may include elements from revs.
171 153
172 missing = []
173 for curr in xrange(start, nullrev, -1):
154 Equivalent to the revset (::revs - ::self.bases). Revs are returned in
155 revision number order, which is a topological order.'''
156 revsvisit = set(revs)
157 basesvisit = self.bases
158 pfunc = self.pfunc
159 bothvisit = revsvisit.intersection(basesvisit)
160 revsvisit.difference_update(bothvisit)
174 161 if not revsvisit:
175 break
162 return []
176 163
177 if curr in bothvisit:
178 bothvisit.remove(curr)
179 # curr's parents might have made it into revsvisit through another
180 # path
181 for p in pfunc(curr):
182 revsvisit.discard(p)
183 basesvisit.add(p)
184 bothvisit.add(p)
185 continue
164 start = max(max(revsvisit), max(basesvisit))
165 # At this point, we hold the invariants that:
166 # - revsvisit is the set of nodes we know are an ancestor of at least
167 # one of the nodes in revs
168 # - basesvisit is the same for bases
169 # - bothvisit is the set of nodes we know are ancestors of at least one
170 # of the nodes in revs and one of the nodes in bases. bothvisit and
171 # revsvisit are mutually exclusive, but bothvisit is a subset of
172 # basesvisit.
173 # Now we walk down in reverse topo order, adding parents of nodes
174 # already visited to the sets while maintaining the invariants. When a
175 # node is found in both revsvisit and basesvisit, it is removed from
176 # revsvisit and added to bothvisit. When revsvisit becomes empty, there
177 # are no more ancestors of revs that aren't also ancestors of bases, so
178 # exit.
179
180 missing = []
181 for curr in xrange(start, nullrev, -1):
182 if not revsvisit:
183 break
186 184
187 if curr in revsvisit:
188 missing.append(curr)
189 revsvisit.remove(curr)
190 thisvisit = revsvisit
191 othervisit = basesvisit
192 elif curr in basesvisit:
193 thisvisit = basesvisit
194 othervisit = revsvisit
195 else:
196 # not an ancestor of revs or bases: ignore
197 continue
185 if curr in bothvisit:
186 bothvisit.remove(curr)
187 # curr's parents might have made it into revsvisit through
188 # another path
189 for p in pfunc(curr):
190 revsvisit.discard(p)
191 basesvisit.add(p)
192 bothvisit.add(p)
193 continue
198 194
199 for p in pfunc(curr):
200 if p == nullrev:
201 pass
202 elif p in othervisit or p in bothvisit:
203 # p is implicitly in thisvisit. This means p is or should be
204 # in bothvisit
205 revsvisit.discard(p)
206 basesvisit.add(p)
207 bothvisit.add(p)
195 if curr in revsvisit:
196 missing.append(curr)
197 revsvisit.remove(curr)
198 thisvisit = revsvisit
199 othervisit = basesvisit
200 elif curr in basesvisit:
201 thisvisit = basesvisit
202 othervisit = revsvisit
208 203 else:
209 # visit later
210 thisvisit.add(p)
204 # not an ancestor of revs or bases: ignore
205 continue
211 206
212 missing.reverse()
213 return missing
207 for p in pfunc(curr):
208 if p == nullrev:
209 pass
210 elif p in othervisit or p in bothvisit:
211 # p is implicitly in thisvisit. This means p is or should be
212 # in bothvisit
213 revsvisit.discard(p)
214 basesvisit.add(p)
215 bothvisit.add(p)
216 else:
217 # visit later
218 thisvisit.add(p)
219
220 missing.reverse()
221 return missing
222
223 def missingancestors(revs, bases, pfunc):
224 inc = incrementalmissingancestors(pfunc, bases)
225 return inc.missingancestors(revs)
214 226
215 227 class lazyancestors(object):
216 228 def __init__(self, pfunc, revs, stoprev=0, inclusive=False):
@@ -88,7 +88,8 b' def test_missingancestors(seed, rng):'
88 88 revs = samplerevs(graphnodes)
89 89
90 90 # fast algorithm
91 h = ancestor.missingancestors(revs, bases, graph.__getitem__)
91 inc = ancestor.incrementalmissingancestors(graph.__getitem__, bases)
92 h = inc.missingancestors(revs)
92 93 # reference slow algorithm
93 94 r = naivemissingancestors(ancs, revs, bases)
94 95 if h != r:
General Comments 0
You need to be logged in to leave comments. Login now