diff -r 2c7c4824969e -r a3da6f298592 mercurial/revset.py --- a/mercurial/revset.py Sat Jul 07 00:47:30 2012 -0400 +++ b/mercurial/revset.py Sat Jul 07 00:47:55 2012 -0400 @@ -561,6 +561,50 @@ # Like ``descendants(set)`` but follows only the first parents. return _descendants(repo, subset, x, followfirst=True) +def destination(repo, subset, x): + """``destination([set])`` + Changesets that were created by a graft, transplant or rebase operation, + with the given revisions specified as the source. Omitting the optional set + is the same as passing all(). + """ + if x is not None: + args = set(getset(repo, range(len(repo)), x)) + else: + args = set(getall(repo, range(len(repo)), x)) + + dests = set() + + # subset contains all of the possible destinations that can be returned, so + # iterate over them and see if their source(s) were provided in the args. + # Even if the immediate src of r is not in the args, src's source (or + # further back) may be. Scanning back further than the immediate src allows + # transitive transplants and rebases to yield the same results as transitive + # grafts. + for r in subset: + src = _getrevsource(repo, r) + lineage = None + + while src is not None: + if lineage is None: + lineage = list() + + lineage.append(r) + + # The visited lineage is a match if the current source is in the arg + # set. Since every candidate dest is visited by way of iterating + # subset, any dests futher back in the lineage will be tested by a + # different iteration over subset. Likewise, if the src was already + # selected, the current lineage can be selected without going back + # further. + if src in args or src in dests: + dests.update(lineage) + break + + r = src + src = _getrevsource(repo, r) + + return [r for r in subset if r in dests] + def draft(repo, subset, x): """``draft()`` Changeset in draft phase.""" @@ -1399,6 +1443,7 @@ "desc": desc, "descendants": descendants, "_firstdescendants": _firstdescendants, + "destination": destination, "draft": draft, "extinct": extinct, "extra": extra,