Patchwork [2,of,2] bundle2: add support for changegroup2

login
register
mail settings
Submitter Sune Foldager
Date Sept. 2, 2014, 10:43 a.m.
Message ID <67ef070167adfe202310.1409654610@Cuivienen.local>
Download mbox | patch
Permalink /patch/5679/
State Changes Requested
Headers show

Comments

Sune Foldager - Sept. 2, 2014, 10:43 a.m.
# HG changeset patch
# User Sune Foldager <cryo@cyanite.org>
# Date 1409654405 -7200
#      Tue Sep 02 12:40:05 2014 +0200
# Node ID 67ef070167adfe202310ac0a3783df82b813ee38
# Parent  6e8fe07ad948009f64ea7f199d1c3e3ca22b92c4
bundle2: add support for changegroup2

Changegroup format 2 includes the delta parent for each chunk so we can create
more efficient changegroups when using generaldelta.
Sune Foldager - Sept. 2, 2014, 10:46 a.m.
I accidentally included a commented-out print-statement in the delta parent function below.

Pierre-Yves wanted bundle2 to be able to include both cg1 and cg2, but I don’t see why we need that; this change will break existing bundle2 anyway, so people who use it will need to update both ends in all cases. Pierre-Yves, what do you think?

-Sune


On 02/09/2014, at 12:43, Sune Foldager <sune.foldager@me.com> wrote:

