# HG changeset patch # User Patrick Mezard # Date 2012-04-11 09:25:34 # Node ID 2cbd7dd0cc1fa78e3f9d7eb69973a370c366c253 # Parent d74099ac2ac18a424f621cd27d37d7862b62291a graphlog: fix --follow-first --rev combinations This solves a similar problem than the previous --follow/--rev patch. This time we need changelog.ancestors()/descendants() filtering on first parent. Duplicating the code looked better than introducing keyword arguments. Besides, the ancestors() version was already implemented in follow() revset. diff --git a/hgext/graphlog.py b/hgext/graphlog.py --- a/hgext/graphlog.py +++ b/hgext/graphlog.py @@ -279,11 +279,12 @@ def _makelogrevset(repo, pats, opts, rev the files to be detailed when displaying the revision. """ opt2revset = { - 'follow_first': ('_followfirst()', None), 'no_merges': ('not merge()', None), 'only_merges': ('merge()', None), '_ancestors': ('ancestors(%(val)s)', None), + '_fancestors': ('_firstancestors(%(val)s)', None), '_descendants': ('descendants(%(val)s)', None), + '_fdescendants': ('_firstdescendants(%(val)s)', None), '_matchfiles': ('_matchfiles(%(val)s)', None), 'date': ('date(%(val)r)', None), 'branch': ('branch(%(val)r)', ' or '), @@ -299,8 +300,6 @@ def _makelogrevset(repo, pats, opts, rev # follow or not follow? follow = opts.get('follow') or opts.get('follow_first') followfirst = opts.get('follow_first') - if 'follow_first' in opts: - del opts['follow_first'] # --follow with FILE behaviour depends on revs... startrev = revs[0] followdescendants = len(revs) > 1 and revs[0] < revs[1] @@ -356,7 +355,10 @@ def _makelogrevset(repo, pats, opts, rev if pats: opts['_patsfollowfirst'] = list(pats) else: - opts['follow_first'] = True + if followdescendants: + opts['_fdescendants'] = str(startrev) + else: + opts['_fancestors'] = str(startrev) else: if pats: opts['_patsfollow'] = list(pats) diff --git a/mercurial/revset.py b/mercurial/revset.py --- a/mercurial/revset.py +++ b/mercurial/revset.py @@ -13,6 +13,39 @@ import match as matchmod from i18n import _ import encoding +def _revancestors(repo, revs, followfirst): + """Like revlog.ancestors(), but supports followfirst.""" + cut = followfirst and 1 or None + cl = repo.changelog + visit = list(revs) + seen = set([nodemod.nullrev]) + while visit: + for parent in cl.parentrevs(visit.pop(0))[:cut]: + if parent not in seen: + visit.append(parent) + seen.add(parent) + yield parent + +def _revdescendants(repo, revs, followfirst): + """Like revlog.descendants() but supports followfirst.""" + cut = followfirst and 1 or None + cl = repo.changelog + first = min(revs) + if first == nodemod.nullrev: + # Are there nodes with a null first parent and a non-null + # second one? Maybe. Do we care? Probably not. + for i in cl: + yield i + return + + seen = set(revs) + for i in xrange(first + 1, len(cl)): + for x in cl.parentrevs(i)[:cut]: + if x != nodemod.nullrev and x in seen: + seen.add(i) + yield i + break + elements = { "(": (20, ("group", 1, ")"), ("func", 1, ")")), "~": (18, None, ("ancestor", 18)), @@ -203,15 +236,23 @@ def ancestor(repo, subset, x): return [r for r in an if r in subset] +def _ancestors(repo, subset, x, followfirst=False): + args = getset(repo, range(len(repo)), x) + if not args: + return [] + s = set(_revancestors(repo, args, followfirst)) | set(args) + return [r for r in subset if r in s] + def ancestors(repo, subset, x): """``ancestors(set)`` Changesets that are ancestors of a changeset in set. """ - args = getset(repo, range(len(repo)), x) - if not args: - return [] - s = set(repo.changelog.ancestors(*args)) | set(args) - return [r for r in subset if r in s] + return _ancestors(repo, subset, x) + +def _firstancestors(repo, subset, x): + # ``_firstancestors(set)`` + # Like ``ancestors(set)`` but follows only the first parents. + return _ancestors(repo, subset, x, followfirst=True) def ancestorspec(repo, subset, x, n): """``set~n`` @@ -395,15 +436,23 @@ def desc(repo, subset, x): l.append(r) return l +def _descendants(repo, subset, x, followfirst=False): + args = getset(repo, range(len(repo)), x) + if not args: + return [] + s = set(_revdescendants(repo, args, followfirst)) | set(args) + return [r for r in subset if r in s] + def descendants(repo, subset, x): """``descendants(set)`` Changesets which are descendants of changesets in set. """ - args = getset(repo, range(len(repo)), x) - if not args: - return [] - s = set(repo.changelog.descendants(*args)) | set(args) - return [r for r in subset if r in s] + return _descendants(repo, subset, x) + +def _firstdescendants(repo, subset, x): + # ``_firstdescendants(set)`` + # Like ``descendants(set)`` but follows only the first parents. + return _descendants(repo, subset, x, followfirst=True) def draft(repo, subset, x): """``draft()`` @@ -454,16 +503,7 @@ def _follow(repo, subset, x, name, follo 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()) + s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()]) return [r for r in subset if r in s] @@ -1055,6 +1095,7 @@ symbols = { "all": getall, "ancestor": ancestor, "ancestors": ancestors, + "_firstancestors": _firstancestors, "author": author, "bisect": bisect, "bisected": bisected, @@ -1066,6 +1107,7 @@ symbols = { "date": date, "desc": desc, "descendants": descendants, + "_firstdescendants": _firstdescendants, "draft": draft, "file": hasfile, "filelog": filelog, diff --git a/tests/test-glog.t b/tests/test-glog.t --- a/tests/test-glog.t +++ b/tests/test-glog.t @@ -1746,8 +1746,8 @@ Test --follow-first [] (group (func - ('symbol', '_followfirst') - None)) + ('symbol', '_firstancestors') + ('symbol', '6'))) Cannot compare with log --follow-first FILE as it never worked @@ -1967,6 +1967,23 @@ Test --follow and forward --rev +nodetag 6 [1] +Test --follow-first and forward --rev + + $ testlog --follow-first -r6 -r8 -r5 -r7 -r4 + ['6', '8', '5', '7', '4'] + (group + (func + ('symbol', '_firstdescendants') + ('symbol', '6'))) + --- log.nodes * (glob) + +++ glog.nodes * (glob) + @@ -1,3 +1,3 @@ + -nodetag 6 + nodetag 8 + nodetag 7 + +nodetag 6 + [1] + Test --follow and backward --rev $ testlog --follow -r6 -r5 -r7 -r8 -r4 @@ -1976,3 +1993,11 @@ Test --follow and backward --rev ('symbol', 'ancestors') ('symbol', '6'))) +Test --follow-first and backward --rev + + $ testlog --follow-first -r6 -r5 -r7 -r8 -r4 + ['6', '5', '7', '8', '4'] + (group + (func + ('symbol', '_firstancestors') + ('symbol', '6')))