Patchwork D3304: formatter: add support for -Tcbor

login
register
mail settings
Submitter phabricator
Date April 13, 2018, 6:27 a.m.
Message ID <differential-rev-PHID-DREV-filbyhfh75nakqrwetla-req@phab.mercurial-scm.org>
Download mbox | patch
Permalink /patch/30847/
State New
Headers show

Comments

phabricator - April 13, 2018, 6:27 a.m.
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  The implementation in the formatter is hopefully pretty
  straightforward.
  
  Unlike other formats, we don't emit a list/array or need to
  handle "joining" elements between emitted items. That's because
  CBOR doesn't require any joining element: you just read the
  next byte to see what type it is then decode it. You do that for
  as long as there are bytes to read.
  
  As long as we aren't nesting formatters, the choice to omit
  an outer container for data seems acceptable. If nothing else,
  it will make it easier for decoders who don't support streaming
  to decode data without buffering the entire array first.
  
  I'm a bit unsure about add --cbor support to the f tool by importing
  Mercurial modules. It does work in the test environment. But there's
  no demand module importer, etc. Unlike the existing JSON and pickle
  formatters, there isn't an implementation in the standard library
  for us to use. So unless we want to forgo parsing the CBOR (this
  doesn't seem great because the data isn't human readable), we'll
  need to import at least the CBOR package from mercurial.*.
  
  This commit also doesn't implement -Tcbor for changeset printing.
  I'll have to figure that out...
  
  I added tests for -Tcbor for most of the instances of -Tjson in
  the test harness.
  
  .. feature::
  
    `--template/-T cbor` can now be specified to emit data as CBOR.
    
    (This feature is experimental and the data format may change.)

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D3304

AFFECTED FILES
  mercurial/formatter.py
  mercurial/help/scripting.txt
  tests/f
  tests/test-annotate.t
  tests/test-bookmarks.t
  tests/test-branches.t
  tests/test-cat.t
  tests/test-export.t
  tests/test-obsolete.t
  tests/test-paths.t
  tests/test-resolve.t
  tests/test-status.t
  tests/test-tools.t

CHANGE DETAILS




To: indygreg, #hg-reviewers
Cc: mercurial-devel

Patch

diff --git a/tests/test-tools.t b/tests/test-tools.t
--- a/tests/test-tools.t
+++ b/tests/test-tools.t
@@ -17,6 +17,7 @@ 
     -M, --md5             show md5 hash of the content
     -D, --dump            dump file content
     -H, --hexdump         hexdump file content
+    --cbor                parse as CBOR and dump parsed result
     -B BYTES, --bytes=BYTES
                           number of characters to dump
     -L LINES, --lines=LINES
diff --git a/tests/test-status.t b/tests/test-status.t
--- a/tests/test-status.t
+++ b/tests/test-status.t
@@ -259,6 +259,30 @@ 
    }
   ]
 
+  $ hg status -A -Tcbor | f --size --hex --cbor
+  size=206
+  0000: bf 44 70 61 74 68 45 61 64 64 65 64 46 73 74 61 |.DpathEaddedFsta|
+  0010: 74 75 73 41 41 ff bf 44 63 6f 70 79 48 6d 6f 64 |tusAA..DcopyHmod|
+  0020: 69 66 69 65 64 44 70 61 74 68 46 63 6f 70 69 65 |ifiedDpathFcopie|
+  0030: 64 46 73 74 61 74 75 73 41 41 ff bf 44 70 61 74 |dFstatusAA..Dpat|
+  0040: 68 47 72 65 6d 6f 76 65 64 46 73 74 61 74 75 73 |hGremovedFstatus|
+  0050: 41 52 ff bf 44 70 61 74 68 47 64 65 6c 65 74 65 |AR..DpathGdelete|
+  0060: 64 46 73 74 61 74 75 73 41 21 ff bf 44 70 61 74 |dFstatusA!..Dpat|
+  0070: 68 47 75 6e 6b 6e 6f 77 6e 46 73 74 61 74 75 73 |hGunknownFstatus|
+  0080: 41 3f ff bf 44 70 61 74 68 47 69 67 6e 6f 72 65 |A?..DpathGignore|
+  0090: 64 46 73 74 61 74 75 73 41 49 ff bf 44 70 61 74 |dFstatusAI..Dpat|
+  00a0: 68 49 2e 68 67 69 67 6e 6f 72 65 46 73 74 61 74 |hI.hgignoreFstat|
+  00b0: 75 73 41 43 ff bf 44 70 61 74 68 48 6d 6f 64 69 |usAC..DpathHmodi|
+  00c0: 66 69 65 64 46 73 74 61 74 75 73 41 43 ff       |fiedFstatusAC.|
+  {b'path': b'added', b'status': b'A'}
+  {b'copy': b'modified', b'path': b'copied', b'status': b'A'}
+  {b'path': b'removed', b'status': b'R'}
+  {b'path': b'deleted', b'status': b'!'}
+  {b'path': b'unknown', b'status': b'?'}
+  {b'path': b'ignored', b'status': b'I'}
+  {b'path': b'.hgignore', b'status': b'C'}
+  {b'path': b'modified', b'status': b'C'}
+
   $ hg status -A -Tpickle > pickle
   >>> from __future__ import print_function
   >>> import pickle
diff --git a/tests/test-resolve.t b/tests/test-resolve.t
--- a/tests/test-resolve.t
+++ b/tests/test-resolve.t
@@ -162,6 +162,14 @@ 
    }
   ]
 
+  $ hg resolve -l -Tcbor | f --size --hex --cbor
+  size=44
+  0000: bf 44 70 61 74 68 45 66 69 6c 65 31 46 73 74 61 |.DpathEfile1Fsta|
+  0010: 74 75 73 41 52 ff bf 44 70 61 74 68 45 66 69 6c |tusAR..DpathEfil|
+  0020: 65 32 46 73 74 61 74 75 73 41 55 ff             |e2FstatusAU.|
+  {b'path': b'file1', b'status': b'R'}
+  {b'path': b'file2', b'status': b'U'}
+
 resolve -m without paths should mark all resolved
 
   $ hg resolve -m
@@ -176,6 +184,8 @@ 
   [
   ]
 
+  $ hg resolve -l -Tcbor
+
 resolve --all should abort when no merge in progress
 
   $ hg resolve --all
diff --git a/tests/test-paths.t b/tests/test-paths.t
--- a/tests/test-paths.t
+++ b/tests/test-paths.t
@@ -14,6 +14,8 @@ 
   [
   ]
 
+  $ hg paths -Tcbor
+
 with paths:
 
   $ echo '[paths]' >> .hg/hgrc
@@ -84,6 +86,11 @@ 
   ]
   [1]
 
+  $ hg paths -Tcbor | f --cbor
+  
+  {b'name': b'dupe', b'pushurl': b'https://example.com/dupe', b'url': b'$TESTTMP/b#tip'}
+  {b'name': b'expand', b'url': b'$TESTTMP/a/$SOMETHING/bar'}
+
 log template:
 
  (behaves as a {name: path-string} dict by default)