> # HG changeset patch
> # User Sune Foldager <cryo@cyanite.org>
> # Date 1409654405 -7200
> #      Tue Sep 02 12:40:05 2014 +0200
> # Node ID 67ef070167adfe202310ac0a3783df82b813ee38
> # Parent  6e8fe07ad948009f64ea7f199d1c3e3ca22b92c4
> bundle2: add support for changegroup2
> 
> Changegroup format 2 includes the delta parent for each chunk so we can create
> more efficient changegroups when using generaldelta.
> 
> diff -r 6e8fe07ad948 -r 67ef070167ad mercurial/bundle2.py
> --- a/mercurial/bundle2.py	Tue Sep 02 12:11:36 2014 +0200
> +++ b/mercurial/bundle2.py	Tue Sep 02 12:40:05 2014 +0200
> @@ -820,7 +820,7 @@
>     # we need to make sure we trigger the creation of a transaction object used
>     # for the whole processing scope.
>     op.gettransaction()
> -    cg = changegroup.cg1unpacker(inpart, 'UN')
> +    cg = changegroup.cg2unpacker(inpart, 'UN')
>     ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2')
>     op.records.add('changegroup', {'return': ret})
>     if op.reply is not None:
> diff -r 6e8fe07ad948 -r 67ef070167ad mercurial/changegroup.py
> --- a/mercurial/changegroup.py	Tue Sep 02 12:11:36 2014 +0200
> +++ b/mercurial/changegroup.py	Tue Sep 02 12:40:05 2014 +0200
> @@ -13,6 +13,7 @@
> import discovery, error, phases, branchmap
> 
> _CHANGEGROUPV1_DELTA_HEADER = "20s20s20s20s"
> +_CHANGEGROUPV2_DELTA_HEADER = "20s20s20s20s20s"
> 
> def readexactly(stream, n):
>     '''read n bytes from stream.read and abort if less was available'''
> @@ -215,6 +216,15 @@
>                     pos = next
>             yield closechunk()
> 
> +class cg2unpacker(cg1unpacker):
> +    deltaheader = _CHANGEGROUPV2_DELTA_HEADER
> +    deltaheadersize = struct.calcsize(deltaheader)
> +
> +    def _deltaheader(self, headertuple, prevnode):
> +        node, p1, p2, deltabase, cs = headertuple
> +        return node, p1, p2, deltabase, cs
> +
> +
> class headerlessfixup(object):
>     def __init__(self, fh, h):
>         self._h = h
> @@ -410,10 +420,13 @@
>                                         reorder=reorder):
>                     yield chunk
> 
> +    def deltaparent(self, revlog, rev, p1, p2, prev):
> +        return prev
> +
>     def revchunk(self, revlog, rev, prev, linknode):
>         node = revlog.node(rev)
>         p1, p2 = revlog.parentrevs(rev)
> -        base = prev
> +        base = self.deltaparent(revlog, rev, p1, p2, prev)
> 
>         prefix = ''
>         if base == nullrev:
> @@ -433,6 +446,28 @@
>         # do nothing with basenode, it is implicitly the previous one in HG10
>         return struct.pack(self.deltaheader, node, p1n, p2n, linknode)
> 
> +class cg2packer(cg1packer):
> +
> +    deltaheader = _CHANGEGROUPV2_DELTA_HEADER
> +
> +    def group(self, nodelist, revlog, lookup, units=None, reorder=None):
> +        if (revlog._generaldelta and reorder is not True):
> +            reorder = False
> +        return cg1packer.group(self, nodelist, revlog, lookup,
> +                               units=units, reorder=reorder)
> +
> +    def deltaparent(self, revlog, rev, p1, p2, prev):
> +        dp = revlog.deltaparent(rev)
> +        #print 'rev %d dp %d p1 %d p2 %d prev %d' % (rev, dp, p1, p2, prev)
> +        # avoid storing full revisions; pick prev in those cases
> +        # also pick prev when we can't be sure remote has dp
> +        if dp == nullrev or (dp != p1 and dp != p2 and dp != prev):
> +            return prev
> +        return dp
> +
> +    def builddeltaheader(self, node, p1n, p2n, basenode, linknode):
> +        return struct.pack(self.deltaheader, node, p1n, p2n, basenode, linknode)
> +
> def _changegroupinfo(repo, nodes, source):
>     if repo.ui.verbose or source == 'bundle':
>         repo.ui.status(_("%d changesets found\n") % len(nodes))
> @@ -524,6 +559,24 @@
>     outgoing = _computeoutgoing(repo, heads, common)
>     return getlocalchangegroup(repo, source, outgoing, bundlecaps=bundlecaps)
> 
> +def getlocalchangegroup2(repo, source, outgoing, bundlecaps=None):
> +    if not outgoing.missing:
> +        return None
> +    bundler = cg2packer(repo, bundlecaps)
> +    repo = repo.unfiltered()
> +    commonrevs = outgoing.common
> +    csets = outgoing.missing
> +    heads = outgoing.missingheads
> +    heads.sort()
> +    fastpathlinkrev = repo.filtername is None and heads == sorted(repo.heads())
> +    repo.hook('preoutgoing', throw=True, source=source)
> +    _changegroupinfo(repo, csets, source)
> +    return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
> +
> +def getchangegroup2(repo, source, heads=None, common=None, bundlecaps=None):
> +    outgoing = _computeoutgoing(repo, heads, common)
> +    return getlocalchangegroup2(repo, source, outgoing, bundlecaps=bundlecaps)
> +
> def changegroup(repo, basenodes, source):
>     # to avoid a race we use changegroupsubset() (issue1320)
>     return changegroupsubset(repo, basenodes, repo.heads(), source)
> diff -r 6e8fe07ad948 -r 67ef070167ad mercurial/exchange.py
> --- a/mercurial/exchange.py	Tue Sep 02 12:11:36 2014 +0200
> +++ b/mercurial/exchange.py	Tue Sep 02 12:40:05 2014 +0200
> @@ -401,8 +401,8 @@
>                                      pushop.outgoing)
>     if not pushop.force:
>         bundler.newpart('B2X:CHECK:HEADS', data=iter(pushop.remoteheads))
> -    cg = changegroup.getlocalchangegroup(pushop.repo, 'push', pushop.outgoing)
> -    cgpart = bundler.newpart('B2X:CHANGEGROUP', data=cg.getchunks())
> +    cg = changegroup.getlocalchangegroup2(pushop.repo, 'push', pushop.outgoing)
> +    cgpart = bundler.newpart('B2X:CHANGEGROUP', data=cg)
>     def handlereply(op):
>         """extract addchangroup returns from server reply"""
>         cgreplies = op.records.getreplies(cgpart.id)
> @@ -976,28 +976,27 @@
>     The implementation is at a very early stage and will get massive rework
>     when the API of bundle is refined.
>     """
> -    cg = None
> -    if kwargs.get('cg', True):
> -        # build changegroup bundle here.
> -        cg = changegroup.getchangegroup(repo, source, heads=heads,
> -                                         common=common, bundlecaps=bundlecaps)
> -    elif 'HG2X' not in bundlecaps:
> -        raise ValueError(_('request for bundle10 must include changegroup'))
>     if bundlecaps is None or 'HG2X' not in bundlecaps:
>         if kwargs:
>             raise ValueError(_('unsupported getbundle arguments: %s')
>                              % ', '.join(sorted(kwargs.keys())))
> -        return cg
> +        return changegroup.getchangegroup(repo, source, heads=heads,
> +                                   common=common, bundlecaps=bundlecaps)
> +
>     # very crude first implementation,
>     # the bundle API will change and the generation will be done lazily.
> +    cg = None
>     b2caps = {}
>     for bcaps in bundlecaps:
>         if bcaps.startswith('bundle2='):
>             blob = urllib.unquote(bcaps[len('bundle2='):])
>             b2caps.update(bundle2.decodecaps(blob))
>     bundler = bundle2.bundle20(repo.ui, b2caps)
> -    if cg:
> -        bundler.newpart('b2x:changegroup', data=cg.getchunks())
> +    if kwargs.get('cg', True):
> +        cg = changegroup.getchangegroup2(repo, source, heads=heads,
> +                                   common=common, bundlecaps=bundlecaps)
> +        if cg:
> +            bundler.newpart('b2x:changegroup', data=cg)
>     listkeys = kwargs.get('listkeys', ())
>     for namespace in listkeys:
>         part = bundler.newpart('b2x:listkeys')
> diff -r 6e8fe07ad948 -r 67ef070167ad tests/test-bundle2.t
> --- a/tests/test-bundle2.t	Tue Sep 02 12:11:36 2014 +0200
> +++ b/tests/test-bundle2.t	Tue Sep 02 12:40:05 2014 +0200
> @@ -106,8 +106,8 @@
>>            headmissing = [c.node() for c in repo.set('heads(%ld)', revs)]
>>            headcommon  = [c.node() for c in repo.set('parents(%ld) - %ld', revs, revs)]
>>            outgoing = discovery.outgoing(repo.changelog, headcommon, headmissing)
> -  >             cg = changegroup.getlocalchangegroup(repo, 'test:bundle2', outgoing, None)
> -  >             bundler.newpart('b2x:changegroup', data=cg.getchunks())
> +  >             cg = changegroup.getlocalchangegroup2(repo, 'test:bundle2', outgoing, None)
> +  >             bundler.newpart('b2x:changegroup', data=cg)
>> 
>>    if opts['parts']:
>>       bundler.newpart('test:empty')
> @@ -719,26 +719,26 @@
>   end of bundle
> 
>   $ cat ../rev.hg2
> -  HG2X\x00\x00\x00\x16\x0fb2x:changegroup\x00\x00\x00\x00\x00\x00\x00\x00\x06\x13\x00\x00\x00\xa42\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j_\xdd\xd9\x89W\xc8\xa5JMCm\xfe\x1d\xa9\xd8\x7f!\xa1\xb9{\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c (esc)
> +  HG2X\x00\x00\x00\x16\x0fb2x:changegroup\x00\x00\x00\x00\x00\x00\x00\x00\x06\xef\x00\x00\x00\xb82\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j_\xdd\xd9\x89W\xc8\xa5JMCm\xfe\x1d\xa9\xd8\x7f!\xa1\xb9{\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00_\xdd\xd9\x89W\xc8\xa5JMCm\xfe\x1d\xa9\xd8\x7f!\xa1\xb9{2\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c (esc)
>   \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x02D (esc)
> -  \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01D\x00\x00\x00\xa4\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xcd\x01\x0b\x8c\xd9\x98\xf3\x98\x1aZ\x81\x15\xf9O\x8d\xa4\xabP`\x89\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)4dece9c826f69490507b98c6383a3009b295837d (esc)
> +  \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01D\x00\x00\x00\xb8\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xcd\x01\x0b\x8c\xd9\x98\xf3\x98\x1aZ\x81\x15\xf9O\x8d\xa4\xabP`\x89\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)4dece9c826f69490507b98c6383a3009b295837d (esc)
>   \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x02E (esc)
> -  \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01E\x00\x00\x00\xa2\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)365b93d57fdf4814e2b5911d6bacff2b12014441 (esc)
> -  \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x00\x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01G\x00\x00\x00\xa4\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
> -  \x87\xcd\xc9n\x8e\xaa\xb6$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
> +  \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01E\x00\x00\x00\xb6\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)365b93d57fdf4814e2b5911d6bacff2b12014441 (esc)
> +  \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x00\x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01G\x00\x00\x00\xb8\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
> +  \x87\xcd\xc9n\x8e\xaa\xb6$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
>   \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)8bee48edc7318541fc0013ee41b089276a8c24bf (esc)
>   \x00\x00\x00f\x00\x00\x00f\x00\x00\x00\x02H (esc)
> -  \x00\x00\x00g\x00\x00\x00h\x00\x00\x00\x01H\x00\x00\x00\x00\x00\x00\x00\x8bn\x1fLG\xec\xb53\xff\xd0\xc8\xe5,\xdc\x88\xaf\xb6\xcd9\xe2\x0cf\xa5\xa0\x18\x17\xfd\xf5#\x9c'8\x02\xb5\xb7a\x8d\x05\x1c\x89\xe4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+D\x00c3f1ca2924c16a19b0656a84900e504e5b0aec2d (esc)
> -  \x00\x00\x00\x8bM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0	\xb2\x95\x83}\x00}\x8c\x9d\x88\x84\x13%\xf5\xc6\xb0cq\xb3[N\x8a+\x1a\x83\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00+\x00\x00\x00\xac\x00\x00\x00+E\x009c6fd0350a6c0d0c49d4a9c5017cf07043f54e58 (esc)
> -  \x00\x00\x00\x8b6[\x93\xd5\x7f\xdfH\x14\xe2\xb5\x91\x1dk\xac\xff+\x12\x01DA(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xceM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0	\xb2\x95\x83}\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00V\x00\x00\x00V\x00\x00\x00+F\x0022bfcfd62a21a3287edbd4d656218d0f525ed76a (esc)
> -  \x00\x00\x00\x97\x8b\xeeH\xed\xc71\x85A\xfc\x00\x13\xeeA\xb0\x89'j\x8c$\xbf(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xce\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
> +  \x00\x00\x00g\x00\x00\x00h\x00\x00\x00\x01H\x00\x00\x00\x00\x00\x00\x00\x9fn\x1fLG\xec\xb53\xff\xd0\xc8\xe5,\xdc\x88\xaf\xb6\xcd9\xe2\x0cf\xa5\xa0\x18\x17\xfd\xf5#\x9c'8\x02\xb5\xb7a\x8d\x05\x1c\x89\xe4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00f\xa5\xa0\x18\x17\xfd\xf5#\x9c'8\x02\xb5\xb7a\x8d\x05\x1c\x89\xe42\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+D\x00c3f1ca2924c16a19b0656a84900e504e5b0aec2d (esc)
> +  \x00\x00\x00\x9fM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0	\xb2\x95\x83}\x00}\x8c\x9d\x88\x84\x13%\xf5\xc6\xb0cq\xb3[N\x8a+\x1a\x83\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00n\x1fLG\xec\xb53\xff\xd0\xc8\xe5,\xdc\x88\xaf\xb6\xcd9\xe2\x0c\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00+\x00\x00\x00\xac\x00\x00\x00+E\x009c6fd0350a6c0d0c49d4a9c5017cf07043f54e58 (esc)
> +  \x00\x00\x00\x9f6[\x93\xd5\x7f\xdfH\x14\xe2\xb5\x91\x1dk\xac\xff+\x12\x01DA(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xceM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0	\xb2\x95\x83}(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xce\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00+\x00\x00\x00+\x00\x00\x00+E\x009c6fd0350a6c0d0c49d4a9c5017cf07043f54e58 (esc)
> +  \x00\x00\x00\xab\x8b\xeeH\xed\xc71\x85A\xfc\x00\x13\xeeA\xb0\x89'j\x8c$\xbf(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xce\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x006[\x93\xd5\x7f\xdfH\x14\xe2\xb5\x91\x1dk\xac\xff+\x12\x01DA\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
>   \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00+\x00\x00\x00V\x00\x00\x00\x00\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+H\x008500189e74a9e0475e822093bc7db0d631aeb0b4 (esc)
> -  \x00\x00\x00\x00\x00\x00\x00\x05D\x00\x00\x00b\xc3\xf1\xca)$\xc1j\x19\xb0ej\x84\x90\x0ePN[ (esc)
> -  \xec-\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02D (esc)
> -  \x00\x00\x00\x00\x00\x00\x00\x05E\x00\x00\x00b\x9co\xd05 (esc)
> +  \x00\x00\x00\x00\x00\x00\x00\x05D\x00\x00\x00v\xc3\xf1\xca)$\xc1j\x19\xb0ej\x84\x90\x0ePN[ (esc)
> +  \xec-\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02D (esc)
> +  \x00\x00\x00\x00\x00\x00\x00\x05E\x00\x00\x00v\x9co\xd05 (esc)
>   l\r (no-eol) (esc)
> -  \x0cI\xd4\xa9\xc5\x01|\xf0pC\xf5NX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02E (esc)
> -  \x00\x00\x00\x00\x00\x00\x00\x05H\x00\x00\x00b\x85\x00\x18\x9et\xa9\xe0G^\x82 \x93\xbc}\xb0\xd61\xae\xb0\xb4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
> +  \x0cI\xd4\xa9\xc5\x01|\xf0pC\xf5NX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02E (esc)
> +  \x00\x00\x00\x00\x00\x00\x00\x05H\x00\x00\x00v\x85\x00\x18\x9et\xa9\xe0G^\x82 \x93\xbc}\xb0\xd61\xae\xb0\xb4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
>   \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02H (esc)
>   \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
> 
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
Sune Foldager - Sept. 2, 2014, 11:28 a.m.
> +    def deltaparent(self, revlog, rev, p1, p2, prev):
> +        dp = revlog.deltaparent(rev)
> +        #print 'rev %d dp %d p1 %d p2 %d prev %d' % (rev, dp, p1, p2, prev)
> +        # avoid storing full revisions; pick prev in those cases
> +        # also pick prev when we can't be sure remote has dp
> +        if dp == nullrev or (dp != p1 and dp != p2 and dp != prev):
> +            return prev
> +        return dp

This should probably go in the commit message ;) but, the rationale for choosing the delta parent like this is the following:

1. Many times, full revisions are stored in the revlog because the chain length max. is exceeded. In that case we would prefer a delta in the changegroup to make it smaller.
2. Some chains are quite small in the beginning, or when the data amount is small.
3. The difference between a really poor delta (a kill everything and insert everything) and a full version is small.
4. We can change this algorithm at any time without breaking anything :)

-Sune
Mike Hommey - Sept. 2, 2014, 11:36 a.m.
On Tue, Sep 02, 2014 at 12:46:50PM +0200, sune.foldager@me.com wrote:
> I accidentally included a commented-out print-statement in the delta
> parent function below.
> 
> Pierre-Yves wanted bundle2 to be able to include both cg1 and cg2, but
> I don’t see why we need that; this change will break existing bundle2
> anyway, so people who use it will need to update both ends in all
> cases. Pierre-Yves, what do you think?

FWIW, one usecase of my most recent patch was to allow for a mercurial
server to provide a bundle2 with one part being a link to an existing
bundle10 we refresh every day at Mozilla, and have the client understand
that. That requires bundle2 to be able to include both cg1 and cg2.

Mike
Sune Foldager - Sept. 2, 2014, 11:50 a.m.
On 02/09/2014, at 13:36, Mike Hommey <mh@glandium.org> wrote:

> On Tue, Sep 02, 2014 at 12:46:50PM +0200, sune.foldager@me.com wrote:
>> I accidentally included a commented-out print-statement in the delta
>> parent function below.
>> 
>> Pierre-Yves wanted bundle2 to be able to include both cg1 and cg2, but
>> I don’t see why we need that; this change will break existing bundle2
>> anyway, so people who use it will need to update both ends in all
>> cases. Pierre-Yves, what do you think?
> 
> FWIW, one usecase of my most recent patch was to allow for a mercurial
> server to provide a bundle2 with one part being a link to an existing
> bundle10 we refresh every day at Mozilla, and have the client understand
> that. That requires bundle2 to be able to include both cg1 and cg2.

I don’t think I understand.. do you have custom code that reads the bundle2’s and extracts the cg1 (which is pretty much a bundle1) and applies it to some other repo automatically, or something like that?

-Sune
Mike Hommey - Sept. 2, 2014, 1:11 p.m.
On Tue, Sep 02, 2014 at 01:50:49PM +0200, sune.foldager@me.com wrote:
> On 02/09/2014, at 13:36, Mike Hommey <mh@glandium.org> wrote:
> 
> > On Tue, Sep 02, 2014 at 12:46:50PM +0200, sune.foldager@me.com
> > wrote:
> >> I accidentally included a commented-out print-statement in the
> >> delta parent function below.
> >> 
> >> Pierre-Yves wanted bundle2 to be able to include both cg1 and cg2,
> >> but I don’t see why we need that; this change will break existing
> >> bundle2 anyway, so people who use it will need to update both ends
> >> in all cases. Pierre-Yves, what do you think?
> > 
> > FWIW, one usecase of my most recent patch was to allow for a
> > mercurial server to provide a bundle2 with one part being a link to
> > an existing bundle10 we refresh every day at Mozilla, and have the
> > client understand that. That requires bundle2 to be able to include
> > both cg1 and cg2.
> 
> I don’t think I understand.. do you have custom code that reads the
> bundle2’s and extracts the cg1 (which is pretty much a bundle1) and
> applies it to some other repo automatically, or something like that?

No, I have code that adds a new part type to bundle2 that gives a
url pointer to a cg1, and the patch I recently sent [1] takes care of
the client handling that. To avoid code repetition (and for
consistency), that relies on the bundle2 handling of the
b2x:changegroup part, which needs to support cg1 for that to work.

1. http://www.selenic.com/pipermail/mercurial-devel/2014-September/061279.html
Sune Foldager - Sept. 2, 2014, 2:06 p.m.
On 02/09/2014, at 15:11, Mike Hommey <mh@glandium.org> wrote:

> On Tue, Sep 02, 2014 at 01:50:49PM +0200, sune.foldager@me.com wrote:
>> On 02/09/2014, at 13:36, Mike Hommey <mh@glandium.org> wrote:
>> 
>>> On Tue, Sep 02, 2014 at 12:46:50PM +0200, sune.foldager@me.com
>>> wrote:
>>>> I accidentally included a commented-out print-statement in the
>>>> delta parent function below.
>>>> 
>>>> Pierre-Yves wanted bundle2 to be able to include both cg1 and cg2,
>>>> but I don’t see why we need that; this change will break existing
>>>> bundle2 anyway, so people who use it will need to update both ends
>>>> in all cases. Pierre-Yves, what do you think?
>>> 
>>> FWIW, one usecase of my most recent patch was to allow for a
>>> mercurial server to provide a bundle2 with one part being a link to
>>> an existing bundle10 we refresh every day at Mozilla, and have the
>>> client understand that. That requires bundle2 to be able to include
>>> both cg1 and cg2.
>> 
>> I don’t think I understand.. do you have custom code that reads the
>> bundle2’s and extracts the cg1 (which is pretty much a bundle1) and
>> applies it to some other repo automatically, or something like that?
> 
> No, I have code that adds a new part type to bundle2 that gives a
> url pointer to a cg1, and the patch I recently sent [1] takes care of
> the client handling that. To avoid code repetition (and for
> consistency), that relies on the bundle2 handling of the
> b2x:changegroup part, which needs to support cg1 for that to work.
> 
> 1. http://www.selenic.com/pipermail/mercurial-devel/2014-September/061279.html

Ok. I don’t think it’s a good idea to use readbundle at that point, since it’s intended to read actual bundles (1 or 2), and not changegroups. Of course bundle 1’s are identical to changegroups but not so for bundle2, and I think we’d like to return something richer than just a changegroup from readbundle at some point.

I think bundle2 will fully support being written and read from disk soon as well. How do you propose this would work? It’s not like we can generate both changegroup formats and stick them in all bundle2s generated all the time. Could you just refer to a bundle2 instead of bundle10 in the future when we can produce bundle2’s to disk?

Maybe I’m confused :p

-Sune
Sune Foldager - Sept. 3, 2014, 8:03 a.m.
On 02/09/2014, at 16:06, sune.foldager@me.com wrote:

> On 02/09/2014, at 15:11, Mike Hommey <mh@glandium.org> wrote:
> 
>> On Tue, Sep 02, 2014 at 01:50:49PM +0200, sune.foldager@me.com wrote:
>>> On 02/09/2014, at 13:36, Mike Hommey <mh@glandium.org> wrote:
>>> 
>>>> On Tue, Sep 02, 2014 at 12:46:50PM +0200, sune.foldager@me.com
>>>> wrote:
>>>>> I accidentally included a commented-out print-statement in the
>>>>> delta parent function below.
>>>>> 
>>>>> Pierre-Yves wanted bundle2 to be able to include both cg1 and cg2,
>>>>> but I don’t see why we need that; this change will break existing
>>>>> bundle2 anyway, so people who use it will need to update both ends
>>>>> in all cases. Pierre-Yves, what do you think?
>>>> 
>>>> FWIW, one usecase of my most recent patch was to allow for a
>>>> mercurial server to provide a bundle2 with one part being a link to
>>>> an existing bundle10 we refresh every day at Mozilla, and have the
>>>> client understand that. That requires bundle2 to be able to include
>>>> both cg1 and cg2.
>>> 
>>> I don’t think I understand.. do you have custom code that reads the
>>> bundle2’s and extracts the cg1 (which is pretty much a bundle1) and
>>> applies it to some other repo automatically, or something like that?
>> 
>> No, I have code that adds a new part type to bundle2 that gives a
>> url pointer to a cg1, and the patch I recently sent [1] takes care of
>> the client handling that. To avoid code repetition (and for
>> consistency), that relies on the bundle2 handling of the
>> b2x:changegroup part, which needs to support cg1 for that to work.
>> 
>> 1. http://www.selenic.com/pipermail/mercurial-devel/2014-September/061279.html
> 
> Ok. I don’t think it’s a good idea to use readbundle at that point, since it’s intended to read actual bundles (1 or 2), and not changegroups. Of course bundle 1’s are identical to changegroups but not so for bundle2, and I think we’d like to return something richer than just a changegroup from readbundle at some point.

To expand a bit, I want to move writebundle to exchange as well, and change things a bit. The current design is not great, as writebundle takes a bundle (currently just  cg1) and a bundletype. We want to be able to pass in bundle10s and bundle2s, not just bundle10=cg1. That means bundletype has to match the passed in bundle which is obviously unfortunate. Instead writebundle should just get the compression type passed in, if even that, and then get the necessary bundle header from the bundle object. We will still need to support passing in the old types (HG10UN etc.) due to the wire protocol. I’ll make a patch for this :)

-Sune
Pierre-Yves David - Sept. 3, 2014, 3:45 p.m.
On 09/02/2014 04:06 PM, sune.foldager@me.com wrote:
> On 02/09/2014, at 15:11, Mike Hommey <mh@glandium.org> wrote:
>
>> On Tue, Sep 02, 2014 at 01:50:49PM +0200, sune.foldager@me.com wrote:
>>> On 02/09/2014, at 13:36, Mike Hommey <mh@glandium.org> wrote:
>>>
>>>> On Tue, Sep 02, 2014 at 12:46:50PM +0200, sune.foldager@me.com
>>>> wrote:
>>>>> I accidentally included a commented-out print-statement in the
>>>>> delta parent function below.
>>>>>
>>>>> Pierre-Yves wanted bundle2 to be able to include both cg1 and cg2,
>>>>> but I don’t see why we need that; this change will break existing
>>>>> bundle2 anyway, so people who use it will need to update both ends
>>>>> in all cases. Pierre-Yves, what do you think?
>>>>
>>>> FWIW, one usecase of my most recent patch was to allow for a
>>>> mercurial server to provide a bundle2 with one part being a link to
>>>> an existing bundle10 we refresh every day at Mozilla, and have the
>>>> client understand that. That requires bundle2 to be able to include
>>>> both cg1 and cg2.
>>>
>>> I don’t think I understand.. do you have custom code that reads the
>>> bundle2’s and extracts the cg1 (which is pretty much a bundle1) and
>>> applies it to some other repo automatically, or something like that?
>>
>> No, I have code that adds a new part type to bundle2 that gives a
>> url pointer to a cg1, and the patch I recently sent [1] takes care of
>> the client handling that. To avoid code repetition (and for
>> consistency), that relies on the bundle2 handling of the
>> b2x:changegroup part, which needs to support cg1 for that to work.
>>
>> 1. http://www.selenic.com/pipermail/mercurial-devel/2014-September/061279.html
>
> Ok. I don’t think it’s a good idea to use readbundle at that point, since it’s intended to read actual bundles (1 or 2), and not changegroups. Of course bundle 1’s are identical to changegroups but not so for bundle2, and I think we’d like to return something richer than just a changegroup from readbundle at some point.

I also think that using readbundle is a bad idea. Not sure why the idea 
got lost between my brain and my hand during the review.

> I think bundle2 will fully support being written and read from disk soon as well. How do you propose this would work? It’s not like we can generate both changegroup formats and stick them in all bundle2s generated all the time. Could you just refer to a bundle2 instead of bundle10 in the future when we can produce bundle2’s to disk?

We'll definitly want on disk bundle2 support at some point.
Pierre-Yves David - Sept. 3, 2014, 4:12 p.m.
On 09/02/2014 12:43 PM, Sune Foldager wrote:
> # HG changeset patch
> # User Sune Foldager <cryo@cyanite.org>
> # Date 1409654405 -7200
> #      Tue Sep 02 12:40:05 2014 +0200
> # Node ID 67ef070167adfe202310ac0a3783df82b813ee38
> # Parent  6e8fe07ad948009f64ea7f199d1c3e3ca22b92c4
> bundle2: add support for changegroup2
>
> Changegroup format 2 includes the delta parent for each chunk so we can create
> more efficient changegroups when using generaldelta.

This changesets needs to be split into multiple ones so that each 
semantic changes can be reviewed as its own context. This will also 
helps to get the consensual bits in while we polish the last ones.

It is okay to introduce code that stay unused until other patches land.


> diff -r 6e8fe07ad948 -r 67ef070167ad mercurial/bundle2.py
> --- a/mercurial/bundle2.py	Tue Sep 02 12:11:36 2014 +0200
> +++ b/mercurial/bundle2.py	Tue Sep 02 12:40:05 2014 +0200
> @@ -820,7 +820,7 @@
>       # we need to make sure we trigger the creation of a transaction object used
>       # for the whole processing scope.
>       op.gettransaction()
> -    cg = changegroup.cg1unpacker(inpart, 'UN')
> +    cg = changegroup.cg2unpacker(inpart, 'UN')
>       ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2')
>       op.records.add('changegroup', {'return': ret})
>       if op.reply is not None:

If you do that, all the people who are nice enough to test the 
experimental version of bundle2 will get a crash on pull and push when 
one of the sides is not aware about "cg2".

Please advertise a new capability with a list of version to let the 
client and the server negociates the cg version to be used.

This should also go into another patch and the cg2 parts. If you do not 
have time to get to the capability stuff, I'm okay with doing it myself 
as long as the cg2 part are done.

The option to use cg1 in bundle2 may be dropped when we makes bundle2 
non-experimental (s/HG2X/HG20/) but we needs ot for now.

> diff -r 6e8fe07ad948 -r 67ef070167ad mercurial/changegroup.py
> --- a/mercurial/changegroup.py	Tue Sep 02 12:11:36 2014 +0200
> +++ b/mercurial/changegroup.py	Tue Sep 02 12:40:05 2014 +0200
> @@ -13,6 +13,7 @@
>   import discovery, error, phases, branchmap
>
>   _CHANGEGROUPV1_DELTA_HEADER = "20s20s20s20s"
> +_CHANGEGROUPV2_DELTA_HEADER = "20s20s20s20s20s"
>
>   def readexactly(stream, n):
>       '''read n bytes from stream.read and abort if less was available'''
> @@ -215,6 +216,15 @@
>                       pos = next
>               yield closechunk()
>
> +class cg2unpacker(cg1unpacker):
> +    deltaheader = _CHANGEGROUPV2_DELTA_HEADER
> +    deltaheadersize = struct.calcsize(deltaheader)
> +
> +    def _deltaheader(self, headertuple, prevnode):
> +        node, p1, p2, deltabase, cs = headertuple
> +        return node, p1, p2, deltabase, cs
> +
> +
>   class headerlessfixup(object):
>       def __init__(self, fh, h):
>           self._h = h
> @@ -410,10 +420,13 @@
>                                           reorder=reorder):
>                       yield chunk
>
> +    def deltaparent(self, revlog, rev, p1, p2, prev):
> +        return prev
> +
>       def revchunk(self, revlog, rev, prev, linknode):
>           node = revlog.node(rev)
>           p1, p2 = revlog.parentrevs(rev)
> -        base = prev
> +        base = self.deltaparent(revlog, rev, p1, p2, prev)
>
>           prefix = ''
>           if base == nullrev:
> @@ -433,6 +446,28 @@
>           # do nothing with basenode, it is implicitly the previous one in HG10
>           return struct.pack(self.deltaheader, node, p1n, p2n, linknode)
>
> +class cg2packer(cg1packer):
> +
> +    deltaheader = _CHANGEGROUPV2_DELTA_HEADER
> +
> +    def group(self, nodelist, revlog, lookup, units=None, reorder=None):
> +        if (revlog._generaldelta and reorder is not True):
> +            reorder = False
> +        return cg1packer.group(self, nodelist, revlog, lookup,
> +                               units=units, reorder=reorder)
> +
> +    def deltaparent(self, revlog, rev, p1, p2, prev):
> +        dp = revlog.deltaparent(rev)
> +        #print 'rev %d dp %d p1 %d p2 %d prev %d' % (rev, dp, p1, p2, prev)

boo, spurious print

> +        # avoid storing full revisions; pick prev in those cases
> +        # also pick prev when we can't be sure remote has dp

You can maybe expand the comment to says that picking "prev" when we 
can't be sure remote has dp is corrent but may be suboptimal. So that 
some else looking at improving the code feels empowered to do so.

> +        if dp == nullrev or (dp != p1 and dp != p2 and dp != prev):
> +            return prev
> +        return dp
> +
> +    def builddeltaheader(self, node, p1n, p2n, basenode, linknode):
> +        return struct.pack(self.deltaheader, node, p1n, p2n, basenode, linknode)
> +
>   def _changegroupinfo(repo, nodes, source):
>       if repo.ui.verbose or source == 'bundle':
>           repo.ui.status(_("%d changesets found\n") % len(nodes))
> @@ -524,6 +559,24 @@
>       outgoing = _computeoutgoing(repo, heads, common)
>       return getlocalchangegroup(repo, source, outgoing, bundlecaps=bundlecaps)
>
> +def getlocalchangegroup2(repo, source, outgoing, bundlecaps=None):
> +    if not outgoing.missing:
> +        return None
> +    bundler = cg2packer(repo, bundlecaps)
> +    repo = repo.unfiltered()
> +    commonrevs = outgoing.common
> +    csets = outgoing.missing
> +    heads = outgoing.missingheads
> +    heads.sort()
> +    fastpathlinkrev = repo.filtername is None and heads == sorted(repo.heads())
> +    repo.hook('preoutgoing', throw=True, source=source)
> +    _changegroupinfo(repo, csets, source)
> +    return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
> +
> +def getchangegroup2(repo, source, heads=None, common=None, bundlecaps=None):
> +    outgoing = _computeoutgoing(repo, heads, common)
> +    return getlocalchangegroup2(repo, source, outgoing, bundlecaps=bundlecaps)
> +
>   def changegroup(repo, basenodes, source):
>       # to avoid a race we use changegroupsubset() (issue1320)
>       return changegroupsubset(repo, basenodes, repo.heads(), source)
> diff -r 6e8fe07ad948 -r 67ef070167ad mercurial/exchange.py
> --- a/mercurial/exchange.py	Tue Sep 02 12:11:36 2014 +0200
> +++ b/mercurial/exchange.py	Tue Sep 02 12:40:05 2014 +0200
> @@ -401,8 +401,8 @@
>                                        pushop.outgoing)
>       if not pushop.force:
>           bundler.newpart('B2X:CHECK:HEADS', data=iter(pushop.remoteheads))
> -    cg = changegroup.getlocalchangegroup(pushop.repo, 'push', pushop.outgoing)
> -    cgpart = bundler.newpart('B2X:CHANGEGROUP', data=cg.getchunks())
> +    cg = changegroup.getlocalchangegroup2(pushop.repo, 'push', pushop.outgoing)
> +    cgpart = bundler.newpart('B2X:CHANGEGROUP', data=cg)

Same that for getbundle, please extract this in another patches and 
makes it possible to keep using cg1

>       def handlereply(op):
>           """extract addchangroup returns from server reply"""
>           cgreplies = op.records.getreplies(cgpart.id)
> @@ -976,28 +976,27 @@
>       The implementation is at a very early stage and will get massive rework
>       when the API of bundle is refined.
>       """
> -    cg = None
> -    if kwargs.get('cg', True):
> -        # build changegroup bundle here.
> -        cg = changegroup.getchangegroup(repo, source, heads=heads,
> -                                         common=common, bundlecaps=bundlecaps)
> -    elif 'HG2X' not in bundlecaps:
> -        raise ValueError(_('request for bundle10 must include changegroup'))
>       if bundlecaps is None or 'HG2X' not in bundlecaps:
>           if kwargs:
>               raise ValueError(_('unsupported getbundle arguments: %s')
>                                % ', '.join(sorted(kwargs.keys())))
> -        return cg
> +        return changegroup.getchangegroup(repo, source, heads=heads,
> +                                   common=common, bundlecaps=bundlecaps)
> +
>       # very crude first implementation,
>       # the bundle API will change and the generation will be done lazily.
> +    cg = None
>       b2caps = {}
>       for bcaps in bundlecaps:
>           if bcaps.startswith('bundle2='):
>               blob = urllib.unquote(bcaps[len('bundle2='):])
>               b2caps.update(bundle2.decodecaps(blob))
>       bundler = bundle2.bundle20(repo.ui, b2caps)
> -    if cg:
> -        bundler.newpart('b2x:changegroup', data=cg.getchunks())
> +    if kwargs.get('cg', True):
> +        cg = changegroup.getchangegroup2(repo, source, heads=heads,
> +                                   common=common, bundlecaps=bundlecaps)
> +        if cg:
> +            bundler.newpart('b2x:changegroup', data=cg)
>       listkeys = kwargs.get('listkeys', ())
>       for namespace in listkeys:
>           part = bundler.newpart('b2x:listkeys')

