[PATCH 6 of 6] cat: add formatter support

Yuya Nishihara yuya at tcha.org
Tue May 30 12:07:14 EDT 2017


# HG changeset patch
# User Yuya Nishihara <yuya at tcha.org>
# Date 1495716824 -32400
#      Thu May 25 21:53:44 2017 +0900
# Node ID a20184dbfeadfa5c8da9a67fecafd972d7e2202d
# Parent  88dd1416922eaf34fd90e2e76a3d941f9a950ba3
cat: add formatter support

This is an example showing how formatter can handle the --output option.
git subrepo isn't supported for now.

diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -2632,21 +2632,21 @@ def remove(ui, repo, m, prefix, after, f
 
     return ret
 
-def cat(ui, repo, ctx, matcher, fntemplate, prefix, **opts):
+def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
     err = 1
 
     def write(path):
+        filename = None
         if fntemplate:
             filename = makefilename(repo, fntemplate, ctx.node(),
                                     pathname=os.path.join(prefix, path))
-            fp = open(filename, 'wb')
-        else:
-            fp = _unclosablefile(ui.fout)
-        with fp:
+        with formatter.maybereopen(basefm, filename, opts) as fm:
             data = ctx[path].data()
             if opts.get('decode'):
                 data = repo.wwritedata(path, data)
-            fp.write(data)
+            fm.startitem()
+            fm.write('data', '%s', data)
+            fm.data(abspath=path, path=matcher.rel(path))
 
     # Automation often uses hg cat on single files, so special case it
     # for performance to avoid the cost of parsing the manifest.
@@ -2670,7 +2670,7 @@ def cat(ui, repo, ctx, matcher, fntempla
         try:
             submatch = matchmod.subdirmatcher(subpath, matcher)
 
-            if not sub.cat(submatch, fntemplate,
+            if not sub.cat(submatch, basefm, fntemplate,
                            os.path.join(prefix, sub._path), **opts):
                 err = 0
         except error.RepoLookupError:
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -35,6 +35,7 @@ from . import (
     error,
     exchange,
     extensions,
+    formatter,
     graphmod,
     hbisect,
     help,
@@ -1338,7 +1339,7 @@ def bundle(ui, repo, fname, dest=None, *
      _('print output to file with formatted name'), _('FORMAT')),
     ('r', 'rev', '', _('print the given revision'), _('REV')),
     ('', 'decode', None, _('apply any matching decode filter')),
-    ] + walkopts,
+    ] + walkopts + formatteropts,
     _('[OPTION]... FILE...'),
     inferrepo=True)
 def cat(ui, repo, file1, *pats, **opts):
@@ -1368,9 +1369,13 @@ def cat(ui, repo, file1, *pats, **opts):
     if cmdutil.isstdiofilename(fntemplate):
         fntemplate = ''
 
-    if not fntemplate:
+    if fntemplate:
+        fm = formatter.nullformatter(ui, 'cat')
+    else:
         ui.pager('cat')
-    return cmdutil.cat(ui, repo, ctx, m, fntemplate, '', **opts)
+        fm = ui.formatter('cat', opts)
+    with fm:
+        return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, '', **opts)
 
 @command('^clone',
     [('U', 'noupdate', None, _('the clone will include an empty working '
diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py
--- a/mercurial/subrepo.py
+++ b/mercurial/subrepo.py
@@ -538,7 +538,7 @@ class abstractsubrepo(object):
         self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
         return 1
 
-    def cat(self, match, fntemplate, prefix, **opts):
+    def cat(self, match, fm, fntemplate, prefix, **opts):
         return 1
 
     def status(self, rev2, **opts):
@@ -767,11 +767,11 @@ class hgsubrepo(abstractsubrepo):
                                  dry_run, similarity)
 
     @annotatesubrepoerror
-    def cat(self, match, fntemplate, prefix, **opts):
+    def cat(self, match, fm, fntemplate, prefix, **opts):
         rev = self._state[1]
         ctx = self._repo[rev]
-        return cmdutil.cat(self.ui, self._repo, ctx, match, fntemplate, prefix,
-                           **opts)
+        return cmdutil.cat(self.ui, self._repo, ctx, match, fm, fntemplate,
+                           prefix, **opts)
 
     @annotatesubrepoerror
     def status(self, rev2, **opts):
@@ -1833,7 +1833,7 @@ class gitsubrepo(abstractsubrepo):
 
 
     @annotatesubrepoerror
-    def cat(self, match, fntemplate, prefix, **opts):
+    def cat(self, match, fm, fntemplate, prefix, **opts):
         rev = self._state[1]
         if match.anypats():
             return 1 #No support for include/exclude yet
@@ -1841,6 +1841,7 @@ class gitsubrepo(abstractsubrepo):
         if not match.files():
             return 1
 
+        # TODO: add support for non-plain formatter (see cmdutil.cat())
         for f in match.files():
             output = self._gitcommand(["show", "%s:%s" % (rev, f)])
             fp = cmdutil.makefileobj(self._subparent, fntemplate,
diff --git a/tests/test-cat.t b/tests/test-cat.t
--- a/tests/test-cat.t
+++ b/tests/test-cat.t
@@ -63,6 +63,46 @@ Test fileset
   tmp/h_45116003780e
   tmp/r_2
 
+Test template output
+
+  $ hg --cwd tmp cat ../b ../c -T '== {path} ({abspath}) ==\n{data}'
+  == ../b (b) == (glob)
+  1
+  == ../c (c) == (glob)
+  3
+
+  $ hg cat b c -Tjson --output -
+  [
+   {
+    "abspath": "b",
+    "data": "1\n",
+    "path": "b"
+   },
+   {
+    "abspath": "c",
+    "data": "3\n",
+    "path": "c"
+   }
+  ]
+
+  $ hg cat b c -Tjson --output 'tmp/%p.json'
+  $ cat tmp/b.json
+  [
+   {
+    "abspath": "b",
+    "data": "1\n",
+    "path": "b"
+   }
+  ]
+  $ cat tmp/c.json
+  [
+   {
+    "abspath": "c",
+    "data": "3\n",
+    "path": "c"
+   }
+  ]
+
 Test working directory
 
   $ echo b-wdir > b
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -241,7 +241,7 @@ Show all commands + options
   branch: force, clean
   branches: active, closed, template
   bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
-  cat: output, rev, decode, include, exclude
+  cat: output, rev, decode, include, exclude, template
   config: untrusted, edit, local, global, template
   copy: after, force, include, exclude, dry-run
   debugancestor: 
diff --git a/tests/test-hook.t b/tests/test-hook.t
--- a/tests/test-hook.t
+++ b/tests/test-hook.t
@@ -99,9 +99,9 @@ test generic hooks
   abort: pre-identify hook exited with status 1
   [255]
   $ hg cat b
-  pre-cat hook: HG_ARGS=cat b HG_HOOKNAME=pre-cat HG_HOOKTYPE=pre-cat HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b']
+  pre-cat hook: HG_ARGS=cat b HG_HOOKNAME=pre-cat HG_HOOKTYPE=pre-cat HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': '', 'template': ''} HG_PATS=['b']
   b
-  post-cat hook: HG_ARGS=cat b HG_HOOKNAME=post-cat HG_HOOKTYPE=post-cat HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] HG_RESULT=0
+  post-cat hook: HG_ARGS=cat b HG_HOOKNAME=post-cat HG_HOOKTYPE=post-cat HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': '', 'template': ''} HG_PATS=['b'] HG_RESULT=0
 
   $ cd ../b
   $ hg pull ../a
diff --git a/tests/test-subrepo.t b/tests/test-subrepo.t
--- a/tests/test-subrepo.t
+++ b/tests/test-subrepo.t
@@ -1020,6 +1020,14 @@ Prepare a repo with subrepo
   $ hg cat sub/repo/foo
   test
   test
+  $ hg cat sub/repo/foo -Tjson
+  [
+   {
+    "abspath": "foo",
+    "data": "test\ntest\n",
+    "path": "sub/repo/foo" (glob)
+   }
+  ]
   $ mkdir -p tmp/sub/repo
   $ hg cat -r 0 --output tmp/%p_p sub/repo/foo
   $ cat tmp/sub/repo/foo_p


More information about the Mercurial-devel mailing list