[PATCH 7 of 7] graphlog: apply file filters --patch/--stat output
Patrick Mezard
patrick at mezard.eu
Sun Feb 26 16:14:30 UTC 2012
# HG changeset patch
# User Patrick Mezard <patrick at mezard.eu>
# Date 1330272735 -3600
# Node ID bb30feac064f15a6665f2d1f7cd619f3de2f4b91
# Parent 0853c68ee6f36b5125fe2cfd877eb5236492e1cb
graphlog: apply file filters --patch/--stat output
When passing --patch/--stat, file filters have to be applied to generate the
correct diff or stat output:
- Without --follow, the static match object can be reused
- With --follow, the files displayed at revision X are the ancestors of
selected files at parent revision. To do this, we reproduce the ancestry
calculations done by --follow, lazily.
test-glog.t changes show that --patch output is not satisfying because renames
are reported as copies. This can probably be fixed by:
- Without --follow: compute files to display, look for renames sources and
extend the matcher to include them.
- With --follow: detect .path() transitions between parent/child filectx,
filter them using the linked changectx .removed() field and extend fcache
with them.
diff --git a/hgext/graphlog.py b/hgext/graphlog.py
--- a/hgext/graphlog.py
+++ b/hgext/graphlog.py
@@ -242,8 +242,40 @@
raise util.Abort(_("-G/--graph option is incompatible with --%s")
% op.replace("_", "-"))
+def makefilematcher(repo, pats, followfirst):
+ # When displaying a revision with --patch --follow FILE, we have
+ # to know which file of the revision must be diffed. With
+ # --follow, we want the names of the ancestors of FILE in the
+ # revision, stored in "fcache". "fache" is populated by
+ # reproducing the graph traversal already done by --follow revset
+ # and relating linkrevs to file names (which is not "correct" but
+ # good enough).
+ fcache = {}
+ fcacheready = [False]
+ pctx = repo['.']
+ wctx = repo[None]
+
+ def populate():
+ for fn in pats:
+ for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
+ for c in i:
+ fcache.setdefault(c.linkrev(), set()).add(c.path())
+
+ def filematcher(rev):
+ if not fcacheready[0]:
+ # Lazy initialization
+ fcacheready[0] = True
+ populate()
+ return scmutil.match(wctx, fcache.get(rev, []), default='path')
+
+ return filematcher
+
def revset(repo, pats, opts):
- """Return revset str built of revisions, log options and file patterns.
+ """Return (expr, filematcher) where expr is a revset string built
+ of revisions, log options and file patterns. If --stat or --patch
+ are not passed filematcher is None. Otherwise it a a callable
+ taking a revision number and returning a match objects filtering
+ the files to be detailed when displaying the revision.
"""
opt2revset = {
'follow': ('follow()', None),
@@ -329,6 +361,13 @@
else:
opts['_patslog'] = list(pats)
+ filematcher = None
+ if opts.get('patch') or opts.get('stat'):
+ if follow:
+ filematcher = makefilematcher(repo, pats, followfirst)
+ else:
+ filematcher = lambda rev: match
+
revset = []
for op, val in opts.iteritems():
if not val:
@@ -349,9 +388,10 @@
revset = '(' + ' and '.join(revset) + ')'
else:
revset = 'all()'
- return revset
+ return revset, filematcher
-def generate(ui, dag, displayer, showparents, edgefn, getrenamed=None):
+def generate(ui, dag, displayer, showparents, edgefn, getrenamed=None,
+ filematcher=None):
seen, state = [], asciistate()
for rev, type, ctx, parents in dag:
char = ctx.node() in showparents and '@' or 'o'
@@ -362,7 +402,10 @@
rename = getrenamed(fn, ctx.rev())
if rename:
copies.append((fn, rename[0]))
- displayer.show(ctx, copies=copies)
+ revmatchfn = None
+ if filematcher is not None:
+ revmatchfn = filematcher(ctx.rev())
+ displayer.show(ctx, copies=copies, matchfn=revmatchfn)
lines = displayer.hunk.pop(rev).split('\n')[:-1]
displayer.flush(rev)
edges = edgefn(type, char, lines, seen, rev, parents)
@@ -389,7 +432,8 @@
check_unsupported_flags(pats, opts)
- revs = sorted(scmutil.revrange(repo, [revset(repo, pats, opts)]), reverse=1)
+ expr, filematcher = revset(repo, pats, opts)
+ revs = sorted(scmutil.revrange(repo, [expr]), reverse=1)
limit = cmdutil.loglimit(opts)
if limit is not None:
revs = revs[:limit]
@@ -403,7 +447,8 @@
getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
displayer = show_changeset(ui, repo, opts, buffered=True)
showparents = [ctx.node() for ctx in repo[None].parents()]
- generate(ui, revdag, displayer, showparents, asciiedges, getrenamed)
+ generate(ui, revdag, displayer, showparents, asciiedges, getrenamed,
+ filematcher)
def graphrevs(repo, nodes, opts):
limit = cmdutil.loglimit(opts)
diff --git a/tests/test-glog.t b/tests/test-glog.t
--- a/tests/test-glog.t
+++ b/tests/test-glog.t
@@ -90,7 +90,7 @@
> def uisetup(ui):
> def printrevset(orig, ui, repo, *pats, **opts):
> if opts.get('print_revset'):
- > expr = graphlog.revset(repo, pats, opts)
+ > expr = graphlog.revset(repo, pats, opts)[0]
> tree = revset.parse(expr)[0]
> ui.write(tree, "\n")
> return 0
@@ -1655,3 +1655,99 @@
abort: can only follow copies/renames for explicit filenames
abort: can only follow copies/renames for explicit filenames
abort: can only follow copies/renames for explicit filenames
+
+Test --patch and --stat with --follow and --follow-first
+
+ $ hg up -q 3
+ $ hg log -G --git --patch b
+ o changeset: 1:216d4c92cf98
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: copy a b
+ |
+ | diff --git a/a b/b
+ | copy from a
+ | copy to b
+ |
+
+ $ hg log -G --git --stat b
+ o changeset: 1:216d4c92cf98
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: copy a b
+ |
+ | a | 0
+ | 1 files changed, 0 insertions(+), 0 deletions(-)
+ |
+
+ $ hg log -G --git --patch --follow b
+ o changeset: 1:216d4c92cf98
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: copy a b
+ |
+ | diff --git a/a b/b
+ | copy from a
+ | copy to b
+ |
+ o changeset: 0:f8035bb17114
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add a
+
+ diff --git a/a b/a
+ new file mode 100644
+ --- /dev/null
+ +++ b/a
+ @@ -0,0 +1,1 @@
+ +a
+
+
+ $ hg log -G --git --stat --follow b
+ o changeset: 1:216d4c92cf98
+ | user: test
+ | date: Thu Jan 01 00:00:00 1970 +0000
+ | summary: copy a b
+ |
+ | a | 0
+ | 1 files changed, 0 insertions(+), 0 deletions(-)
+ |
+ o changeset: 0:f8035bb17114
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: add a
+
+ a | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+
+
+ $ hg up -q 6
+ $ hg log -G --git --patch --follow-first e
+ @ changeset: 6:fc281d8ff18d
+ |\ tag: tip
+ | | parent: 5:99b31f1c2782
+ | | parent: 4:17d952250a9d
+ | | user: test
+ | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | summary: merge 5 and 4
+ | |
+ | | diff --git a/e b/e
+ | | --- a/e
+ | | +++ b/e
+ | | @@ -1,1 +1,1 @@
+ | | -ee
+ | | +merge
+ | |
+ o | changeset: 5:99b31f1c2782
+ | | parent: 3:5918b8d165d1
+ | | user: test
+ | | date: Thu Jan 01 00:00:00 1970 +0000
+ | | summary: add another e
+ | |
+ | | diff --git a/e b/e
+ | | new file mode 100644
+ | | --- /dev/null
+ | | +++ b/e
+ | | @@ -0,0 +1,1 @@
+ | | +ee
+ | |
More information about the Mercurial-devel
mailing list