Please do the reordering//refactor in another changeset so that the 
actual changes (that should also be in another patches) are easier to 
review.

Also install capability negotiation here as discussed before.


We should probably consider to move the code in a _getbundlecgpart 
function to help extension to wrap it.

> diff -r 6e8fe07ad948 -r 67ef070167ad tests/test-bundle2.t
> --- a/tests/test-bundle2.t	Tue Sep 02 12:11:36 2014 +0200
> +++ b/tests/test-bundle2.t	Tue Sep 02 12:40:05 2014 +0200

Please to not touch the test-bundle2.t file to tests bundle2 -part- 
behavior. either introduce a new test or tests the packer//unpacker in a 
changegroup related tests.

> @@ -106,8 +106,8 @@
>     >             headmissing = [c.node() for c in repo.set('heads(%ld)', revs)]
>     >             headcommon  = [c.node() for c in repo.set('parents(%ld) - %ld', revs, revs)]
>     >             outgoing = discovery.outgoing(repo.changelog, headcommon, headmissing)
> -  >             cg = changegroup.getlocalchangegroup(repo, 'test:bundle2', outgoing, None)
> -  >             bundler.newpart('b2x:changegroup', data=cg.getchunks())
> +  >             cg = changegroup.getlocalchangegroup2(repo, 'test:bundle2', outgoing, None)
> +  >             bundler.newpart('b2x:changegroup', data=cg)
>     >
>     >     if opts['parts']:
>     >        bundler.newpart('test:empty')
> @@ -719,26 +719,26 @@
>     end of bundle
>
>     $ cat ../rev.hg2
> -  HG2X\x00\x00\x00\x16\x0fb2x:changegroup\x00\x00\x00\x00\x00\x00\x00\x00\x06\x13\x00\x00\x00\xa42\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j_\xdd\xd9\x89W\xc8\xa5JMCm\xfe\x1d\xa9\xd8\x7f!\xa1\xb9{\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c (esc)
> +  HG2X\x00\x00\x00\x16\x0fb2x:changegroup\x00\x00\x00\x00\x00\x00\x00\x00\x06\xef\x00\x00\x00\xb82\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j_\xdd\xd9\x89W\xc8\xa5JMCm\xfe\x1d\xa9\xd8\x7f!\xa1\xb9{\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00_\xdd\xd9\x89W\xc8\xa5JMCm\xfe\x1d\xa9\xd8\x7f!\xa1\xb9{2\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c (esc)
>     \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x02D (esc)
> -  \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01D\x00\x00\x00\xa4\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xcd\x01\x0b\x8c\xd9\x98\xf3\x98\x1aZ\x81\x15\xf9O\x8d\xa4\xabP`\x89\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)4dece9c826f69490507b98c6383a3009b295837d (esc)
> +  \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01D\x00\x00\x00\xb8\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xcd\x01\x0b\x8c\xd9\x98\xf3\x98\x1aZ\x81\x15\xf9O\x8d\xa4\xabP`\x89\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)4dece9c826f69490507b98c6383a3009b295837d (esc)
>     \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x02E (esc)
> -  \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01E\x00\x00\x00\xa2\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)365b93d57fdf4814e2b5911d6bacff2b12014441 (esc)
> -  \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x00\x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01G\x00\x00\x00\xa4\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
> -  \x87\xcd\xc9n\x8e\xaa\xb6$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
> +  \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01E\x00\x00\x00\xb6\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)365b93d57fdf4814e2b5911d6bacff2b12014441 (esc)
> +  \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x00\x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01G\x00\x00\x00\xb8\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
> +  \x87\xcd\xc9n\x8e\xaa\xb6$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
>     \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)8bee48edc7318541fc0013ee41b089276a8c24bf (esc)
>     \x00\x00\x00f\x00\x00\x00f\x00\x00\x00\x02H (esc)
> -  \x00\x00\x00g\x00\x00\x00h\x00\x00\x00\x01H\x00\x00\x00\x00\x00\x00\x00\x8bn\x1fLG\xec\xb53\xff\xd0\xc8\xe5,\xdc\x88\xaf\xb6\xcd9\xe2\x0cf\xa5\xa0\x18\x17\xfd\xf5#\x9c'8\x02\xb5\xb7a\x8d\x05\x1c\x89\xe4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+D\x00c3f1ca2924c16a19b0656a84900e504e5b0aec2d (esc)
> -  \x00\x00\x00\x8bM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0	\xb2\x95\x83}\x00}\x8c\x9d\x88\x84\x13%\xf5\xc6\xb0cq\xb3[N\x8a+\x1a\x83\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00+\x00\x00\x00\xac\x00\x00\x00+E\x009c6fd0350a6c0d0c49d4a9c5017cf07043f54e58 (esc)
> -  \x00\x00\x00\x8b6[\x93\xd5\x7f\xdfH\x14\xe2\xb5\x91\x1dk\xac\xff+\x12\x01DA(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xceM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0	\xb2\x95\x83}\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00V\x00\x00\x00V\x00\x00\x00+F\x0022bfcfd62a21a3287edbd4d656218d0f525ed76a (esc)
> -  \x00\x00\x00\x97\x8b\xeeH\xed\xc71\x85A\xfc\x00\x13\xeeA\xb0\x89'j\x8c$\xbf(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xce\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
> +  \x00\x00\x00g\x00\x00\x00h\x00\x00\x00\x01H\x00\x00\x00\x00\x00\x00\x00\x9fn\x1fLG\xec\xb53\xff\xd0\xc8\xe5,\xdc\x88\xaf\xb6\xcd9\xe2\x0cf\xa5\xa0\x18\x17\xfd\xf5#\x9c'8\x02\xb5\xb7a\x8d\x05\x1c\x89\xe4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00f\xa5\xa0\x18\x17\xfd\xf5#\x9c'8\x02\xb5\xb7a\x8d\x05\x1c\x89\xe42\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+D\x00c3f1ca2924c16a19b0656a84900e504e5b0aec2d (esc)
> +  \x00\x00\x00\x9fM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0	\xb2\x95\x83}\x00}\x8c\x9d\x88\x84\x13%\xf5\xc6\xb0cq\xb3[N\x8a+\x1a\x83\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00n\x1fLG\xec\xb53\xff\xd0\xc8\xe5,\xdc\x88\xaf\xb6\xcd9\xe2\x0c\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00+\x00\x00\x00\xac\x00\x00\x00+E\x009c6fd0350a6c0d0c49d4a9c5017cf07043f54e58 (esc)
> +  \x00\x00\x00\x9f6[\x93\xd5\x7f\xdfH\x14\xe2\xb5\x91\x1dk\xac\xff+\x12\x01DA(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xceM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0	\xb2\x95\x83}(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xce\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00+\x00\x00\x00+\x00\x00\x00+E\x009c6fd0350a6c0d0c49d4a9c5017cf07043f54e58 (esc)
> +  \x00\x00\x00\xab\x8b\xeeH\xed\xc71\x85A\xfc\x00\x13\xeeA\xb0\x89'j\x8c$\xbf(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xce\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x006[\x93\xd5\x7f\xdfH\x14\xe2\xb5\x91\x1dk\xac\xff+\x12\x01DA\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
>     \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00+\x00\x00\x00V\x00\x00\x00\x00\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+H\x008500189e74a9e0475e822093bc7db0d631aeb0b4 (esc)
> -  \x00\x00\x00\x00\x00\x00\x00\x05D\x00\x00\x00b\xc3\xf1\xca)$\xc1j\x19\xb0ej\x84\x90\x0ePN[ (esc)
> -  \xec-\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02D (esc)
> -  \x00\x00\x00\x00\x00\x00\x00\x05E\x00\x00\x00b\x9co\xd05 (esc)
> +  \x00\x00\x00\x00\x00\x00\x00\x05D\x00\x00\x00v\xc3\xf1\xca)$\xc1j\x19\xb0ej\x84\x90\x0ePN[ (esc)
> +  \xec-\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02D (esc)
> +  \x00\x00\x00\x00\x00\x00\x00\x05E\x00\x00\x00v\x9co\xd05 (esc)
>     l\r (no-eol) (esc)
> -  \x0cI\xd4\xa9\xc5\x01|\xf0pC\xf5NX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02E (esc)
> -  \x00\x00\x00\x00\x00\x00\x00\x05H\x00\x00\x00b\x85\x00\x18\x9et\xa9\xe0G^\x82 \x93\xbc}\xb0\xd61\xae\xb0\xb4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
> +  \x0cI\xd4\xa9\xc5\x01|\xf0pC\xf5NX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02E (esc)
> +  \x00\x00\x00\x00\x00\x00\x00\x05H\x00\x00\x00v\x85\x00\x18\x9et\xa9\xe0G^\x82 \x93\xbc}\xb0\xd61\xae\xb0\xb4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
>     \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02H (esc)
>     \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
>
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
>

Patch

diff -r 6e8fe07ad948 -r 67ef070167ad mercurial/bundle2.py
--- a/mercurial/bundle2.py	Tue Sep 02 12:11:36 2014 +0200
+++ b/mercurial/bundle2.py	Tue Sep 02 12:40:05 2014 +0200
@@ -820,7 +820,7 @@ 
     # we need to make sure we trigger the creation of a transaction object used
     # for the whole processing scope.
     op.gettransaction()
-    cg = changegroup.cg1unpacker(inpart, 'UN')
+    cg = changegroup.cg2unpacker(inpart, 'UN')
     ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2')
     op.records.add('changegroup', {'return': ret})
     if op.reply is not None:
diff -r 6e8fe07ad948 -r 67ef070167ad mercurial/changegroup.py
--- a/mercurial/changegroup.py	Tue Sep 02 12:11:36 2014 +0200
+++ b/mercurial/changegroup.py	Tue Sep 02 12:40:05 2014 +0200
@@ -13,6 +13,7 @@ 
 import discovery, error, phases, branchmap
 
 _CHANGEGROUPV1_DELTA_HEADER = "20s20s20s20s"
+_CHANGEGROUPV2_DELTA_HEADER = "20s20s20s20s20s"
 
 def readexactly(stream, n):
     '''read n bytes from stream.read and abort if less was available'''
@@ -215,6 +216,15 @@ 
                     pos = next
             yield closechunk()
 
+class cg2unpacker(cg1unpacker):
+    deltaheader = _CHANGEGROUPV2_DELTA_HEADER
+    deltaheadersize = struct.calcsize(deltaheader)
+
+    def _deltaheader(self, headertuple, prevnode):
+        node, p1, p2, deltabase, cs = headertuple
+        return node, p1, p2, deltabase, cs
+
+
 class headerlessfixup(object):
     def __init__(self, fh, h):
         self._h = h
@@ -410,10 +420,13 @@ 
                                         reorder=reorder):
                     yield chunk
 
