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 |