##// END OF EJS Templates
ancestor.missingancestors: turn into a state-keeping class...
Siddharth Agarwal -
r23334:59e6e5dd default
parent child Browse files
Show More
@@ -134,40 +134,48 def ancestors(pfunc, *orignodes):
134 return gca
134 return gca
135 return deepest(gca)
135 return deepest(gca)
136
136
137 def missingancestors(revs, bases, pfunc):
137 class incrementalmissingancestors(object):
138 """Return all the ancestors of revs that are not ancestors of bases.
138 '''persistent state used to calculate missing ancestors incrementally
139
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
149 def missingancestors(self, revs):
150 '''return all the ancestors of revs that are not ancestors of self.bases
139
151
140 This may include elements from revs.
152 This may include elements from revs.
141
153
142 Equivalent to the revset (::revs - ::bases). Revs are returned in
154 Equivalent to the revset (::revs - ::self.bases). Revs are returned in
143 revision number order, which is a topological order.
155 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 """
148
149 revsvisit = set(revs)
156 revsvisit = set(revs)
150 basesvisit = set(bases)
157 basesvisit = self.bases
151 if not basesvisit:
158 pfunc = self.pfunc
152 basesvisit.add(nullrev)
153 bothvisit = revsvisit.intersection(basesvisit)
159 bothvisit = revsvisit.intersection(basesvisit)
154 revsvisit.difference_update(bothvisit)
160 revsvisit.difference_update(bothvisit)
155 if not revsvisit:
161 if not revsvisit:
156 return []
162 return []
163
157 start = max(max(revsvisit), max(basesvisit))
164 start = max(max(revsvisit), max(basesvisit))
158 # At this point, we hold the invariants that:
165 # At this point, we hold the invariants that:
159 # - revsvisit is the set of nodes we know are an ancestor of at least one
166 # - revsvisit is the set of nodes we know are an ancestor of at least
160 # of the nodes in revs
167 # one of the nodes in revs
161 # - basesvisit is the same for bases
168 # - basesvisit is the same for bases
162 # - bothvisit is the set of nodes we know are ancestors of at least one of
169 # - bothvisit is the set of nodes we know are ancestors of at least one
163 # the nodes in revs and one of the nodes in bases, and that are smaller
170 # of the nodes in revs and one of the nodes in bases. bothvisit and
164 # than curr. bothvisit and revsvisit are mutually exclusive, but bothvisit
171 # revsvisit are mutually exclusive, but bothvisit is a subset of
165 # is a subset of basesvisit.
172 # basesvisit.
166 # Now we walk down in reverse topo order, adding parents of nodes already
173 # Now we walk down in reverse topo order, adding parents of nodes
167 # visited to the sets while maintaining the invariants. When a node is found
174 # already visited to the sets while maintaining the invariants. When a
168 # in both revsvisit and basesvisit, it is removed from revsvisit and added
175 # node is found in both revsvisit and basesvisit, it is removed from
169 # to bothvisit. When revsvisit becomes empty, there are no more ancestors of
176 # revsvisit and added to bothvisit. When revsvisit becomes empty, there
170 # revs that aren't also ancestors of bases, so exit.
177 # are no more ancestors of revs that aren't also ancestors of bases, so
178 # exit.
171
179
172 missing = []
180 missing = []
173 for curr in xrange(start, nullrev, -1):
181 for curr in xrange(start, nullrev, -1):
@@ -176,8 +184,8 def missingancestors(revs, bases, pfunc)
176
184
177 if curr in bothvisit:
185 if curr in bothvisit:
178 bothvisit.remove(curr)
186 bothvisit.remove(curr)
179 # curr's parents might have made it into revsvisit through another
187 # curr's parents might have made it into revsvisit through
180 # path
188 # another path
181 for p in pfunc(curr):
189 for p in pfunc(curr):
182 revsvisit.discard(p)
190 revsvisit.discard(p)
183 basesvisit.add(p)
191 basesvisit.add(p)
@@ -212,6 +220,10 def missingancestors(revs, bases, pfunc)
212 missing.reverse()
220 missing.reverse()
213 return missing
221 return missing
214
222
223 def missingancestors(revs, bases, pfunc):
224 inc = incrementalmissingancestors(pfunc, bases)
225 return inc.missingancestors(revs)
226
215 class lazyancestors(object):
227 class lazyancestors(object):
216 def __init__(self, pfunc, revs, stoprev=0, inclusive=False):
228 def __init__(self, pfunc, revs, stoprev=0, inclusive=False):
217 """Create a new object generating ancestors for the given revs. Does
229 """Create a new object generating ancestors for the given revs. Does
@@ -88,7 +88,8 def test_missingancestors(seed, rng):
88 revs = samplerevs(graphnodes)
88 revs = samplerevs(graphnodes)
89
89
90 # fast algorithm
90 # fast algorithm
91 h = ancestor.missingancestors(revs, bases, graph.__getitem__)
91 inc = ancestor.incrementalmissingancestors(graph.__getitem__, bases)
92 h = inc.missingancestors(revs)
92 # reference slow algorithm
93 # reference slow algorithm
93 r = naivemissingancestors(ancs, revs, bases)
94 r = naivemissingancestors(ancs, revs, bases)
94 if h != r:
95 if h != r:
General Comments 0
You need to be logged in to leave comments. Login now