+    def deltaparent(self, revlog, rev, p1, p2, prev):
+        return prev
+
     def revchunk(self, revlog, rev, prev, linknode):
         node = revlog.node(rev)
         p1, p2 = revlog.parentrevs(rev)
-        base = prev
+        base = self.deltaparent(revlog, rev, p1, p2, prev)
 
         prefix = ''
         if base == nullrev:
@@ -433,6 +446,28 @@ 
         # do nothing with basenode, it is implicitly the previous one in HG10
         return struct.pack(self.deltaheader, node, p1n, p2n, linknode)
 
+class cg2packer(cg1packer):
+
+    deltaheader = _CHANGEGROUPV2_DELTA_HEADER
+
+    def group(self, nodelist, revlog, lookup, units=None, reorder=None):
+        if (revlog._generaldelta and reorder is not True):
+            reorder = False
+        return cg1packer.group(self, nodelist, revlog, lookup,
+                               units=units, reorder=reorder)
+
+    def deltaparent(self, revlog, rev, p1, p2, prev):
+        dp = revlog.deltaparent(rev)
+        #print 'rev %d dp %d p1 %d p2 %d prev %d' % (rev, dp, p1, p2, prev)
+        # avoid storing full revisions; pick prev in those cases
+        # also pick prev when we can't be sure remote has dp
+        if dp == nullrev or (dp != p1 and dp != p2 and dp != prev):
+            return prev
+        return dp
+
+    def builddeltaheader(self, node, p1n, p2n, basenode, linknode):
+        return struct.pack(self.deltaheader, node, p1n, p2n, basenode, linknode)
+
 def _changegroupinfo(repo, nodes, source):
     if repo.ui.verbose or source == 'bundle':
         repo.ui.status(_("%d changesets found\n") % len(nodes))