diff --git a/tests/test-obsolete.t b/tests/test-obsolete.t
--- a/tests/test-obsolete.t
+++ b/tests/test-obsolete.t
@@ -773,6 +773,91 @@ 
    }
   ]
 
+  $ hg debugobsolete -Tcbor | f --size --hex --cbor
+  size=1173
+  0000: bf 44 64 61 74 65 82 f9 65 3b 00 44 66 6c 61 67 |.Ddate..e;.Dflag|
+  0010: 00 48 6d 65 74 61 64 61 74 61 a1 44 75 73 65 72 |.Hmetadata.Duser|
+  0020: 44 74 65 73 74 48 70 72 65 64 6e 6f 64 65 58 28 |DtestHprednodeX(|
+  0030: 31 33 33 39 31 33 33 39 31 33 33 39 31 33 33 39 |1339133913391339|
+  0040: 31 33 33 39 31 33 33 39 31 33 33 39 31 33 33 39 |1339133913391339|
+  0050: 31 33 33 39 31 33 33 39 49 73 75 63 63 6e 6f 64 |13391339Isuccnod|
+  0060: 65 73 81 58 28 63 61 38 31 39 31 38 30 65 64 62 |es.X(ca819180edb|
+  0070: 39 39 65 64 32 35 63 65 61 66 62 33 65 39 35 38 |99ed25ceafb3e958|
+  0080: 34 61 63 32 38 37 65 32 34 30 62 30 30 ff bf 44 |4ac287e240b00..D|
+  0090: 64 61 74 65 82 f9 65 3b 00 44 66 6c 61 67 00 48 |date..e;.Dflag.H|
+  00a0: 6d 65 74 61 64 61 74 61 a1 44 75 73 65 72 44 74 |metadata.DuserDt|
+  00b0: 65 73 74 48 70 72 65 64 6e 6f 64 65 58 28 31 33 |estHprednodeX(13|
+  00c0: 33 37 31 33 33 37 31 33 33 37 31 33 33 37 31 33 |3713371337133713|
+  00d0: 33 37 31 33 33 37 31 33 33 37 31 33 33 37 31 33 |3713371337133713|
+  00e0: 33 37 31 33 33 37 49 73 75 63 63 6e 6f 64 65 73 |371337Isuccnodes|
+  00f0: 81 58 28 35 36 30 31 66 62 39 33 61 33 35 30 37 |.X(5601fb93a3507|
+  0100: 33 34 64 39 33 35 31 39 35 66 65 65 33 37 66 34 |34d935195fee37f4|
+  0110: 30 35 34 63 35 32 39 66 66 33 39 ff bf 44 64 61 |054c529ff39..Dda|
+  0120: 74 65 82 f9 57 90 18 78 44 66 6c 61 67 0c 48 6d |te..W..xDflag.Hm|
+  0130: 65 74 61 64 61 74 61 a1 44 75 73 65 72 44 74 65 |etadata.DuserDte|
+  0140: 73 74 48 70 72 65 64 6e 6f 64 65 58 28 32 34 35 |stHprednodeX(245|
+  0150: 62 64 65 34 32 37 30 63 64 31 30 37 32 61 32 37 |bde4270cd1072a27|
+  0160: 37 35 37 39 38 34 66 39 63 64 61 38 62 61 32 36 |757984f9cda8ba26|
+  0170: 66 30 38 63 61 49 73 75 63 63 6e 6f 64 65 73 81 |f08caIsuccnodes.|
+  0180: 58 28 63 64 62 63 65 32 66 62 62 31 36 33 31 33 |X(cdbce2fbb16313|
+  0190: 39 32 38 38 35 31 65 39 37 65 30 64 38 35 34 31 |928851e97e0d8541|
+  01a0: 33 66 33 66 37 65 62 37 37 66 ff bf 44 64 61 74 |3f3f7eb77f..Ddat|
+  01b0: 65 82 f9 65 3a 00 44 66 6c 61 67 01 48 6d 65 74 |e..e:.Dflag.Hmet|
+  01c0: 61 64 61 74 61 a1 44 75 73 65 72 44 74 65 73 74 |adata.DuserDtest|
+  01d0: 48 70 72 65 64 6e 6f 64 65 58 28 35 36 30 31 66 |HprednodeX(5601f|
+  01e0: 62 39 33 61 33 35 30 37 33 34 64 39 33 35 31 39 |b93a350734d93519|
+  01f0: 35 66 65 65 33 37 66 34 30 35 34 63 35 32 39 66 |5fee37f4054c529f|
+  0200: 66 33 39 49 73 75 63 63 6e 6f 64 65 73 81 58 28 |f39Isuccnodes.X(|
+  0210: 36 66 39 36 34 31 39 39 35 30 37 32 39 66 33 36 |6f96419950729f36|
+  0220: 37 31 31 38 35 62 38 34 37 33 35 32 38 39 30 66 |71185b847352890f|
+  0230: 30 37 34 66 37 35 35 37 ff bf 44 64 61 74 65 82 |074f7557..Ddate.|
+  0240: f9 65 3a 00 44 66 6c 61 67 00 48 6d 65 74 61 64 |.e:.Dflag.Hmetad|
+  0250: 61 74 61 a1 44 75 73 65 72 44 74 65 73 74 48 70 |ata.DuserDtestHp|
+  0260: 72 65 64 6e 6f 64 65 58 28 63 61 38 31 39 31 38 |rednodeX(ca81918|
+  0270: 30 65 64 62 39 39 65 64 32 35 63 65 61 66 62 33 |0edb99ed25ceafb3|
+  0280: 65 39 35 38 34 61 63 32 38 37 65 32 34 30 62 30 |e9584ac287e240b0|
+  0290: 30 49 73 75 63 63 6e 6f 64 65 73 81 58 28 31 33 |0Isuccnodes.X(13|
+  02a0: 33 37 31 33 33 37 31 33 33 37 31 33 33 37 31 33 |3713371337133713|
+  02b0: 33 37 31 33 33 37 31 33 33 37 31 33 33 37 31 33 |3713371337133713|
+  02c0: 33 37 31 33 33 37 ff bf 44 64 61 74 65 82 f9 65 |371337..Ddate..e|
+  02d0: 39 00 44 66 6c 61 67 00 48 6d 65 74 61 64 61 74 |9.Dflag.Hmetadat|
+  02e0: 61 a1 44 75 73 65 72 44 74 65 73 74 48 70 72 65 |a.DuserDtestHpre|
+  02f0: 64 6e 6f 64 65 58 28 63 64 62 63 65 32 66 62 62 |dnodeX(cdbce2fbb|
+  0300: 31 36 33 31 33 39 32 38 38 35 31 65 39 37 65 30 |16313928851e97e0|
+  0310: 64 38 35 34 31 33 66 33 66 37 65 62 37 37 66 49 |d85413f3f7eb77fI|
+  0320: 73 75 63 63 6e 6f 64 65 73 81 58 28 63 61 38 31 |succnodes.X(ca81|
+  0330: 39 31 38 30 65 64 62 39 39 65 64 32 35 63 65 61 |9180edb99ed25cea|
+  0340: 66 62 33 65 39 35 38 34 61 63 32 38 37 65 32 34 |fb3e9584ac287e24|
+  0350: 30 62 30 30 ff bf 44 64 61 74 65 82 fa 00 00 00 |0b00..Ddate.....|
+  0360: 00 00 44 66 6c 61 67 00 48 6d 65 74 61 64 61 74 |..Dflag.Hmetadat|
+  0370: 61 a1 44 75 73 65 72 44 74 65 73 74 4b 70 61 72 |a.DuserDtestKpar|
+  0380: 65 6e 74 6e 6f 64 65 73 81 58 28 36 66 39 36 34 |entnodes.X(6f964|
+  0390: 31 39 39 35 30 37 32 39 66 33 36 37 31 31 38 35 |19950729f3671185|
+  03a0: 62 38 34 37 33 35 32 38 39 30 66 30 37 34 66 37 |b847352890f074f7|
+  03b0: 35 35 37 48 70 72 65 64 6e 6f 64 65 58 28 39 34 |557HprednodeX(94|
+  03c0: 62 33 33 34 35 33 66 39 33 62 64 62 38 64 34 35 |b33453f93bdb8d45|
+  03d0: 37 65 66 39 62 37 37 30 38 35 31 61 36 31 38 62 |7ef9b770851a618b|
+  03e0: 66 34 31 33 65 31 49 73 75 63 63 6e 6f 64 65 73 |f413e1Isuccnodes|
+  03f0: 80 ff bf 44 64 61 74 65 82 fa 00 00 00 00 00 44 |...Ddate.......D|
+  0400: 66 6c 61 67 00 48 6d 65 74 61 64 61 74 61 a1 44 |flag.Hmetadata.D|
+  0410: 75 73 65 72 57 74 65 73 74 20 3c 74 65 73 74 40 |userWtest <test@|
+  0420: 65 78 61 6d 70 6c 65 2e 6e 65 74 3e 48 70 72 65 |example.net>Hpre|
+  0430: 64 6e 6f 64 65 58 28 63 64 61 36 34 38 63 61 35 |dnodeX(cda648ca5|
+  0440: 30 66 35 30 34 38 32 62 37 30 35 35 63 30 62 30 |0f50482b7055c0b0|
+  0450: 63 34 63 31 31 37 62 62 61 36 37 33 33 64 39 49 |c4c117bba6733d9I|
+  0460: 73 75 63 63 6e 6f 64 65 73 81 58 28 33 64 65 35 |succnodes.X(3de5|
+  0470: 65 63 61 38 38 63 30 30 61 61 30 33 39 64 61 37 |eca88c00aa039da7|
+  0480: 33 39 39 61 32 32 30 66 34 61 35 32 32 31 66 61 |399a220f4a5221fa|
+  0490: 61 35 38 35 ff                                  |a585.|
+  {b'date': [1339.000000, 0], b'flag': 0, b'metadata': {b'user': b'test'}, b'prednode': b'1339133913391339133913391339133913391339', b'succnodes': [b'ca819180edb99ed25ceafb3e9584ac287e240b00']}
+  {b'date': [1339.000000, 0], b'flag': 0, b'metadata': {b'user': b'test'}, b'prednode': b'1337133713371337133713371337133713371337', b'succnodes': [b'5601fb93a350734d935195fee37f4054c529ff39']}
+  {b'date': [121.000000, 120], b'flag': 12, b'metadata': {b'user': b'test'}, b'prednode': b'245bde4270cd1072a27757984f9cda8ba26f08ca', b'succnodes': [b'cdbce2fbb16313928851e97e0d85413f3f7eb77f']}
+  {b'date': [1338.000000, 0], b'flag': 1, b'metadata': {b'user': b'test'}, b'prednode': b'5601fb93a350734d935195fee37f4054c529ff39', b'succnodes': [b'6f96419950729f3671185b847352890f074f7557']}
+  {b'date': [1338.000000, 0], b'flag': 0, b'metadata': {b'user': b'test'}, b'prednode': b'ca819180edb99ed25ceafb3e9584ac287e240b00', b'succnodes': [b'1337133713371337133713371337133713371337']}
+  {b'date': [1337.000000, 0], b'flag': 0, b'metadata': {b'user': b'test'}, b'prednode': b'cdbce2fbb16313928851e97e0d85413f3f7eb77f', b'succnodes': [b'ca819180edb99ed25ceafb3e9584ac287e240b00']}
+  {b'date': [0.000000, 0], b'flag': 0, b'metadata': {b'user': b'test'}, b'parentnodes': [b'6f96419950729f3671185b847352890f074f7557'], b'prednode': b'94b33453f93bdb8d457ef9b770851a618bf413e1', b'succnodes': []}
+  {b'date': [0.000000, 0], b'flag': 0, b'metadata': {b'user': b'test <test@example.net>'}, b'prednode': b'cda648ca50f50482b7055c0b0c4c117bba6733d9', b'succnodes': [b'3de5eca88c00aa039da7399a220f4a5221faa585']}
+
 Template keywords
 
   $ hg debugobsolete -r6 -T '{succnodes % "{node|short}"} {date|shortdate}\n'
@@ -1563,6 +1648,33 @@ 
    }
   ]
 
+  $ hg debugobsolete --index --rev '3+7' -Tcbor | f --size --hex --cbor
+  size=346
+  0000: bf 44 64 61 74 65 82 fa 00 00 00 00 00 44 66 6c |.Ddate.......Dfl|
+  0010: 61 67 00 45 69 6e 64 65 78 01 48 6d 65 74 61 64 |ag.Eindex.Hmetad|
+  0020: 61 74 61 a3 43 65 66 31 41 31 44 75 73 65 72 44 |ata.Cef1A1DuserD|
+  0030: 74 65 73 74 49 6f 70 65 72 61 74 69 6f 6e 45 61 |testIoperationEa|
+  0040: 6d 65 6e 64 48 70 72 65 64 6e 6f 64 65 58 28 36 |mendHprednodeX(6|
+  0050: 66 64 65 66 36 30 66 63 62 61 62 62 64 33 64 35 |fdef60fcbabbd3d5|
+  0060: 30 65 39 62 39 63 62 63 32 61 32 34 30 37 32 34 |0e9b9cbc2a240724|
+  0070: 62 39 31 61 35 65 31 49 73 75 63 63 6e 6f 64 65 |b91a5e1Isuccnode|
+  0080: 73 81 58 28 64 32 37 66 62 39 62 30 36 36 30 37 |s.X(d27fb9b06607|
+  0090: 36 66 64 39 32 31 32 37 37 61 34 62 39 65 38 62 |6fd921277a4b9e8b|
+  00a0: 39 63 62 34 38 63 39 35 62 63 36 61 ff bf 44 64 |9cb48c95bc6a..Dd|
+  00b0: 61 74 65 82 fa 00 00 00 00 00 44 66 6c 61 67 00 |ate.......Dflag.|
+  00c0: 45 69 6e 64 65 78 03 48 6d 65 74 61 64 61 74 61 |Eindex.Hmetadata|
+  00d0: a3 43 65 66 31 41 31 44 75 73 65 72 44 74 65 73 |.Cef1A1DuserDtes|
+  00e0: 74 49 6f 70 65 72 61 74 69 6f 6e 45 61 6d 65 6e |tIoperationEamen|
+  00f0: 64 48 70 72 65 64 6e 6f 64 65 58 28 34 37 31 35 |dHprednodeX(4715|
+  0100: 63 66 37 36 37 34 34 30 65 64 38 39 31 37 35 35 |cf767440ed891755|
+  0110: 34 34 38 30 31 36 63 32 62 38 63 66 37 30 37 36 |448016c2b8cf7076|
+  0120: 30 63 33 30 49 73 75 63 63 6e 6f 64 65 73 81 58 |0c30Isuccnodes.X|
+  0130: 28 37 61 65 37 39 63 35 64 36 30 66 30 34 39 63 |(7ae79c5d60f049c|
+  0140: 37 62 30 64 64 30 32 66 35 66 32 35 62 39 64 36 |7b0dd02f5f25b9d6|
+  0150: 30 61 61 66 37 62 33 36 64 ff                   |0aaf7b36d.|
+  {b'date': [0.000000, 0], b'flag': 0, b'index': 1, b'metadata': {b'ef1': b'1', b'operation': b'amend', b'user': b'test'}, b'prednode': b'6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1', b'succnodes': [b'd27fb9b066076fd921277a4b9e8b9cb48c95bc6a']}
+  {b'date': [0.000000, 0], b'flag': 0, b'index': 3, b'metadata': {b'ef1': b'1', b'operation': b'amend', b'user': b'test'}, b'prednode': b'4715cf767440ed891755448016c2b8cf70760c30', b'succnodes': [b'7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d']}
+
 Test the --delete option of debugobsolete command
   $ hg debugobsolete --index
   0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b f9bd49731b0b175e42992a3c8fa6c678b2bc11f1 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'test'}
diff --git a/tests/test-export.t b/tests/test-export.t
--- a/tests/test-export.t
+++ b/tests/test-export.t
@@ -174,6 +174,27 @@ 
    }
   ]
 
+  $ hg export -Tcbor 0 | f --size --hex --cbor
+  size=263
+  0000: bf 46 62 72 61 6e 63 68 47 64 65 66 61 75 6c 74 |.FbranchGdefault|
+  0010: 44 64 61 74 65 82 fa 00 00 00 00 00 44 64 65 73 |Ddate.......Ddes|
+  0020: 63 45 66 6f 6f 2d 30 44 64 69 66 66 58 96 64 69 |cEfoo-0DdiffX.di|
+  0030: 66 66 20 2d 72 20 30 30 30 30 30 30 30 30 30 30 |ff -r 0000000000|
+  0040: 30 30 20 2d 72 20 38 37 31 35 35 38 64 65 36 61 |00 -r 871558de6a|
+  0050: 66 32 20 66 6f 6f 0a 2d 2d 2d 20 2f 64 65 76 2f |f2 foo.--- /dev/|
+  0060: 6e 75 6c 6c 09 54 68 75 20 4a 61 6e 20 30 31 20 |null.Thu Jan 01 |
+  0070: 30 30 3a 30 30 3a 30 30 20 31 39 37 30 20 2b 30 |00:00:00 1970 +0|
+  0080: 30 30 30 0a 2b 2b 2b 20 62 2f 66 6f 6f 09 54 68 |000.+++ b/foo.Th|
+  0090: 75 20 4a 61 6e 20 30 31 20 30 30 3a 30 30 3a 30 |u Jan 01 00:00:0|
+  00a0: 30 20 31 39 37 30 20 2b 30 30 30 30 0a 40 40 20 |0 1970 +0000.@@ |
+  00b0: 2d 30 2c 30 20 2b 31 2c 31 20 40 40 0a 2b 66 6f |-0,0 +1,1 @@.+fo|
+  00c0: 6f 2d 30 0a 44 6e 6f 64 65 58 28 38 37 31 35 35 |o-0.DnodeX(87155|
+  00d0: 38 64 65 36 61 66 32 65 38 63 32 34 34 32 32 32 |8de6af2e8c244222|
+  00e0: 66 38 65 65 61 36 39 62 37 38 32 63 39 34 63 65 |f8eea69b782c94ce|
+  00f0: 33 64 66 47 70 61 72 65 6e 74 73 80 44 75 73 65 |3dfGparents.Duse|
+  0100: 72 44 74 65 73 74 ff                            |rDtest.|
+  {b'branch': b'default', b'date': [0.000000, 0], b'desc': b'foo-0', b'diff': b'diff -r 000000000000 -r 871558de6af2 foo\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/foo\tThu Jan 01 00:00:00 1970 +0000\n@@ -0,0 +1,1 @@\n+foo-0\n', b'node': b'871558de6af2e8c244222f8eea69b782c94ce3df', b'parents': [], b'user': b'test'}
+
 Templated output to single file:
 
   $ hg export -Tjson 0:1 -o out.json
@@ -199,6 +220,48 @@ 
    }
   ]
 
+  $ hg export -Tcbor 0:1 -o out.cbor
+  $ f --size --hex --cbor out.cbor
+  out.cbor: size=571
+  0000: bf 46 62 72 61 6e 63 68 47 64 65 66 61 75 6c 74 |.FbranchGdefault|
+  0010: 44 64 61 74 65 82 fa 00 00 00 00 00 44 64 65 73 |Ddate.......Ddes|
+  0020: 63 45 66 6f 6f 2d 30 44 64 69 66 66 58 96 64 69 |cEfoo-0DdiffX.di|
+  0030: 66 66 20 2d 72 20 30 30 30 30 30 30 30 30 30 30 |ff -r 0000000000|
+  0040: 30 30 20 2d 72 20 38 37 31 35 35 38 64 65 36 61 |00 -r 871558de6a|
+  0050: 66 32 20 66 6f 6f 0a 2d 2d 2d 20 2f 64 65 76 2f |f2 foo.--- /dev/|
+  0060: 6e 75 6c 6c 09 54 68 75 20 4a 61 6e 20 30 31 20 |null.Thu Jan 01 |
+  0070: 30 30 3a 30 30 3a 30 30 20 31 39 37 30 20 2b 30 |00:00:00 1970 +0|
+  0080: 30 30 30 0a 2b 2b 2b 20 62 2f 66 6f 6f 09 54 68 |000.+++ b/foo.Th|
+  0090: 75 20 4a 61 6e 20 30 31 20 30 30 3a 30 30 3a 30 |u Jan 01 00:00:0|
+  00a0: 30 20 31 39 37 30 20 2b 30 30 30 30 0a 40 40 20 |0 1970 +0000.@@ |
+  00b0: 2d 30 2c 30 20 2b 31 2c 31 20 40 40 0a 2b 66 6f |-0,0 +1,1 @@.+fo|
+  00c0: 6f 2d 30 0a 44 6e 6f 64 65 58 28 38 37 31 35 35 |o-0.DnodeX(87155|
+  00d0: 38 64 65 36 61 66 32 65 38 63 32 34 34 32 32 32 |8de6af2e8c244222|
+  00e0: 66 38 65 65 61 36 39 62 37 38 32 63 39 34 63 65 |f8eea69b782c94ce|
+  00f0: 33 64 66 47 70 61 72 65 6e 74 73 80 44 75 73 65 |3dfGparents.Duse|
+  0100: 72 44 74 65 73 74 ff bf 46 62 72 61 6e 63 68 47 |rDtest..FbranchG|
+  0110: 64 65 66 61 75 6c 74 44 64 61 74 65 82 fa 00 00 |defaultDdate....|
+  0120: 00 00 00 44 64 65 73 63 45 66 6f 6f 2d 31 44 64 |...DdescEfoo-1Dd|
+  0130: 69 66 66 58 99 64 69 66 66 20 2d 72 20 38 37 31 |iffX.diff -r 871|
+  0140: 35 35 38 64 65 36 61 66 32 20 2d 72 20 64 31 63 |558de6af2 -r d1c|
+  0150: 39 36 35 36 65 39 37 33 63 20 66 6f 6f 0a 2d 2d |9656e973c foo.--|
+  0160: 2d 20 61 2f 66 6f 6f 09 54 68 75 20 4a 61 6e 20 |- a/foo.Thu Jan |
+  0170: 30 31 20 30 30 3a 30 30 3a 30 30 20 31 39 37 30 |01 00:00:00 1970|
+  0180: 20 2b 30 30 30 30 0a 2b 2b 2b 20 62 2f 66 6f 6f | +0000.+++ b/foo|
+  0190: 09 54 68 75 20 4a 61 6e 20 30 31 20 30 30 3a 30 |.Thu Jan 01 00:0|
+  01a0: 30 3a 30 30 20 31 39 37 30 20 2b 30 30 30 30 0a |0:00 1970 +0000.|
+  01b0: 40 40 20 2d 31 2c 31 20 2b 31 2c 32 20 40 40 0a |@@ -1,1 +1,2 @@.|
+  01c0: 20 66 6f 6f 2d 30 0a 2b 66 6f 6f 2d 31 0a 44 6e | foo-0.+foo-1.Dn|
+  01d0: 6f 64 65 58 28 64 31 63 39 36 35 36 65 39 37 33 |odeX(d1c9656e973|
+  01e0: 63 66 62 35 61 65 62 64 35 34 39 39 62 62 64 32 |cfb5aebd5499bbd2|
+  01f0: 63 62 33 35 30 65 33 62 31 32 32 36 36 47 70 61 |cb350e3b12266Gpa|
+  0200: 72 65 6e 74 73 81 58 28 38 37 31 35 35 38 64 65 |rents.X(871558de|
+  0210: 36 61 66 32 65 38 63 32 34 34 32 32 32 66 38 65 |6af2e8c244222f8e|
+  0220: 65 61 36 39 62 37 38 32 63 39 34 63 65 33 64 66 |ea69b782c94ce3df|
+  0230: 44 75 73 65 72 44 74 65 73 74 ff                |DuserDtest.|
+  {b'branch': b'default', b'date': [0.000000, 0], b'desc': b'foo-0', b'diff': b'diff -r 000000000000 -r 871558de6af2 foo\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/foo\tThu Jan 01 00:00:00 1970 +0000\n@@ -0,0 +1,1 @@\n+foo-0\n', b'node': b'871558de6af2e8c244222f8eea69b782c94ce3df', b'parents': [], b'user': b'test'}
+  {b'branch': b'default', b'date': [0.000000, 0], b'desc': b'foo-1', b'diff': b'diff -r 871558de6af2 -r d1c9656e973c foo\n--- a/foo\tThu Jan 01 00:00:00 1970 +0000\n+++ b/foo\tThu Jan 01 00:00:00 1970 +0000\n@@ -1,1 +1,2 @@\n foo-0\n+foo-1\n', b'node': b'd1c9656e973cfb5aebd5499bbd2cb350e3b12266', b'parents': [b'871558de6af2e8c244222f8eea69b782c94ce3df'], b'user': b'test'}
+
 Templated output to multiple files:
 
   $ hg export -Tjson 0:1 -o 'out-{rev}.json'
@@ -227,6 +290,51 @@ 
    }
   ]
 
+  $ hg export -Tcbor 0:1 -o 'out-{rev}.cbor'
+  $ f --size --hex --cbor out-0.cbor
+  out-0.cbor: size=263
+  0000: bf 46 62 72 61 6e 63 68 47 64 65 66 61 75 6c 74 |.FbranchGdefault|
+  0010: 44 64 61 74 65 82 fa 00 00 00 00 00 44 64 65 73 |Ddate.......Ddes|
+  0020: 63 45 66 6f 6f 2d 30 44 64 69 66 66 58 96 64 69 |cEfoo-0DdiffX.di|
+  0030: 66 66 20 2d 72 20 30 30 30 30 30 30 30 30 30 30 |ff -r 0000000000|
+  0040: 30 30 20 2d 72 20 38 37 31 35 35 38 64 65 36 61 |00 -r 871558de6a|
+  0050: 66 32 20 66 6f 6f 0a 2d 2d 2d 20 2f 64 65 76 2f |f2 foo.--- /dev/|
+  0060: 6e 75 6c 6c 09 54 68 75 20 4a 61 6e 20 30 31 20 |null.Thu Jan 01 |
+  0070: 30 30 3a 30 30 3a 30 30 20 31 39 37 30 20 2b 30 |00:00:00 1970 +0|
+  0080: 30 30 30 0a 2b 2b 2b 20 62 2f 66 6f 6f 09 54 68 |000.+++ b/foo.Th|
+  0090: 75 20 4a 61 6e 20 30 31 20 30 30 3a 30 30 3a 30 |u Jan 01 00:00:0|
+  00a0: 30 20 31 39 37 30 20 2b 30 30 30 30 0a 40 40 20 |0 1970 +0000.@@ |
+  00b0: 2d 30 2c 30 20 2b 31 2c 31 20 40 40 0a 2b 66 6f |-0,0 +1,1 @@.+fo|
+  00c0: 6f 2d 30 0a 44 6e 6f 64 65 58 28 38 37 31 35 35 |o-0.DnodeX(87155|
+  00d0: 38 64 65 36 61 66 32 65 38 63 32 34 34 32 32 32 |8de6af2e8c244222|
+  00e0: 66 38 65 65 61 36 39 62 37 38 32 63 39 34 63 65 |f8eea69b782c94ce|
+  00f0: 33 64 66 47 70 61 72 65 6e 74 73 80 44 75 73 65 |3dfGparents.Duse|
+  0100: 72 44 74 65 73 74 ff                            |rDtest.|
+  {b'branch': b'default', b'date': [0.000000, 0], b'desc': b'foo-0', b'diff': b'diff -r 000000000000 -r 871558de6af2 foo\n--- /dev/null\tThu Jan 01 00:00:00 1970 +0000\n+++ b/foo\tThu Jan 01 00:00:00 1970 +0000\n@@ -0,0 +1,1 @@\n+foo-0\n', b'node': b'871558de6af2e8c244222f8eea69b782c94ce3df', b'parents': [], b'user': b'test'}
+  $ f --size --hex --cbor out-1.cbor
+  out-1.cbor: size=308
+  0000: bf 46 62 72 61 6e 63 68 47 64 65 66 61 75 6c 74 |.FbranchGdefault|
+  0010: 44 64 61 74 65 82 fa 00 00 00 00 00 44 64 65 73 |Ddate.......Ddes|
+  0020: 63 45 66 6f 6f 2d 31 44 64 69 66 66 58 99 64 69 |cEfoo-1DdiffX.di|
+  0030: 66 66 20 2d 72 20 38 37 31 35 35 38 64 65 36 61 |ff -r 871558de6a|
+  0040: 66 32 20 2d 72 20 64 31 63 39 36 35 36 65 39 37 |f2 -r d1c9656e97|
+  0050: 33 63 20 66 6f 6f 0a 2d 2d 2d 20 61 2f 66 6f 6f |3c foo.--- a/foo|
+  0060: 09 54 68 75 20 4a 61 6e 20 30 31 20 30 30 3a 30 |.Thu Jan 01 00:0|
+  0070: 30 3a 30 30 20 31 39 37 30 20 2b 30 30 30 30 0a |0:00 1970 +0000.|
+  0080: 2b 2b 2b 20 62 2f 66 6f 6f 09 54 68 75 20 4a 61 |+++ b/foo.Thu Ja|
+  0090: 6e 20 30 31 20 30 30 3a 30 30 3a 30 30 20 31 39 |n 01 00:00:00 19|
+  00a0: 37 30 20 2b 30 30 30 30 0a 40 40 20 2d 31 2c 31 |70 +0000.@@ -1,1|
+  00b0: 20 2b 31 2c 32 20 40 40 0a 20 66 6f 6f 2d 30 0a | +1,2 @@. foo-0.|
+  00c0: 2b 66 6f 6f 2d 31 0a 44 6e 6f 64 65 58 28 64 31 |+foo-1.DnodeX(d1|
+  00d0: 63 39 36 35 36 65 39 37 33 63 66 62 35 61 65 62 |c9656e973cfb5aeb|
+  00e0: 64 35 34 39 39 62 62 64 32 63 62 33 35 30 65 33 |d5499bbd2cb350e3|
+  00f0: 62 31 32 32 36 36 47 70 61 72 65 6e 74 73 81 58 |b12266Gparents.X|
+  0100: 28 38 37 31 35 35 38 64 65 36 61 66 32 65 38 63 |(871558de6af2e8c|
+  0110: 32 34 34 32 32 32 66 38 65 65 61 36 39 62 37 38 |244222f8eea69b78|
+  0120: 32 63 39 34 63 65 33 64 66 44 75 73 65 72 44 74 |2c94ce3dfDuserDt|
+  0130: 65 73 74 ff                                     |est.|
+  {b'branch': b'default', b'date': [0.000000, 0], b'desc': b'foo-1', b'diff': b'diff -r 871558de6af2 -r d1c9656e973c foo\n--- a/foo\tThu Jan 01 00:00:00 1970 +0000\n+++ b/foo\tThu Jan 01 00:00:00 1970 +0000\n@@ -1,1 +1,2 @@\n foo-0\n+foo-1\n', b'node': b'd1c9656e973cfb5aebd5499bbd2cb350e3b12266', b'parents': [b'871558de6af2e8c244222f8eea69b782c94ce3df'], b'user': b'test'}
+
 Template keywrods:
 
   $ hg export 0 -T '# {node|shortest}\n\n{diff}'
diff --git a/tests/test-cat.t b/tests/test-cat.t
--- a/tests/test-cat.t
+++ b/tests/test-cat.t
@@ -103,6 +103,27 @@ 
    }
   ]
 
+  $ hg cat b c -Tcbor --output - | f --size --hex --cbor
+  size=54
+  0000: bf 47 61 62 73 70 61 74 68 41 62 44 64 61 74 61 |.GabspathAbDdata|
+  0010: 42 31 0a 44 70 61 74 68 41 62 ff bf 47 61 62 73 |B1.DpathAb..Gabs|
+  0020: 70 61 74 68 41 63 44 64 61 74 61 42 33 0a 44 70 |pathAcDdataB3.Dp|
+  0030: 61 74 68 41 63 ff                               |athAc.|
+  {b'abspath': b'b', b'data': b'1\n', b'path': b'b'}
+  {b'abspath': b'c', b'data': b'3\n', b'path': b'c'}
+
+  $ hg cat b c -Tcbor --output 'tmp/%p.cbor'
+  $ f --size --hex --cbor tmp/b.cbor
+  tmp/b.cbor: size=27
+  0000: bf 47 61 62 73 70 61 74 68 41 62 44 64 61 74 61 |.GabspathAbDdata|
+  0010: 42 31 0a 44 70 61 74 68 41 62 ff                |B1.DpathAb.|
+  {b'abspath': b'b', b'data': b'1\n', b'path': b'b'}
+  $ f --size --hex --cbor tmp/c.cbor
+  tmp/c.cbor: size=27
+  0000: bf 47 61 62 73 70 61 74 68 41 63 44 64 61 74 61 |.GabspathAcDdata|
+  0010: 42 33 0a 44 70 61 74 68 41 63 ff                |B3.DpathAc.|
+  {b'abspath': b'c', b'data': b'3\n', b'path': b'c'}
+
 Test working directory
 
   $ echo b-wdir > b
diff --git a/tests/test-branches.t b/tests/test-branches.t
--- a/tests/test-branches.t
+++ b/tests/test-branches.t
@@ -666,6 +666,53 @@ 
    }
   ]
 
+  $ hg branches -Tcbor | f --size --hex --cbor
+  size=608
+  0000: bf 46 61 63 74 69 76 65 f5 46 62 72 61 6e 63 68 |.Factive.Fbranch|
+  0010: 42 6d 64 46 63 6c 6f 73 65 64 f4 47 63 75 72 72 |BmdFclosed.Gcurr|
+  0020: 65 6e 74 f4 44 6e 6f 64 65 58 28 63 39 31 34 63 |ent.DnodeX(c914c|
+  0030: 39 39 66 31 66 62 62 32 62 31 64 37 38 35 61 30 |99f1fbb2b1d785a0|
+  0040: 61 39 33 39 65 64 33 66 36 37 32 37 35 64 66 31 |a939ed3f67275df1|
+  0050: 38 65 39 43 72 65 76 12 ff bf 46 61 63 74 69 76 |8e9Crev...Factiv|
+  0060: 65 f5 46 62 72 61 6e 63 68 41 62 46 63 6c 6f 73 |e.FbranchAbFclos|
+  0070: 65 64 f4 47 63 75 72 72 65 6e 74 f5 44 6e 6f 64 |ed.Gcurrent.Dnod|
+  0080: 65 58 28 65 32 33 62 35 35 30 35 64 31 61 64 32 |eX(e23b5505d1ad2|
+  0090: 34 61 61 62 36 66 38 34 66 64 38 63 37 63 62 38 |4aab6f84fd8c7cb8|
+  00a0: 63 64 38 65 35 65 39 33 62 65 30 43 72 65 76 0d |cd8e5e93be0Crev.|
+  00b0: ff bf 46 61 63 74 69 76 65 f5 46 62 72 61 6e 63 |..Factive.Fbranc|
+  00c0: 68 58 49 61 20 62 72 61 6e 63 68 20 6e 61 6d 65 |hXIa branch name|
+  00d0: 20 6d 75 63 68 20 6c 6f 6e 67 65 72 20 74 68 61 | much longer tha|
+  00e0: 6e 20 74 68 65 20 64 65 66 61 75 6c 74 20 6a 75 |n the default ju|
+  00f0: 73 74 69 66 69 63 61 74 69 6f 6e 20 75 73 65 64 |stification used|
+  0100: 20 62 79 20 62 72 61 6e 63 68 65 73 46 63 6c 6f | by branchesFclo|
+  0110: 73 65 64 f4 47 63 75 72 72 65 6e 74 f4 44 6e 6f |sed.Gcurrent.Dno|
+  0120: 64 65 58 28 31 30 66 66 35 38 39 35 61 61 35 37 |deX(10ff5895aa57|
+  0130: 39 33 62 64 33 37 38 64 61 35 37 34 61 66 38 63 |93bd378da574af8c|
+  0140: 65 63 38 65 61 34 30 38 64 38 33 31 43 72 65 76 |ec8ea408d831Crev|
+  0150: 07 ff bf 46 61 63 74 69 76 65 f4 46 62 72 61 6e |...Factive.Fbran|
+  0160: 63 68 41 6d 46 63 6c 6f 73 65 64 f4 47 63 75 72 |chAmFclosed.Gcur|
+  0170: 72 65 6e 74 f4 44 6e 6f 64 65 58 28 64 66 33 34 |rent.DnodeX(df34|
+  0180: 33 62 30 64 66 30 34 66 65 62 32 61 39 34 36 63 |3b0df04feb2a946c|
+  0190: 64 34 62 36 65 39 35 32 30 65 35 35 32 66 65 66 |d4b6e9520e552fef|
+  01a0: 31 34 65 65 43 72 65 76 11 ff bf 46 61 63 74 69 |14eeCrev...Facti|
+  01b0: 76 65 f4 46 62 72 61 6e 63 68 41 61 46 63 6c 6f |ve.FbranchAaFclo|
+  01c0: 73 65 64 f4 47 63 75 72 72 65 6e 74 f4 44 6e 6f |sed.Gcurrent.Dno|
+  01d0: 64 65 58 28 64 38 63 62 63 36 31 64 62 61 61 36 |deX(d8cbc61dbaa6|
+  01e0: 64 63 38 31 37 31 37 35 64 31 65 33 30 31 65 65 |dc817175d1e301ee|
+  01f0: 63 62 38 36 33 66 32 38 30 38 33 32 43 72 65 76 |cb863f280832Crev|
+  0200: 05 ff bf 46 61 63 74 69 76 65 f4 46 62 72 61 6e |...Factive.Fbran|
+  0210: 63 68 47 64 65 66 61 75 6c 74 46 63 6c 6f 73 65 |chGdefaultFclose|
+  0220: 64 f4 47 63 75 72 72 65 6e 74 f4 44 6e 6f 64 65 |d.Gcurrent.Dnode|
+  0230: 58 28 31 39 37 30 39 63 35 61 34 65 37 35 62 66 |X(19709c5a4e75bf|
+  0240: 39 33 38 66 38 65 33 34 39 61 66 66 39 37 34 33 |938f8e349aff9743|
+  0250: 38 35 33 39 62 62 37 32 39 65 43 72 65 76 00 ff |8539bb729eCrev..|
+  {b'active': True, b'branch': b'md', b'closed': False, b'current': False, b'node': b'c914c99f1fbb2b1d785a0a939ed3f67275df18e9', b'rev': 18}
+  {b'active': True, b'branch': b'b', b'closed': False, b'current': True, b'node': b'e23b5505d1ad24aab6f84fd8c7cb8cd8e5e93be0', b'rev': 13}
+  {b'active': True, b'branch': b'a branch name much longer than the default justification used by branches', b'closed': False, b'current': False, b'node': b'10ff5895aa5793bd378da574af8cec8ea408d831', b'rev': 7}
+  {b'active': False, b'branch': b'm', b'closed': False, b'current': False, b'node': b'df343b0df04feb2a946cd4b6e9520e552fef14ee', b'rev': 17}
+  {b'active': False, b'branch': b'a', b'closed': False, b'current': False, b'node': b'd8cbc61dbaa6dc817175d1e301eecb863f280832', b'rev': 5}
+  {b'active': False, b'branch': b'default', b'closed': False, b'current': False, b'node': b'19709c5a4e75bf938f8e349aff97438539bb729e', b'rev': 0}
+
   $ hg branches --closed -T '{if(closed, "{branch}\n")}'
   c
 
diff --git a/tests/test-bookmarks.t b/tests/test-bookmarks.t
--- a/tests/test-bookmarks.t
+++ b/tests/test-bookmarks.t
@@ -96,6 +96,26 @@ 
    }
   ]
 
+  $ hg bookmarks -Tcbor | f --size --hex --cbor
+  size=220
+  0000: bf 46 61 63 74 69 76 65 f4 48 62 6f 6f 6b 6d 61 |.Factive.Hbookma|
+  0010: 72 6b 41 58 44 6e 6f 64 65 58 28 66 37 62 31 65 |rkAXDnodeX(f7b1e|
+  0020: 62 31 37 61 64 32 34 37 33 30 61 31 36 35 31 66 |b17ad24730a1651f|
+  0030: 63 63 64 34 36 63 34 33 38 32 36 64 31 62 62 63 |ccd46c43826d1bbc|
+  0040: 32 61 63 43 72 65 76 00 ff bf 46 61 63 74 69 76 |2acCrev...Factiv|
+  0050: 65 f5 48 62 6f 6f 6b 6d 61 72 6b 42 58 32 44 6e |e.HbookmarkBX2Dn|
+  0060: 6f 64 65 58 28 39 32 35 64 38 30 66 34 37 39 62 |odeX(925d80f479b|
+  0070: 62 30 32 36 62 30 66 62 33 64 65 62 32 37 35 30 |b026b0fb3deb2750|
+  0080: 33 37 38 30 62 31 33 66 37 34 31 32 33 43 72 65 |3780b13f74123Cre|
+  0090: 76 01 ff bf 46 61 63 74 69 76 65 f4 48 62 6f 6f |v...Factive.Hboo|
+  00a0: 6b 6d 61 72 6b 41 59 44 6e 6f 64 65 58 28 30 30 |kmarkAYDnodeX(00|
+  00b0: 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
+  00c0: 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
+  00d0: 30 30 30 30 30 30 43 72 65 76 20 ff             |000000Crev .|
+  {b'active': False, b'bookmark': b'X', b'node': b'f7b1eb17ad24730a1651fccd46c43826d1bbc2ac', b'rev': 0}
+  {b'active': True, b'bookmark': b'X2', b'node': b'925d80f479bb026b0fb3deb27503780b13f74123', b'rev': 1}
+  {b'active': False, b'bookmark': b'Y', b'node': b'0000000000000000000000000000000000000000', b'rev': -1}
+
 bookmarks revset
 
   $ hg log -r 'bookmark()'
diff --git a/tests/test-annotate.t b/tests/test-annotate.t
--- a/tests/test-annotate.t
+++ b/tests/test-annotate.t
@@ -71,6 +71,13 @@ 
    }
   ]
 
+  $ hg annotate -Tcbor a | f --size --hex --cbor
+  size=40
+  0000: bf 47 61 62 73 70 61 74 68 41 61 45 6c 69 6e 65 |.GabspathAaEline|
+  0010: 73 81 a2 43 72 65 76 00 44 6c 69 6e 65 42 61 0a |s..Crev.DlineBa.|
+  0020: 44 70 61 74 68 41 61 ff                         |DpathAa.|
+  {b'abspath': b'a', b'lines': [{b'line': b'a\n', b'rev': 0}], b'path': b'a'}
+
 log-like templating
 
   $ hg annotate -T'{lines % "{rev} {node|shortest}: {line}"}' a
diff --git a/tests/f b/tests/f
--- a/tests/f
+++ b/tests/f
@@ -28,11 +28,19 @@ 
 import binascii
 import glob
 import hashlib
+import io
 import optparse
 import os
 import re
 import sys
 
+from mercurial.thirdparty import (
+    cbor,
+)
+from mercurial.utils import (
+    stringutil,
+)
+
 # Python 3 adapters
 ispy3 = (sys.version_info[0] >= 3)
 if ispy3:
@@ -120,13 +128,23 @@ 
                         chunk = chunk[:opts.bytes]
                     else:
                         chunk = chunk[opts.bytes:]
+
             if opts.hexdump:
                 for i in range(0, len(chunk), 16):
                     s = chunk[i:i + 16]
                     outfile.write(b'%04x: %-47s |%s|\n' %
                                   (i, b' '.join(
                                       b'%02x' % ord(c) for c in iterbytes(s)),
                                    re.sub(b'[^ -~]', b'.', s)))
+
+            if opts.cbor:
+                b = io.BytesIO(content)
+                decoder = cbor.CBORDecoder(b)
+                while b.tell() + 1 < len(content):
+                    o = decoder.decode()
+                    outfile.write(stringutil.pprint(o))
+                    outfile.write(b'\n')
+
             if opts.dump:
                 if not quiet:
                     outfile.write(b'>>>\n')
@@ -136,6 +154,7 @@ 
                         outfile.write(b'<<<\n')
                     else:
                         outfile.write(b'\n<<< no trailing newline\n')
+
         if opts.recurse and dirfiles:
             assert not isstdin
             visit(opts, dirfiles, outfile)
@@ -164,6 +183,8 @@ 
                       help="dump file content")
     parser.add_option("-H", "--hexdump", action="store_true",
                       help="hexdump file content")
+    parser.add_option("", "--cbor", action="store_true",
+                      help="parse as CBOR and dump parsed result")
     parser.add_option("-B", "--bytes", type="int",
                       help="number of characters to dump")
     parser.add_option("-L", "--lines", type="int",
diff --git a/mercurial/help/scripting.txt b/mercurial/help/scripting.txt
--- a/mercurial/help/scripting.txt
+++ b/mercurial/help/scripting.txt
@@ -142,20 +142,23 @@ 
    using templates to make your life easier.
 
 The ``-T/--template`` argument allows specifying pre-defined styles.
-Mercurial ships with the machine-readable styles ``json`` and ``xml``,
-which provide JSON and XML output, respectively. These are useful for
-producing output that is machine readable as-is.
+Mercurial ships with the machine-readable styles ``cbor``, ``json``
+and ``xml``, which provide CBOR, JSON, and XML output, respectively.
+These are useful for producing output that is machine readable as-is.
 
 .. important::
 
-   The ``json`` and ``xml`` styles are considered experimental. While
-   they may be attractive to use for easily obtaining machine-readable
-   output, their behavior may change in subsequent versions.
+   The ``cbor``, ``json``, and ``xml`` styles are considered
+   experimental. While they may be attractive to use for easily
+   obtaining machine-readable output, their behavior may change in
+   subsequent versions.
 
    These styles may also exhibit unexpected results when dealing with
    certain encodings. Mercurial treats things like filenames as a
    series of bytes and normalizing certain byte sequences to JSON
-   or XML with certain encoding settings can lead to surprises.
+   or XML with certain encoding settings can lead to surprises. CBOR,
+   however, is binary safe. So it is generally the safest encoding
+   format for content preservation.
 
 Command Server Output
 ---------------------
diff --git a/mercurial/formatter.py b/mercurial/formatter.py
--- a/mercurial/formatter.py
+++ b/mercurial/formatter.py
@@ -118,6 +118,9 @@ 
     short,
 )
 
+from.thirdparty import (
+    cbor,
+)
 from . import (
     error,
     pycompat,
@@ -127,7 +130,10 @@ 
     templateutil,
     util,
 )
-from .utils import dateutil
+from .utils import (
+    cborutil,
+    dateutil,
+)
 
 pickle = util.pickle
 
@@ -351,6 +357,20 @@ 
         baseformatter.end(self)
         self._out.write("\n]\n")
 
+class cborformatter(baseformatter):
+    def __init__(self, ui, out, topic, opts):
+        super(cborformatter, self).__init__(ui, topic, opts, _nullconverter)
+        self._out = out
+        self._encoder = cbor.CBOREncoder(out, canonical=True)
+
+    def _showitem(self):
+        with cborutil.streammap(self._encoder) as fn:
+            for k, v in sorted(self._item.items()):
+                fn(k, v)
+
+    def end(self):
+        super(cborformatter, self).end()
+
 class _templateconverter(object):
     '''convert non-primitive data types to be processed by templater'''
 
@@ -568,6 +588,8 @@ 
     template = opts.get("template", "")
     if template == "json":
         return jsonformatter(ui, out, topic, opts)
+    elif template == "cbor":
+        return cborformatter(ui, out, topic, opts)
     elif template == "pickle":
         return pickleformatter(ui, out, topic, opts)
     elif template == "debug":