# HG changeset patch # User Patrick Mezard # Date 2012-02-26 16:10:57 # Node ID 352053e6cd8ef8283c797a853758d6da20df6080 # Parent 6863caf01daa754fc1fc9b2d6d769c6edc8d951c context: add followfirst arg to filectx and workingfilectx When _followfirst() revset was introduced it seemed to be the sole user of such an argument, so filectx.ancestors() was duplicated and modified instead. It now appears this argument could be used when computing the set of files to be considered when --patch or --stat are passed along with --follow FILE. diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -611,11 +611,12 @@ class filectx(object): return None - def ancestors(self): + def ancestors(self, followfirst=False): visit = {} c = self + cut = followfirst and 1 or None while True: - for parent in c.parents(): + for parent in c.parents()[:cut]: visit[(parent.rev(), parent.node())] = parent if not visit: break @@ -930,9 +931,10 @@ class workingctx(changectx): finally: wlock.release() - def ancestors(self): + def ancestors(self, followfirst=False): + cut = followfirst and 1 or None for a in self._repo.changelog.ancestors( - *[p.rev() for p in self._parents]): + *[p.rev() for p in self._parents[:cut]]): yield changectx(self._repo, a) def undelete(self, list): diff --git a/mercurial/revset.py b/mercurial/revset.py --- a/mercurial/revset.py +++ b/mercurial/revset.py @@ -441,63 +441,45 @@ def first(repo, subset, x): """ return limit(repo, subset, x) +def _follow(repo, subset, x, name, followfirst=False): + l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name) + c = repo['.'] + if l: + x = getstring(l[0], _("%s expected a filename") % name) + if x in c: + cx = c[x] + s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst)) + # include the revision responsible for the most recent version + s.add(cx.linkrev()) + else: + return [] + else: + cut = followfirst and 1 or None + cl = repo.changelog + s = set() + visit = [c.rev()] + while visit: + for prev in cl.parentrevs(visit.pop(0))[:cut]: + if prev not in s and prev != nodemod.nullrev: + visit.append(prev) + s.add(prev) + s.add(c.rev()) + + return [r for r in subset if r in s] + def follow(repo, subset, x): """``follow([file])`` An alias for ``::.`` (ancestors of the working copy's first parent). If a filename is specified, the history of the given file is followed, including copies. """ - # i18n: "follow" is a keyword - l = getargs(x, 0, 1, _("follow takes no arguments or a filename")) - c = repo['.'] - if l: - x = getstring(l[0], _("follow expected a filename")) - if x in c: - cx = c[x] - s = set(ctx.rev() for ctx in cx.ancestors()) - # include the revision responsible for the most recent version - s.add(cx.linkrev()) - else: - return [] - else: - s = set(repo.changelog.ancestors(c.rev())) - s.add(c.rev()) - - return [r for r in subset if r in s] + return _follow(repo, subset, x, 'follow') def _followfirst(repo, subset, x): # ``followfirst([file])`` # Like ``follow([file])`` but follows only the first parent of # every revision or file revision. - # i18n: "_followfirst" is a keyword - l = getargs(x, 0, 1, _("_followfirst takes no arguments or a filename")) - c = repo['.'] - if l: - x = getstring(l[0], _("_followfirst expected a filename")) - if x not in c: - return [] - cx = c[x] - visit = {} - s = set([cx.linkrev()]) - while True: - for p in cx.parents()[:1]: - visit[(p.rev(), p.node())] = p - if not visit: - break - cx = visit.pop(max(visit)) - s.add(cx.rev()) - else: - cl = repo.changelog - s = set() - visit = [c.rev()] - while visit: - for prev in cl.parentrevs(visit.pop(0))[:1]: - if prev not in s and prev != nodemod.nullrev: - visit.append(prev) - s.add(prev) - s.add(c.rev()) - - return [r for r in subset if r in s] + return _follow(repo, subset, x, '_followfirst', followfirst=True) def getall(repo, subset, x): """``all()``