@@ -524,6 +559,24 @@ 
     outgoing = _computeoutgoing(repo, heads, common)
     return getlocalchangegroup(repo, source, outgoing, bundlecaps=bundlecaps)
 
+def getlocalchangegroup2(repo, source, outgoing, bundlecaps=None):
+    if not outgoing.missing:
+        return None
+    bundler = cg2packer(repo, bundlecaps)
+    repo = repo.unfiltered()
+    commonrevs = outgoing.common
+    csets = outgoing.missing
+    heads = outgoing.missingheads
+    heads.sort()
+    fastpathlinkrev = repo.filtername is None and heads == sorted(repo.heads())
+    repo.hook('preoutgoing', throw=True, source=source)
+    _changegroupinfo(repo, csets, source)
+    return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
+
+def getchangegroup2(repo, source, heads=None, common=None, bundlecaps=None):
+    outgoing = _computeoutgoing(repo, heads, common)
+    return getlocalchangegroup2(repo, source, outgoing, bundlecaps=bundlecaps)
+
 def changegroup(repo, basenodes, source):
     # to avoid a race we use changegroupsubset() (issue1320)
     return changegroupsubset(repo, basenodes, repo.heads(), source)
diff -r 6e8fe07ad948 -r 67ef070167ad mercurial/exchange.py
--- a/mercurial/exchange.py	Tue Sep 02 12:11:36 2014 +0200
+++ b/mercurial/exchange.py	Tue Sep 02 12:40:05 2014 +0200
@@ -401,8 +401,8 @@ 
                                      pushop.outgoing)
     if not pushop.force:
         bundler.newpart('B2X:CHECK:HEADS', data=iter(pushop.remoteheads))
