comparison mercurial/revlog.py @ 1457:518da3c3b6ce

This implements the nodesbetween method, and it removes the newer method and replaces it with calls to nodesbetween. nodesbetween calculates all the changesets needed to have a complete revision graph between a given set of base nodes and a given set of head nodes.
author Eric Hopper <hopper@omnifarious.org>
date Fri, 07 Oct 2005 10:48:27 -0700
parents 0e2be889ccd7
children 1033892bbb87
comparison
equal deleted inserted replaced
1389:9b3ef6f3cef5 1457:518da3c3b6ce
244 if p not in reachable: 244 if p not in reachable:
245 reachable[p] = 1 245 reachable[p] = 1
246 visit.append(p) 246 visit.append(p)
247 return reachable 247 return reachable
248 248
249 def nodesbetween(self, roots=None, heads=None):
250 """Return a tuple containing three elements. Elements 1 and 2 contain
251 a final list bases and heads after all the unreachable ones have been
252 pruned. Element 0 contains a topologically sorted list of all
253
254 nodes that satisfy these constraints:
255 1. All nodes must be descended from a node in roots (the nodes on
256 roots are considered descended from themselves).
257 2. All nodes must also be ancestors of a node in heads (the nodes in
258 heads are considered to be their own ancestors).
259
260 If roots is unspecified, nullid is assumed as the only root.
261 If heads is unspecified, it is taken to be the output of the
262 heads method (i.e. a list of all nodes in the repository that
263 have no children)."""
264 if roots is not None:
265 roots = list(roots)
266 lowestrev = min([self.rev(n) for n in roots])
267 else:
268 roots = [nullid] # Everybody's a descendent of nullid
269 lowestrev = -1
270 if (lowestrev == -1) and (heads is None):
271 # We want _all_ the nodes!
272 return ([self.node(r) for r in xrange(0, self.count())],
273 [nullid], list(self.heads()))
274 if heads is None:
275 # All nodes are ancestors, so the latest ancestor is the last
276 # node.
277 highestrev = self.count() - 1
278 # Set ancestors to None to signal that every node is an ancestor.
279 ancestors = None
280 # Set heads to an empty dictionary for later discovery of heads
281 heads = {}
282 else:
283 ancestors = {}
284 # Start at the top and keep marking parents until we're done.
285 nodestotag = list(heads)
286 # Turn heads into a dictionary so we can remove 'fake' heads.
287 # Also, later we will be using it to filter out the heads we can't
288 # find from roots.
289 heads = dict.fromkeys(heads, 0)
290 # Remember where the top was so we can use it as a limit later.
291 highestrev = max([self.rev(n) for n in nodestotag])
292 while nodestotag:
293 # grab a node to tag
294 n = nodestotag.pop()
295 # Never tag nullid
296 if n == nullid:
297 continue
298 # A node's revision number represents its place in a
299 # topologically sorted list of nodes.
300 r = self.rev(n)
301 if r >= lowestrev:
302 if n not in ancestors:
303 # If we are possibly a descendent of one of the roots
304 # and we haven't already been marked as an ancestor
305 ancestors[n] = 1 # Mark as ancestor
306 # Add non-nullid parents to list of nodes to tag.
307 nodestotag.extend([p for p in self.parents(n) if
308 p != nullid])
309 elif n in heads: # We've seen it before, is it a fake head?
310 # So it is, real heads should not be the ancestors of
311 # any other heads.
312 heads.pop(n)
313 # Now that we have our set of ancestors, we want to remove any
314 # roots that are not ancestors.
315
316 # If one of the roots was nullid, everything is included anyway.
317 if lowestrev > -1:
318 # But, since we weren't, let's recompute the lowest rev to not
319 # include roots that aren't ancestors.
320
321 # Filter out roots that aren't ancestors of heads
322 roots = [n for n in roots if n in ancestors]
323 # Recompute the lowest revision
324 if roots:
325 lowestrev = min([self.rev(n) for n in roots])
326 else:
327 # No more roots? Return empty list
328 return ([], [], [])
329 else:
330 # We are descending from nullid, and don't need to care about
331 # any other roots.
332 lowestrev = -1
333 roots = [nullid]
334 # Transform our roots list into a 'set' (i.e. a dictionary where the
335 # values don't matter.
336 descendents = dict.fromkeys(roots, 1)
337 # Also, keep the original roots so we can filter out roots that aren't
338 # 'real' roots (i.e. are descended from other roots).
339 roots = descendents.copy()
340 # Our topologically sorted list of output nodes.
341 orderedout = []
342 # Don't start at nullid since we don't want nullid in our output list,
343 # and if nullid shows up in descedents, empty parents will look like
344 # they're descendents.
345 for r in xrange(max(lowestrev, 0), highestrev + 1):
346 n = self.node(r)
347 isdescendent = False
348 if lowestrev == -1: # Everybody is a descendent of nullid
349 isdescendent = True
350 elif n in descendents:
351 # n is already a descendent
352 isdescendent = True
353 # This check only needs to be done here because all the roots
354 # will start being marked is descendents before the loop.
355 if n in roots:
356 # If n was a root, check if it's a 'real' root.
357 p = tuple(self.parents(n))
358 # If any of its parents are descendents, it's not a root.
359 if (p[0] in descendents) or (p[1] in descendents):
360 roots.pop(n)
361 else:
362 p = tuple(self.parents(n))
363 # A node is a descendent if either of its parents are
364 # descendents. (We seeded the dependents list with the roots
365 # up there, remember?)
366 if (p[0] in descendents) or (p[1] in descendents):
367 descendents[n] = 1
368 isdescendent = True
369 if isdescendent and ((ancestors is None) or (n in ancestors)):
370 # Only include nodes that are both descendents and ancestors.
371 orderedout.append(n)
372 if (ancestors is not None) and (n in heads):
373 # We're trying to figure out which heads are reachable
374 # from roots.
375 # Mark this head as having been reached
376 heads[n] = 1
377 elif ancestors is None:
378 # Otherwise, we're trying to discover the heads.
379 # Assume this is a head because if it isn't, the next step
380 # will eventually remove it.
381 heads[n] = 1
382 # But, obviously its parents aren't.
383 for p in self.parents(n):
384 heads.pop(p, None)
385 heads = [n for n in heads.iterkeys() if heads[n] != 0]
386 roots = roots.keys()
387 assert orderedout
388 assert roots
389 assert heads
390 return (orderedout, roots, heads)
391
249 def heads(self, stop=None): 392 def heads(self, stop=None):
250 """return the list of all nodes that have no children""" 393 """return the list of all nodes that have no children"""
251 p = {} 394 p = {}
252 h = [] 395 h = []
253 stoprev = 0 396 stoprev = 0