diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py --- a/mercurial/cmdutil.py +++ b/mercurial/cmdutil.py @@ -19,7 +19,9 @@ nullrev, short, ) - +from .thirdparty import ( + cbor, +) from . import ( bookmarks, changelog, @@ -50,6 +52,7 @@ ) from .utils import ( + cborutil, dateutil, stringutil, ) @@ -1559,8 +1562,47 @@ for chunk, label in patch.diffui(repo, prev, node, match, opts=diffopts): yield chunk, label +def _exportsinglecbor(repo, ctx, match, switch_parent, seqno, diffopts, + encoder): + """Export a single patch to CBOR.""" + node = scmutil.binnode(ctx) + parents = [p.node() for p in ctx.parents() if p] + branch = ctx.branch() + if switch_parent: + parents.reverse() + + if parents: + prev = parents[0] + else: + prev = nullid + + with cborutil.streammap(encoder) as writeitem: + writeitem(b'version', 1) + writeitem(b'user', encoding.fromlocal(ctx.user())) + writeitem(b'time', ctx.date()[0]) + writeitem(b'timezone', ctx.date()[1]) + writeitem(b'branch', branch) + writeitem(b'node', node) + writeitem(b'parents', parents) + + extras = [] + for headerid in extraexport: + header = extraexportmap[headerid](seqno, ctx) + if header: + extras.append(header) + + if extras: + writeitem(b'extras', extras) + + writeitem(b'description', + encoding.fromlocal(ctx.description().rstrip())) + + encoder.encode(b'patchchunks') + chunks = patch.diff(repo, prev, node, match, opts=diffopts) + cborutil.streamarrayitems(encoder, chunks) + def export(repo, revs, fntemplate='hg-%h.patch', fp=None, switch_parent=False, - opts=None, match=None): + opts=None, match=None, fmt='hg'): '''export changesets as hg patches Args: @@ -1572,6 +1614,8 @@ Default is false, which always shows diff against p1. opts: diff options to use for generating the patch. match: If specified, only export changes to files matching this matcher. + fmt: Format to export the patch to. ``hg`` is the default Mercurial + patch format. ``cbor`` encodes to CBOR. Returns: Nothing. @@ -1613,17 +1657,35 @@ else: fp.write(s) + if fmt == 'cbor' and allfp: + allcbor = cbor.CBOREncoder(allfp) + cborutil.beginindefinitearray(allcbor) + else: + allcbor = None + for seqno, rev in enumerate(revs, 1): ctx = repo[rev] if not allfp: assert fntemplate - with makefileobj(ctx, fntemplate, mode='wb', modemap=filemode, total=total, seqno=seqno, revwidth=revwidth) as fp: - doexport(ctx, fp, fp.name) + if fmt == 'cbor': + encoder = cbor.CBOREncoder(fp) + with cborutil.streamarray(encoder): + _exportsinglecbor(repo, ctx, match, switch_parent, + seqno, opts, encoder) + else: + doexport(ctx, fp, fp.name) else: - doexport(ctx, allfp, dest) + if fmt == 'cbor': + _exportsinglecbor(repo, ctx, match, switch_parent, seqno, opts, + allcbor) + else: + doexport(ctx, allfp, dest) + + if allcbor: + cborutil.endindefinite(allcbor) def showmarker(fm, marker, index=None): """utility function to display obsolescence marker in a readable way diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -1893,6 +1893,7 @@ _('print output to file with formatted name'), _('FORMAT')), ('', 'switch-parent', None, _('diff against the second parent')), ('r', 'rev', [], _('revisions to export'), _('REV')), + ('', 'format', 'hg', _('format of written data (EXPERIMENTAL)')), ] + diffopts, _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'), cmdtype=readonly) def export(ui, repo, *changesets, **opts): @@ -1966,14 +1967,20 @@ revs = scmutil.revrange(repo, changesets) if not revs: raise error.Abort(_("export requires at least one changeset")) + + if opts['format'] not in ('hg', 'cbor'): + raise error.Abort(_('invalid value for --format: only "hg" and "cbor" ' + 'are supported')) + if len(revs) > 1: ui.note(_('exporting patches:\n')) else: ui.note(_('exporting patch:\n')) ui.pager('export') cmdutil.export(repo, revs, fntemplate=opts.get('output'), - switch_parent=opts.get('switch_parent'), - opts=patch.diffallopts(ui, opts)) + switch_parent=opts.get('switch_parent'), + opts=patch.diffallopts(ui, opts), + fmt=opts['format']) @command('files', [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')), diff --git a/mercurial/utils/stringutil.py b/mercurial/utils/stringutil.py --- a/mercurial/utils/stringutil.py +++ b/mercurial/utils/stringutil.py @@ -34,6 +34,10 @@ '%s: %s' % (pprint(k), pprint(v)) for k, v in sorted(o.items()))) elif isinstance(o, bool): return b'True' if o else b'False' + elif isinstance(o, int): + return '%d' % o + elif isinstance(o, float): + return b'%f' % o else: raise error.ProgrammingError('do not know how to format %r' % o) diff --git a/tests/test-completion.t b/tests/test-completion.t --- a/tests/test-completion.t +++ b/tests/test-completion.t @@ -231,7 +231,7 @@ clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure commit: addremove, close-branch, amend, secret, edit, interactive, include, exclude, message, logfile, date, user, subrepos diff: rev, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, unified, stat, root, include, exclude, subrepos - export: output, switch-parent, rev, text, git, binary, nodates + export: output, switch-parent, rev, format, text, git, binary, nodates forget: include, exclude, dry-run init: ssh, remotecmd, insecure log: follow, follow-first, date, copies, keyword, rev, line-range, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude diff --git a/tests/test-export.t b/tests/test-export.t --- a/tests/test-export.t +++ b/tests/test-export.t @@ -258,6 +258,51 @@ abort: export requires at least one changeset [255] +--format=hg works and is the default + + $ hg export > expected + $ hg export --format hg > got + $ diff got expected + +--format=cbor emits a CBOR patch + + $ hg export --format cbor > cbor + $ f --sha256 --hexdump cbor + cbor: sha256=825d5c5a23bb64d1aad17e39f1e151c933d519293f3d05eb591a7479453ea21a + 0000: 9f bf 47 76 65 72 73 69 6f 6e 01 44 75 73 65 72 |..Gversion.Duser| + 0010: 44 74 65 73 74 44 74 69 6d 65 fb 00 00 00 00 00 |DtestDtime......| + 0020: 00 00 00 48 74 69 6d 65 7a 6f 6e 65 00 46 62 72 |...Htimezone.Fbr| + 0030: 61 6e 63 68 47 64 65 66 61 75 6c 74 44 6e 6f 64 |anchGdefaultDnod| + 0040: 65 54 19 7e cd 81 a5 7f 76 0b 54 f3 4a 58 81 7a |eT.~....v.T.JX.z| + 0050: d5 b0 49 91 fa 47 47 70 61 72 65 6e 74 73 81 54 |..I..GGparents.T| + 0060: f3 ac ba fa c1 61 ec 68 f1 59 8a f3 8f 79 4f 28 |.....a.h.Y...yO(| + 0070: 84 7c a5 d3 4b 64 65 73 63 72 69 70 74 69 6f 6e |.|..Kdescription| + 0080: 58 5b 20 21 22 23 24 25 26 28 2c 2d 2e 2f 30 31 |X[ !"#$%&(,-./01| + 0090: 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 |23456789:;<=>?@A| + 00a0: 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 |BCDEFGHIJKLMNOPQ| + 00b0: 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 |RSTUVWXYZ[\]^_`a| + 00c0: 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 |bcdefghijklmnopq| + 00d0: 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 4b 70 61 |rstuvwxyz{|}~Kpa| + 00e0: 74 63 68 63 68 75 6e 6b 73 9f 58 7b 64 69 66 66 |tchchunks.X{diff| + 00f0: 20 2d 72 20 66 33 61 63 62 61 66 61 63 31 36 31 | -r f3acbafac161| + 0100: 20 2d 72 20 31 39 37 65 63 64 38 31 61 35 37 66 | -r 197ecd81a57f| + 0110: 20 66 6f 6f 0a 2d 2d 2d 20 61 2f 66 6f 6f 09 54 | foo.--- a/foo.T| + 0120: 68 75 20 4a 61 6e 20 30 31 20 30 30 3a 30 30 3a |hu Jan 01 00:00:| + 0130: 30 30 20 31 39 37 30 20 2b 30 30 30 30 0a 2b 2b |00 1970 +0000.++| + 0140: 2b 20 62 2f 66 6f 6f 09 54 68 75 20 4a 61 6e 20 |+ b/foo.Thu Jan | + 0150: 30 31 20 30 30 3a 30 30 3a 30 30 20 31 39 37 30 |01 00:00:00 1970| + 0160: 20 2b 30 30 30 30 0a 58 2f 40 40 20 2d 31 30 2c | +0000.X/@@ -10,| + 0170: 33 20 2b 31 30 2c 34 20 40 40 0a 20 66 6f 6f 2d |3 +10,4 @@. foo-| + 0180: 39 0a 20 66 6f 6f 2d 31 30 0a 20 66 6f 6f 2d 31 |9. foo-10. foo-1| + 0190: 31 0a 2b 6c 69 6e 65 0a ff ff ff |1.+line....| + + >>> from __future__ import print_function + >>> from mercurial.thirdparty import cbor + >>> from mercurial.utils import stringutil + >>> with open('cbor', 'rb') as fh: + ... print(stringutil.pprint(cbor.load(fh))) + [{b'branch': b'default', b'description': b' !"#$%&(,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~', b'node': b'\x19~\xcd\x81\xa5\x7fv\x0bT\xf3JX\x81z\xd5\xb0I\x91\xfaG', b'parents': [b'\xf3\xac\xba\xfa\xc1a\xech\xf1Y\x8a\xf3\x8fyO(\x84|\xa5\xd3'], b'patchchunks': [b'diff -r f3acbafac161 -r 197ecd81a57f foo\n--- a/foo\tThu Jan 01 00:00:00 1970 +0000\n+++ b/foo\tThu Jan 01 00:00:00 1970 +0000\n', b'@@ -10,3 +10,4 @@\n foo-9\n foo-10\n foo-11\n+line\n'], b'time': 0.000000, b'timezone': 0, b'user': b'test', b'version': 1}] + Check for color output $ cat <> $HGRCPATH > [color]