-    cg = changegroup.getlocalchangegroup(pushop.repo, 'push', pushop.outgoing)
-    cgpart = bundler.newpart('B2X:CHANGEGROUP', data=cg.getchunks())
+    cg = changegroup.getlocalchangegroup2(pushop.repo, 'push', pushop.outgoing)
+    cgpart = bundler.newpart('B2X:CHANGEGROUP', data=cg)
     def handlereply(op):
         """extract addchangroup returns from server reply"""
         cgreplies = op.records.getreplies(cgpart.id)
@@ -976,28 +976,27 @@ 
     The implementation is at a very early stage and will get massive rework
     when the API of bundle is refined.
     """
-    cg = None
-    if kwargs.get('cg', True):
-        # build changegroup bundle here.
-        cg = changegroup.getchangegroup(repo, source, heads=heads,
-                                         common=common, bundlecaps=bundlecaps)
-    elif 'HG2X' not in bundlecaps:
-        raise ValueError(_('request for bundle10 must include changegroup'))
     if bundlecaps is None or 'HG2X' not in bundlecaps:
         if kwargs:
             raise ValueError(_('unsupported getbundle arguments: %s')
                              % ', '.join(sorted(kwargs.keys())))
-        return cg
+        return changegroup.getchangegroup(repo, source, heads=heads,
+                                   common=common, bundlecaps=bundlecaps)
+
     # very crude first implementation,
     # the bundle API will change and the generation will be done lazily.
+    cg = None
     b2caps = {}
     for bcaps in bundlecaps:
         if bcaps.startswith('bundle2='):
             blob = urllib.unquote(bcaps[len('bundle2='):])
             b2caps.update(bundle2.decodecaps(blob))
     bundler = bundle2.bundle20(repo.ui, b2caps)
-    if cg:
-        bundler.newpart('b2x:changegroup', data=cg.getchunks())
+    if kwargs.get('cg', True):
+        cg = changegroup.getchangegroup2(repo, source, heads=heads,
+                                   common=common, bundlecaps=bundlecaps)
+        if cg:
+            bundler.newpart('b2x:changegroup', data=cg)
     listkeys = kwargs.get('listkeys', ())
     for namespace in listkeys:
         part = bundler.newpart('b2x:listkeys')
diff -r 6e8fe07ad948 -r 67ef070167ad tests/test-bundle2.t
--- a/tests/test-bundle2.t	Tue Sep 02 12:11:36 2014 +0200
+++ b/tests/test-bundle2.t	Tue Sep 02 12:40:05 2014 +0200
@@ -106,8 +106,8 @@ 
   >             headmissing = [c.node() for c in repo.set('heads(%ld)', revs)]
   >             headcommon  = [c.node() for c in repo.set('parents(%ld) - %ld', revs, revs)]
   >             outgoing = discovery.outgoing(repo.changelog, headcommon, headmissing)
-  >             cg = changegroup.getlocalchangegroup(repo, 'test:bundle2', outgoing, None)
-  >             bundler.newpart('b2x:changegroup', data=cg.getchunks())
+  >             cg = changegroup.getlocalchangegroup2(repo, 'test:bundle2', outgoing, None)
+  >             bundler.newpart('b2x:changegroup', data=cg)
   > 
   >     if opts['parts']:
   >        bundler.newpart('test:empty')
@@ -719,26 +719,26 @@ 
   end of bundle
 
   $ cat ../rev.hg2
-  HG2X\x00\x00\x00\x16\x0fb2x:changegroup\x00\x00\x00\x00\x00\x00\x00\x00\x06\x13\x00\x00\x00\xa42\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j_\xdd\xd9\x89W\xc8\xa5JMCm\xfe\x1d\xa9\xd8\x7f!\xa1\xb9{\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c (esc)
+  HG2X\x00\x00\x00\x16\x0fb2x:changegroup\x00\x00\x00\x00\x00\x00\x00\x00\x06\xef\x00\x00\x00\xb82\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j_\xdd\xd9\x89W\xc8\xa5JMCm\xfe\x1d\xa9\xd8\x7f!\xa1\xb9{\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00_\xdd\xd9\x89W\xc8\xa5JMCm\xfe\x1d\xa9\xd8\x7f!\xa1\xb9{2\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)6e1f4c47ecb533ffd0c8e52cdc88afb6cd39e20c (esc)
   \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x02D (esc)
-  \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01D\x00\x00\x00\xa4\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xcd\x01\x0b\x8c\xd9\x98\xf3\x98\x1aZ\x81\x15\xf9O\x8d\xa4\xabP`\x89\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)4dece9c826f69490507b98c6383a3009b295837d (esc)
+  \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01D\x00\x00\x00\xb8\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xcd\x01\x0b\x8c\xd9\x98\xf3\x98\x1aZ\x81\x15\xf9O\x8d\xa4\xabP`\x89\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)4dece9c826f69490507b98c6383a3009b295837d (esc)
   \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x02E (esc)
-  \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01E\x00\x00\x00\xa2\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)365b93d57fdf4814e2b5911d6bacff2b12014441 (esc)
-  \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x00\x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01G\x00\x00\x00\xa4\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
-  \x87\xcd\xc9n\x8e\xaa\xb6$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
+  \x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01E\x00\x00\x00\xb6\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)365b93d57fdf4814e2b5911d6bacff2b12014441 (esc)
+  \x00\x00\x00f\x00\x00\x00h\x00\x00\x00\x00\x00\x00\x00i\x00\x00\x00j\x00\x00\x00\x01G\x00\x00\x00\xb8\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
+  \x87\xcd\xc9n\x8e\xaa\xb6$\xb68|\x8c\x8c\xae7\x17\x88\x80\xf3\xfa\x95\xde\xd3\xcb\x1c\xf7\x85\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
   \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00\x00\x00\x00\x00)\x00\x00\x00)8bee48edc7318541fc0013ee41b089276a8c24bf (esc)
   \x00\x00\x00f\x00\x00\x00f\x00\x00\x00\x02H (esc)
-  \x00\x00\x00g\x00\x00\x00h\x00\x00\x00\x01H\x00\x00\x00\x00\x00\x00\x00\x8bn\x1fLG\xec\xb53\xff\xd0\xc8\xe5,\xdc\x88\xaf\xb6\xcd9\xe2\x0cf\xa5\xa0\x18\x17\xfd\xf5#\x9c'8\x02\xb5\xb7a\x8d\x05\x1c\x89\xe4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+D\x00c3f1ca2924c16a19b0656a84900e504e5b0aec2d (esc)
-  \x00\x00\x00\x8bM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0	\xb2\x95\x83}\x00}\x8c\x9d\x88\x84\x13%\xf5\xc6\xb0cq\xb3[N\x8a+\x1a\x83\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00+\x00\x00\x00\xac\x00\x00\x00+E\x009c6fd0350a6c0d0c49d4a9c5017cf07043f54e58 (esc)
-  \x00\x00\x00\x8b6[\x93\xd5\x7f\xdfH\x14\xe2\xb5\x91\x1dk\xac\xff+\x12\x01DA(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xceM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0	\xb2\x95\x83}\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00V\x00\x00\x00V\x00\x00\x00+F\x0022bfcfd62a21a3287edbd4d656218d0f525ed76a (esc)
-  \x00\x00\x00\x97\x8b\xeeH\xed\xc71\x85A\xfc\x00\x13\xeeA\xb0\x89'j\x8c$\xbf(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xce\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
+  \x00\x00\x00g\x00\x00\x00h\x00\x00\x00\x01H\x00\x00\x00\x00\x00\x00\x00\x9fn\x1fLG\xec\xb53\xff\xd0\xc8\xe5,\xdc\x88\xaf\xb6\xcd9\xe2\x0cf\xa5\xa0\x18\x17\xfd\xf5#\x9c'8\x02\xb5\xb7a\x8d\x05\x1c\x89\xe4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00f\xa5\xa0\x18\x17\xfd\xf5#\x9c'8\x02\xb5\xb7a\x8d\x05\x1c\x89\xe42\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+D\x00c3f1ca2924c16a19b0656a84900e504e5b0aec2d (esc)
+  \x00\x00\x00\x9fM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0	\xb2\x95\x83}\x00}\x8c\x9d\x88\x84\x13%\xf5\xc6\xb0cq\xb3[N\x8a+\x1a\x83\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00n\x1fLG\xec\xb53\xff\xd0\xc8\xe5,\xdc\x88\xaf\xb6\xcd9\xe2\x0c\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00+\x00\x00\x00\xac\x00\x00\x00+E\x009c6fd0350a6c0d0c49d4a9c5017cf07043f54e58 (esc)
+  \x00\x00\x00\x9f6[\x93\xd5\x7f\xdfH\x14\xe2\xb5\x91\x1dk\xac\xff+\x12\x01DA(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xceM\xec\xe9\xc8&\xf6\x94\x90P{\x98\xc68:0	\xb2\x95\x83}(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xce\xee\xa17Fy\x9a\x9e\x0b\xfd\x88\xf2\x9d<.\x9d\xc98\x9fRO\x00\x00\x00+\x00\x00\x00+\x00\x00\x00+E\x009c6fd0350a6c0d0c49d4a9c5017cf07043f54e58 (esc)
+  \x00\x00\x00\xab\x8b\xeeH\xed\xc71\x85A\xfc\x00\x13\xeeA\xb0\x89'j\x8c$\xbf(\xa5\x84\xc6^\xf1!\xf8\x9e\xb6j\xb7\xd0\xbc\x15=\x80\x99\xe7\xce\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x006[\x93\xd5\x7f\xdfH\x14\xe2\xb5\x91\x1dk\xac\xff+\x12\x01DA\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
   \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00+\x00\x00\x00V\x00\x00\x00\x00\x00\x00\x00\x81\x00\x00\x00\x81\x00\x00\x00+H\x008500189e74a9e0475e822093bc7db0d631aeb0b4 (esc)
-  \x00\x00\x00\x00\x00\x00\x00\x05D\x00\x00\x00b\xc3\xf1\xca)$\xc1j\x19\xb0ej\x84\x90\x0ePN[ (esc)
-  \xec-\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02D (esc)
-  \x00\x00\x00\x00\x00\x00\x00\x05E\x00\x00\x00b\x9co\xd05 (esc)
+  \x00\x00\x00\x00\x00\x00\x00\x05D\x00\x00\x00v\xc3\xf1\xca)$\xc1j\x19\xb0ej\x84\x90\x0ePN[ (esc)
+  \xec-\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x002\xafv\x86\xd4\x03\xcfE\xb5\xd9_-p\xce\xbe\xa5\x87\xac\x80j\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02D (esc)
+  \x00\x00\x00\x00\x00\x00\x00\x05E\x00\x00\x00v\x9co\xd05 (esc)
   l\r (no-eol) (esc)
-  \x0cI\xd4\xa9\xc5\x01|\xf0pC\xf5NX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02E (esc)
-  \x00\x00\x00\x00\x00\x00\x00\x05H\x00\x00\x00b\x85\x00\x18\x9et\xa9\xe0G^\x82 \x93\xbc}\xb0\xd61\xae\xb0\xb4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
+  \x0cI\xd4\xa9\xc5\x01|\xf0pC\xf5NX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x95 \xee\xa7\x81\xbc\xca\x16\xc1\xe1Z\xcc\x0b\xa1C5\xa0\xe8\xe5\xba\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02E (esc)
+  \x00\x00\x00\x00\x00\x00\x00\x05H\x00\x00\x00v\x85\x00\x18\x9et\xa9\xe0G^\x82 \x93\xbc}\xb0\xd61\xae\xb0\xb4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xdeB\x19n\xbe\xe4.\xf2\x84\xb6x (esc)
   \x87\xcd\xc9n\x8e\xaa\xb6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02H (esc)
   \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)