changeset 22660:978cce51cc5f

bundle2: split test in two We split the test between the one dedicated to the binary format and the one dedicated to checking the exchange of data using pull and push.
author Pierre-Yves David <pierre-yves.david@fb.com>
date Wed, 01 Oct 2014 23:55:22 -0500
parents 798185707833
children 9ea2913e7c41
files tests/test-bundle2-exchange.t tests/test-bundle2-format.t tests/test-bundle2.t
diffstat 3 files changed, 1239 insertions(+), 1193 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-bundle2-exchange.t	Wed Oct 01 23:55:22 2014 -0500
@@ -0,0 +1,470 @@
+Test exchange of common information using bundle2
+
+
+  $ getmainid() {
+  >    hg -R main log --template '{node}\n' --rev "$1"
+  > }
+
+enable obsolescence
+
+  $ cat > obs.py << EOF
+  > import mercurial.obsolete
+  > mercurial.obsolete._enabled = True
+  > EOF
+
+  $ cat >> $HGRCPATH << EOF
+  > [extensions]
+  > obsolete=$TESTTMP/obs.py
+  > [experimental]
+  > bundle2-exp=True
+  > [ui]
+  > ssh=python "$TESTDIR/dummyssh"
+  > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
+  > [web]
+  > push_ssl = false
+  > allow_push = *
+  > [phases]
+  > publish=False
+  > EOF
+
+The extension requires a repo (currently unused)
+
+  $ hg init main
+  $ cd main
+  $ touch a
+  $ hg add a
+  $ hg commit -m 'a'
+
+  $ hg unbundle $TESTDIR/bundles/rebase.hg
+  adding changesets
+  adding manifests
+  adding file changes
+  added 8 changesets with 7 changes to 7 files (+3 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+
+  $ cd ..
+
+Real world exchange
+=====================
+
+Add more obsolescence information
+
+  $ hg -R main debugobsolete -d '0 0' 1111111111111111111111111111111111111111 `getmainid 9520eea781bc`
+  $ hg -R main debugobsolete -d '0 0' 2222222222222222222222222222222222222222 `getmainid 24b6387c8c8c`
+
+clone --pull
+
+  $ hg -R main phase --public cd010b8cd998
+  $ hg clone main other --pull --rev 9520eea781bc
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 2 files
+  1 new obsolescence markers
+  updating to branch default
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg -R other log -G
+  @  1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com>  E
+  |
+  o  0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com>  A
+  
+  $ hg -R other debugobsolete
+  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+
+pull
+
+  $ hg -R main phase --public 9520eea781bc
+  $ hg -R other pull -r 24b6387c8c8c
+  pulling from $TESTTMP/main (glob)
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  1 new obsolescence markers
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg -R other log -G
+  o  2:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com>  F
+  |
+  | @  1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com>  E
+  |/
+  o  0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com>  A
+  
+  $ hg -R other debugobsolete
+  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+
+pull empty (with phase movement)
+
+  $ hg -R main phase --public 24b6387c8c8c
+  $ hg -R other pull -r 24b6387c8c8c
+  pulling from $TESTTMP/main (glob)
+  no changes found
+  $ hg -R other log -G
+  o  2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com>  F
+  |
+  | @  1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com>  E
+  |/
+  o  0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com>  A
+  
+  $ hg -R other debugobsolete
+  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+
+pull empty
+
+  $ hg -R other pull -r 24b6387c8c8c
+  pulling from $TESTTMP/main (glob)
+  no changes found
+  $ hg -R other log -G
+  o  2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com>  F
+  |
+  | @  1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com>  E
+  |/
+  o  0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com>  A
+  
+  $ hg -R other debugobsolete
+  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+
+add extra data to test their exchange during push
+
+  $ hg -R main bookmark --rev eea13746799a book_eea1
+  $ hg -R main debugobsolete -d '0 0' 3333333333333333333333333333333333333333 `getmainid eea13746799a`
+  $ hg -R main bookmark --rev 02de42196ebe book_02de
+  $ hg -R main debugobsolete -d '0 0' 4444444444444444444444444444444444444444 `getmainid 02de42196ebe`
+  $ hg -R main bookmark --rev 42ccdea3bb16 book_42cc
+  $ hg -R main debugobsolete -d '0 0' 5555555555555555555555555555555555555555 `getmainid 42ccdea3bb16`
+  $ hg -R main bookmark --rev 5fddd98957c8 book_5fdd
+  $ hg -R main debugobsolete -d '0 0' 6666666666666666666666666666666666666666 `getmainid 5fddd98957c8`
+  $ hg -R main bookmark --rev 32af7686d403 book_32af
+  $ hg -R main debugobsolete -d '0 0' 7777777777777777777777777777777777777777 `getmainid 32af7686d403`
+
+  $ hg -R other bookmark --rev cd010b8cd998 book_eea1
+  $ hg -R other bookmark --rev cd010b8cd998 book_02de
+  $ hg -R other bookmark --rev cd010b8cd998 book_42cc
+  $ hg -R other bookmark --rev cd010b8cd998 book_5fdd
+  $ hg -R other bookmark --rev cd010b8cd998 book_32af
+
+  $ hg -R main phase --public eea13746799a
+
+push
+  $ hg -R main push other --rev eea13746799a --bookmark book_eea1
+  pushing to other
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 0 changes to 0 files (-1 heads)
+  remote: 1 new obsolescence markers
+  updating bookmark book_eea1
+  $ hg -R other log -G
+  o    3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
+  |\
+  | o  2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com>  F
+  | |
+  @ |  1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com>  E
+  |/
+  o  0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de book_32af book_42cc book_5fdd A
+  
+  $ hg -R other debugobsolete
+  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+
+pull over ssh
+
+  $ hg -R other pull ssh://user@dummy/main -r 02de42196ebe --bookmark book_02de
+  pulling from ssh://user@dummy/main
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  1 new obsolescence markers
+  updating bookmark book_02de
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+  $ hg -R other debugobsolete
+  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+
+pull over http
+
+  $ hg -R main serve -p $HGPORT -d --pid-file=main.pid -E main-error.log
+  $ cat main.pid >> $DAEMON_PIDS
+
+  $ hg -R other pull http://localhost:$HGPORT/ -r 42ccdea3bb16 --bookmark book_42cc
+  pulling from http://localhost:$HGPORT/
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  1 new obsolescence markers
+  updating bookmark book_42cc
+  (run 'hg heads .' to see heads, 'hg merge' to merge)
+  $ cat main-error.log
+  $ hg -R other debugobsolete
+  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+
+push over ssh
+
+  $ hg -R main push ssh://user@dummy/other -r 5fddd98957c8 --bookmark book_5fdd
+  pushing to ssh://user@dummy/other
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files
+  remote: 1 new obsolescence markers
+  updating bookmark book_5fdd
+  $ hg -R other log -G
+  o  6:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
+  |
+  o  5:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
+  |
+  | o  4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
+  | |
+  | | o  3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
+  | |/|
+  | o |  2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com>  F
+  |/ /
+  | @  1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com>  E
+  |/
+  o  0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af A
+  
+  $ hg -R other debugobsolete
+  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+
+push over http
+
+  $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
+  $ cat other.pid >> $DAEMON_PIDS
+
+  $ hg -R main phase --public 32af7686d403
+  $ hg -R main push http://localhost:$HGPORT2/ -r 32af7686d403 --bookmark book_32af
+  pushing to http://localhost:$HGPORT2/
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files
+  remote: 1 new obsolescence markers
+  updating bookmark book_32af
+  $ cat other-error.log
+
+Check final content.
+
+  $ hg -R other log -G
+  o  7:32af7686d403 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af D
+  |
+  o  6:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
+  |
+  o  5:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
+  |
+  | o  4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
+  | |
+  | | o  3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
+  | |/|
+  | o |  2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com>  F
+  |/ /
+  | @  1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com>  E
+  |/
+  o  0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com>  A
+  
+  $ hg -R other debugobsolete
+  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+  7777777777777777777777777777777777777777 32af7686d403cf45b5d95f2d70cebea587ac806a 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
+
+Error Handling
+==============
+
+Check that errors are properly returned to the client during push.
+
+Setting up
+
+  $ cat > failpush.py << EOF
+  > """A small extension that makes push fails when using bundle2
+  > 
+  > used to test error handling in bundle2
+  > """
+  > 
+  > from mercurial import util
+  > from mercurial import bundle2
+  > from mercurial import exchange
+  > from mercurial import extensions
+  > 
+  > def _pushbundle2failpart(pushop, bundler):
+  >     reason = pushop.ui.config('failpush', 'reason', None)
+  >     part = None
+  >     if reason == 'abort':
+  >         bundler.newpart('test:abort')
+  >     if reason == 'unknown':
+  >         bundler.newpart('TEST:UNKNOWN')
+  >     if reason == 'race':
+  >         # 20 Bytes of crap
+  >         bundler.newpart('b2x:check:heads', data='01234567890123456789')
+  > 
+  > @bundle2.parthandler("test:abort")
+  > def handleabort(op, part):
+  >     raise util.Abort('Abandon ship!', hint="don't panic")
+  > 
+  > def uisetup(ui):
+  >     exchange.b2partsgenmapping['failpart'] = _pushbundle2failpart
+  >     exchange.b2partsgenorder.insert(0, 'failpart')
+  > 
+  > EOF
+
+  $ cd main
+  $ hg up tip
+  3 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  $ echo 'I' > I
+  $ hg add I
+  $ hg ci -m 'I'
+  $ hg id
+  e7ec4e813ba6 tip
+  $ cd ..
+
+  $ cat << EOF >> $HGRCPATH
+  > [extensions]
+  > failpush=$TESTTMP/failpush.py
+  > EOF
+
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
+  $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
+  $ cat other.pid >> $DAEMON_PIDS
+
+Doing the actual push: Abort error
+
+  $ cat << EOF >> $HGRCPATH
+  > [failpush]
+  > reason = abort
+  > EOF
+
+  $ hg -R main push other -r e7ec4e813ba6
+  pushing to other
+  searching for changes
+  abort: Abandon ship!
+  (don't panic)
+  [255]
+
+  $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
+  pushing to ssh://user@dummy/other
+  searching for changes
+  abort: Abandon ship!
+  (don't panic)
+  [255]
+
+  $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
+  pushing to http://localhost:$HGPORT2/
+  searching for changes
+  abort: Abandon ship!
+  (don't panic)
+  [255]
+
+
+Doing the actual push: unknown mandatory parts
+
+  $ cat << EOF >> $HGRCPATH
+  > [failpush]
+  > reason = unknown
+  > EOF
+
+  $ hg -R main push other -r e7ec4e813ba6
+  pushing to other
+  searching for changes
+  abort: missing support for test:unknown
+  [255]
+
+  $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
+  pushing to ssh://user@dummy/other
+  searching for changes
+  abort: missing support for test:unknown
+  [255]
+
+  $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
+  pushing to http://localhost:$HGPORT2/
+  searching for changes
+  abort: missing support for test:unknown
+  [255]
+
+Doing the actual push: race
+
+  $ cat << EOF >> $HGRCPATH
+  > [failpush]
+  > reason = race
+  > EOF
+
+  $ hg -R main push other -r e7ec4e813ba6
+  pushing to other
+  searching for changes
+  abort: push failed:
+  'repository changed while pushing - please try again'
+  [255]
+
+  $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
+  pushing to ssh://user@dummy/other
+  searching for changes
+  abort: push failed:
+  'repository changed while pushing - please try again'
+  [255]
+
+  $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
+  pushing to http://localhost:$HGPORT2/
+  searching for changes
+  abort: push failed:
+  'repository changed while pushing - please try again'
+  [255]
+
+Doing the actual push: hook abort
+
+  $ cat << EOF >> $HGRCPATH
+  > [failpush]
+  > reason =
+  > [hooks]
+  > b2x-pretransactionclose.failpush = false
+  > EOF
+
+  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
+  $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
+  $ cat other.pid >> $DAEMON_PIDS
+
+  $ hg -R main push other -r e7ec4e813ba6
+  pushing to other
+  searching for changes
+  transaction abort!
+  rollback completed
+  abort: b2x-pretransactionclose.failpush hook exited with status 1
+  [255]
+
+  $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
+  pushing to ssh://user@dummy/other
+  searching for changes
+  abort: b2x-pretransactionclose.failpush hook exited with status 1
+  remote: transaction abort!
+  remote: rollback completed
+  [255]
+
+  $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
+  pushing to http://localhost:$HGPORT2/
+  searching for changes
+  abort: b2x-pretransactionclose.failpush hook exited with status 1
+  [255]
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-bundle2-format.t	Wed Oct 01 23:55:22 2014 -0500
@@ -0,0 +1,769 @@
+This test is decicated to test the bundle2 container format
+
+It test multiple existing parts to test different feature of the container. You
+probably do not need to touch this test unless you change the binary encoding
+of the bundle2 format itself.
+
+Create an extension to test bundle2 API
+
+  $ cat > bundle2.py << EOF
+  > """A small extension to test bundle2 implementation
+  > 
+  > Current bundle2 implementation is far too limited to be used in any core
+  > code. We still need to be able to test it while it grow up.
+  > """
+  > 
+  > import sys, os
+  > from mercurial import cmdutil
+  > from mercurial import util
+  > from mercurial import bundle2
+  > from mercurial import scmutil
+  > from mercurial import discovery
+  > from mercurial import changegroup
+  > from mercurial import error
+  > from mercurial import obsolete
+  > 
+  > obsolete._enabled = True
+  > 
+  > try:
+  >     import msvcrt
+  >     msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
+  >     msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
+  >     msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
+  > except ImportError:
+  >     pass
+  > 
+  > cmdtable = {}
+  > command = cmdutil.command(cmdtable)
+  > 
+  > ELEPHANTSSONG = """Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
+  > Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
+  > Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko."""
+  > assert len(ELEPHANTSSONG) == 178 # future test say 178 bytes, trust it.
+  > 
+  > @bundle2.parthandler('test:song')
+  > def songhandler(op, part):
+  >     """handle a "test:song" bundle2 part, printing the lyrics on stdin"""
+  >     op.ui.write('The choir starts singing:\n')
+  >     verses = 0
+  >     for line in part.read().split('\n'):
+  >         op.ui.write('    %s\n' % line)
+  >         verses += 1
+  >     op.records.add('song', {'verses': verses})
+  > 
+  > @bundle2.parthandler('test:ping')
+  > def pinghandler(op, part):
+  >     op.ui.write('received ping request (id %i)\n' % part.id)
+  >     if op.reply is not None and 'ping-pong' in op.reply.capabilities:
+  >         op.ui.write_err('replying to ping request (id %i)\n' % part.id)
+  >         op.reply.newpart('test:pong', [('in-reply-to', str(part.id))])
+  > 
+  > @bundle2.parthandler('test:debugreply')
+  > def debugreply(op, part):
+  >     """print data about the capacity of the bundle reply"""
+  >     if op.reply is None:
+  >         op.ui.write('debugreply: no reply\n')
+  >     else:
+  >         op.ui.write('debugreply: capabilities:\n')
+  >         for cap in sorted(op.reply.capabilities):
+  >             op.ui.write('debugreply:     %r\n' % cap)
+  >             for val in op.reply.capabilities[cap]:
+  >                 op.ui.write('debugreply:         %r\n' % val)
+  > 
+  > @command('bundle2',
+  >          [('', 'param', [], 'stream level parameter'),
+  >           ('', 'unknown', False, 'include an unknown mandatory part in the bundle'),
+  >           ('', 'unknownparams', False, 'include an unknown part parameters in the bundle'),
+  >           ('', 'parts', False, 'include some arbitrary parts to the bundle'),
+  >           ('', 'reply', False, 'produce a reply bundle'),
+  >           ('', 'pushrace', False, 'includes a check:head part with unknown nodes'),
+  >           ('r', 'rev', [], 'includes those changeset in the bundle'),],
+  >          '[OUTPUTFILE]')
+  > def cmdbundle2(ui, repo, path=None, **opts):
+  >     """write a bundle2 container on standard ouput"""
+  >     bundler = bundle2.bundle20(ui)
+  >     for p in opts['param']:
+  >         p = p.split('=', 1)
+  >         try:
+  >             bundler.addparam(*p)
+  >         except ValueError, exc:
+  >             raise util.Abort('%s' % exc)
+  > 
+  >     if opts['reply']:
+  >         capsstring = 'ping-pong\nelephants=babar,celeste\ncity%3D%21=celeste%2Cville'
+  >         bundler.newpart('b2x:replycaps', data=capsstring)
+  > 
+  >     if opts['pushrace']:
+  >         # also serve to test the assignement of data outside of init
+  >         part = bundler.newpart('b2x:check:heads')
+  >         part.data = '01234567890123456789'
+  > 
+  >     revs = opts['rev']
+  >     if 'rev' in opts:
+  >         revs = scmutil.revrange(repo, opts['rev'])
+  >         if revs:
+  >             # very crude version of a changegroup part creation
+  >             bundled = repo.revs('%ld::%ld', revs, revs)
+  >             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())
+  > 
+  >     if opts['parts']:
+  >        bundler.newpart('test:empty')
+  >        # add a second one to make sure we handle multiple parts
+  >        bundler.newpart('test:empty')
+  >        bundler.newpart('test:song', data=ELEPHANTSSONG)
+  >        bundler.newpart('test:debugreply')
+  >        mathpart = bundler.newpart('test:math')
+  >        mathpart.addparam('pi', '3.14')
+  >        mathpart.addparam('e', '2.72')
+  >        mathpart.addparam('cooking', 'raw', mandatory=False)
+  >        mathpart.data = '42'
+  >        # advisory known part with unknown mandatory param
+  >        bundler.newpart('test:song', [('randomparam','')])
+  >     if opts['unknown']:
+  >        bundler.newpart('test:UNKNOWN', data='some random content')
+  >     if opts['unknownparams']:
+  >        bundler.newpart('test:SONG', [('randomparams', '')])
+  >     if opts['parts']:
+  >        bundler.newpart('test:ping')
+  > 
+  >     if path is None:
+  >        file = sys.stdout
+  >     else:
+  >         file = open(path, 'wb')
+  > 
+  >     for chunk in bundler.getchunks():
+  >         file.write(chunk)
+  > 
+  > @command('unbundle2', [], '')
+  > def cmdunbundle2(ui, repo, replypath=None):
+  >     """process a bundle2 stream from stdin on the current repo"""
+  >     try:
+  >         tr = None
+  >         lock = repo.lock()
+  >         tr = repo.transaction('processbundle')
+  >         try:
+  >             unbundler = bundle2.unbundle20(ui, sys.stdin)
+  >             op = bundle2.processbundle(repo, unbundler, lambda: tr)
+  >             tr.close()
+  >         except error.BundleValueError, exc:
+  >             raise util.Abort('missing support for %s' % exc)
+  >         except error.PushRaced, exc:
+  >             raise util.Abort('push race: %s' % exc)
+  >     finally:
+  >         if tr is not None:
+  >             tr.release()
+  >         lock.release()
+  >         remains = sys.stdin.read()
+  >         ui.write('%i unread bytes\n' % len(remains))
+  >     if op.records['song']:
+  >         totalverses = sum(r['verses'] for r in op.records['song'])
+  >         ui.write('%i total verses sung\n' % totalverses)
+  >     for rec in op.records['changegroup']:
+  >         ui.write('addchangegroup return: %i\n' % rec['return'])
+  >     if op.reply is not None and replypath is not None:
+  >         file = open(replypath, 'wb')
+  >         for chunk in op.reply.getchunks():
+  >             file.write(chunk)
+  > 
+  > @command('statbundle2', [], '')
+  > def cmdstatbundle2(ui, repo):
+  >     """print statistic on the bundle2 container read from stdin"""
+  >     unbundler = bundle2.unbundle20(ui, sys.stdin)
+  >     try:
+  >         params = unbundler.params
+  >     except error.BundleValueError, exc:
+  >        raise util.Abort('unknown parameters: %s' % exc)
+  >     ui.write('options count: %i\n' % len(params))
+  >     for key in sorted(params):
+  >         ui.write('- %s\n' % key)
+  >         value = params[key]
+  >         if value is not None:
+  >             ui.write('    %s\n' % value)
+  >     count = 0
+  >     for p in unbundler.iterparts():
+  >         count += 1
+  >         ui.write('  :%s:\n' % p.type)
+  >         ui.write('    mandatory: %i\n' % len(p.mandatoryparams))
+  >         ui.write('    advisory: %i\n' % len(p.advisoryparams))
+  >         ui.write('    payload: %i bytes\n' % len(p.read()))
+  >     ui.write('parts count:   %i\n' % count)
+  > EOF
+  $ cat >> $HGRCPATH << EOF
+  > [extensions]
+  > bundle2=$TESTTMP/bundle2.py
+  > [experimental]
+  > bundle2-exp=True
+  > [ui]
+  > ssh=python "$TESTDIR/dummyssh"
+  > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
+  > [web]
+  > push_ssl = false
+  > allow_push = *
+  > [phases]
+  > publish=False
+  > EOF
+
+The extension requires a repo (currently unused)
+
+  $ hg init main
+  $ cd main
+  $ touch a
+  $ hg add a
+  $ hg commit -m 'a'
+
+
+Empty bundle
+=================
+
+- no option
+- no parts
+
+Test bundling
+
+  $ hg bundle2
+  HG2X\x00\x00\x00\x00 (no-eol) (esc)
+
+Test unbundling
+
+  $ hg bundle2 | hg statbundle2
+  options count: 0
+  parts count:   0
+
+Test old style bundle are detected and refused
+
+  $ hg bundle --all ../bundle.hg
+  1 changesets found
+  $ hg statbundle2 < ../bundle.hg
+  abort: unknown bundle version 10
+  [255]
+
+Test parameters
+=================
+
+- some options
+- no parts
+
+advisory parameters, no value
+-------------------------------
+
+Simplest possible parameters form
+
+Test generation simple option
+
+  $ hg bundle2 --param 'caution'
+  HG2X\x00\x07caution\x00\x00 (no-eol) (esc)
+
+Test unbundling
+
+  $ hg bundle2 --param 'caution' | hg statbundle2
+  options count: 1
+  - caution
+  parts count:   0
+
+Test generation multiple option
+
+  $ hg bundle2 --param 'caution' --param 'meal'
+  HG2X\x00\x0ccaution meal\x00\x00 (no-eol) (esc)
+
+Test unbundling
+
+  $ hg bundle2 --param 'caution' --param 'meal' | hg statbundle2
+  options count: 2
+  - caution
+  - meal
+  parts count:   0
+
+advisory parameters, with value
+-------------------------------
+
+Test generation
+
+  $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants'
+  HG2X\x00\x1ccaution meal=vegan elephants\x00\x00 (no-eol) (esc)
+
+Test unbundling
+
+  $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | hg statbundle2
+  options count: 3
+  - caution
+  - elephants
+  - meal
+      vegan
+  parts count:   0
+
+parameter with special char in value
+---------------------------------------------------
+
+Test generation
+
+  $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple
+  HG2X\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
+
+Test unbundling
+
+  $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | hg statbundle2
+  options count: 2
+  - e|! 7/
+      babar%#==tutu
+  - simple
+  parts count:   0
+
+Test unknown mandatory option
+---------------------------------------------------
+
+  $ hg bundle2 --param 'Gravity' | hg statbundle2
+  abort: unknown parameters: Stream Parameter - Gravity
+  [255]
+
+Test debug output
+---------------------------------------------------
+
+bundling debug
+
+  $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2
+  start emission of HG2X stream
+  bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
+  start of parts
+  end of bundle
+
+file content is ok
+
+  $ cat ../out.hg2
+  HG2X\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
+
+unbundling debug
+
+  $ hg statbundle2 --debug < ../out.hg2
+  start processing of HG2X stream
+  reading bundle2 stream parameters
+  ignoring unknown parameter 'e|! 7/'
+  ignoring unknown parameter 'simple'
+  options count: 2
+  - e|! 7/
+      babar%#==tutu
+  - simple
+  start extraction of bundle2 parts
+  part header size: 0
+  end of bundle2 stream
+  parts count:   0
+
+
+Test buggy input
+---------------------------------------------------
+
+empty parameter name
+
+  $ hg bundle2 --param '' --quiet
+  abort: empty parameter name
+  [255]
+
+bad parameter name
+
+  $ hg bundle2 --param 42babar
+  abort: non letter first character: '42babar'
+  [255]
+
+
+Test part
+=================
+
+  $ hg bundle2 --parts ../parts.hg2 --debug
+  start emission of HG2X stream
+  bundle parameter: 
+  start of parts
+  bundle part: "test:empty"
+  bundle part: "test:empty"
+  bundle part: "test:song"
+  bundle part: "test:debugreply"
+  bundle part: "test:math"
+  bundle part: "test:song"
+  bundle part: "test:ping"
+  end of bundle
+
+  $ cat ../parts.hg2
+  HG2X\x00\x00\x00\x11 (esc)
+  test:empty\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11 (esc)
+  test:empty\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x10	test:song\x00\x00\x00\x02\x00\x00\x00\x00\x00\xb2Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko (esc)
+  Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
+  Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.\x00\x00\x00\x00\x00\x16\x0ftest:debugreply\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00+	test:math\x00\x00\x00\x04\x02\x01\x02\x04\x01\x04\x07\x03pi3.14e2.72cookingraw\x00\x00\x00\x0242\x00\x00\x00\x00\x00\x1d	test:song\x00\x00\x00\x05\x01\x00\x0b\x00randomparam\x00\x00\x00\x00\x00\x10	test:ping\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
+
+
+  $ hg statbundle2 < ../parts.hg2
+  options count: 0
+    :test:empty:
+      mandatory: 0
+      advisory: 0
+      payload: 0 bytes
+    :test:empty:
+      mandatory: 0
+      advisory: 0
+      payload: 0 bytes
+    :test:song:
+      mandatory: 0
+      advisory: 0
+      payload: 178 bytes
+    :test:debugreply:
+      mandatory: 0
+      advisory: 0
+      payload: 0 bytes
+    :test:math:
+      mandatory: 2
+      advisory: 1
+      payload: 2 bytes
+    :test:song:
+      mandatory: 1
+      advisory: 0
+      payload: 0 bytes
+    :test:ping:
+      mandatory: 0
+      advisory: 0
+      payload: 0 bytes
+  parts count:   7
+
+  $ hg statbundle2 --debug < ../parts.hg2
+  start processing of HG2X stream
+  reading bundle2 stream parameters
+  options count: 0
+  start extraction of bundle2 parts
+  part header size: 17
+  part type: "test:empty"
+  part id: "0"
+  part parameters: 0
+    :test:empty:
+      mandatory: 0
+      advisory: 0
+  payload chunk size: 0
+      payload: 0 bytes
+  part header size: 17
+  part type: "test:empty"
+  part id: "1"
+  part parameters: 0
+    :test:empty:
+      mandatory: 0
+      advisory: 0
+  payload chunk size: 0
+      payload: 0 bytes
+  part header size: 16
+  part type: "test:song"
+  part id: "2"
+  part parameters: 0
+    :test:song:
+      mandatory: 0
+      advisory: 0
+  payload chunk size: 178
+  payload chunk size: 0
+      payload: 178 bytes
+  part header size: 22
+  part type: "test:debugreply"
+  part id: "3"
+  part parameters: 0
+    :test:debugreply:
+      mandatory: 0
+      advisory: 0
+  payload chunk size: 0
+      payload: 0 bytes
+  part header size: 43
+  part type: "test:math"
+  part id: "4"
+  part parameters: 3
+    :test:math:
+      mandatory: 2
+      advisory: 1
+  payload chunk size: 2
+  payload chunk size: 0
+      payload: 2 bytes
+  part header size: 29
+  part type: "test:song"
+  part id: "5"
+  part parameters: 1
+    :test:song:
+      mandatory: 1
+      advisory: 0
+  payload chunk size: 0
+      payload: 0 bytes
+  part header size: 16
+  part type: "test:ping"
+  part id: "6"
+  part parameters: 0
+    :test:ping:
+      mandatory: 0
+      advisory: 0
+  payload chunk size: 0
+      payload: 0 bytes
+  part header size: 0
+  end of bundle2 stream
+  parts count:   7
+
+Test actual unbundling of test part
+=======================================
+
+Process the bundle
+
+  $ hg unbundle2 --debug < ../parts.hg2
+  start processing of HG2X stream
+  reading bundle2 stream parameters
+  start extraction of bundle2 parts
+  part header size: 17
+  part type: "test:empty"
+  part id: "0"
+  part parameters: 0
+  ignoring unsupported advisory part test:empty
+  payload chunk size: 0
+  part header size: 17
+  part type: "test:empty"
+  part id: "1"
+  part parameters: 0
+  ignoring unsupported advisory part test:empty
+  payload chunk size: 0
+  part header size: 16
+  part type: "test:song"
+  part id: "2"
+  part parameters: 0
+  found a handler for part 'test:song'
+  The choir starts singing:
+  payload chunk size: 178
+  payload chunk size: 0
+      Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
+      Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
+      Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
+  part header size: 22
+  part type: "test:debugreply"
+  part id: "3"
+  part parameters: 0
+  found a handler for part 'test:debugreply'
+  debugreply: no reply
+  payload chunk size: 0
+  part header size: 43
+  part type: "test:math"
+  part id: "4"
+  part parameters: 3
+  ignoring unsupported advisory part test:math
+  payload chunk size: 2
+  payload chunk size: 0
+  part header size: 29
+  part type: "test:song"
+  part id: "5"
+  part parameters: 1
+  found a handler for part 'test:song'
+  ignoring unsupported advisory part test:song - randomparam
+  payload chunk size: 0
+  part header size: 16
+  part type: "test:ping"
+  part id: "6"
+  part parameters: 0
+  found a handler for part 'test:ping'
+  received ping request (id 6)
+  payload chunk size: 0
+  part header size: 0
+  end of bundle2 stream
+  0 unread bytes
+  3 total verses sung
+
+Unbundle with an unknown mandatory part
+(should abort)
+
+  $ hg bundle2 --parts --unknown ../unknown.hg2
+
+  $ hg unbundle2 < ../unknown.hg2
+  The choir starts singing:
+      Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
+      Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
+      Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
+  debugreply: no reply
+  0 unread bytes
+  abort: missing support for test:unknown
+  [255]
+
+Unbundle with an unknown mandatory part parameters
+(should abort)
+
+  $ hg bundle2 --unknownparams ../unknown.hg2
+
+  $ hg unbundle2 < ../unknown.hg2
+  0 unread bytes
+  abort: missing support for test:song - randomparams
+  [255]
+
+unbundle with a reply
+
+  $ hg bundle2 --parts --reply ../parts-reply.hg2
+  $ hg unbundle2 ../reply.hg2 < ../parts-reply.hg2
+  0 unread bytes
+  3 total verses sung
+
+The reply is a bundle
+
+  $ cat ../reply.hg2
+  HG2X\x00\x00\x00\x1f (esc)
+  b2x:output\x00\x00\x00\x00\x00\x01\x0b\x01in-reply-to3\x00\x00\x00\xd9The choir starts singing: (esc)
+      Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
+      Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
+      Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
+  \x00\x00\x00\x00\x00\x1f (esc)
+  b2x:output\x00\x00\x00\x01\x00\x01\x0b\x01in-reply-to4\x00\x00\x00\xc9debugreply: capabilities: (esc)
+  debugreply:     'city=!'
+  debugreply:         'celeste,ville'
+  debugreply:     'elephants'
+  debugreply:         'babar'
+  debugreply:         'celeste'
+  debugreply:     'ping-pong'
+  \x00\x00\x00\x00\x00\x1e	test:pong\x00\x00\x00\x02\x01\x00\x0b\x01in-reply-to7\x00\x00\x00\x00\x00\x1f (esc)
+  b2x:output\x00\x00\x00\x03\x00\x01\x0b\x01in-reply-to7\x00\x00\x00=received ping request (id 7) (esc)
+  replying to ping request (id 7)
+  \x00\x00\x00\x00\x00\x00 (no-eol) (esc)
+
+The reply is valid
+
+  $ hg statbundle2 < ../reply.hg2
+  options count: 0
+    :b2x:output:
+      mandatory: 0
+      advisory: 1
+      payload: 217 bytes
+    :b2x:output:
+      mandatory: 0
+      advisory: 1
+      payload: 201 bytes
+    :test:pong:
+      mandatory: 1
+      advisory: 0
+      payload: 0 bytes
+    :b2x:output:
+      mandatory: 0
+      advisory: 1
+      payload: 61 bytes
+  parts count:   4
+
+Unbundle the reply to get the output:
+
+  $ hg unbundle2 < ../reply.hg2
+  remote: The choir starts singing:
+  remote:     Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
+  remote:     Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
+  remote:     Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
+  remote: debugreply: capabilities:
+  remote: debugreply:     'city=!'
+  remote: debugreply:         'celeste,ville'
+  remote: debugreply:     'elephants'
+  remote: debugreply:         'babar'
+  remote: debugreply:         'celeste'
+  remote: debugreply:     'ping-pong'
+  remote: received ping request (id 7)
+  remote: replying to ping request (id 7)
+  0 unread bytes
+
+Test push race detection
+
+  $ hg bundle2 --pushrace ../part-race.hg2
+
+  $ hg unbundle2 < ../part-race.hg2
+  0 unread bytes
+  abort: push race: repository changed while pushing - please try again
+  [255]
+
+Support for changegroup
+===================================
+
+  $ hg unbundle $TESTDIR/bundles/rebase.hg
+  adding changesets
+  adding manifests
+  adding file changes
+  added 8 changesets with 7 changes to 7 files (+3 heads)
+  (run 'hg heads' to see heads, 'hg merge' to merge)
+
+  $ hg log -G
+  o  8:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com>  H
+  |
+  | o  7:eea13746799a draft Nicolas Dumazet <nicdumz.commits@gmail.com>  G
+  |/|
+  o |  6:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com>  F
+  | |
+  | o  5:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com>  E
+  |/
+  | o  4:32af7686d403 draft Nicolas Dumazet <nicdumz.commits@gmail.com>  D
+  | |
+  | o  3:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com>  C
+  | |
+  | o  2:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com>  B
+  |/
+  o  1:cd010b8cd998 draft Nicolas Dumazet <nicdumz.commits@gmail.com>  A
+  
+  @  0:3903775176ed draft test  a
+  
+
+  $ hg bundle2 --debug --rev '8+7+5+4' ../rev.hg2
+  4 changesets found
+  list of changesets:
+  32af7686d403cf45b5d95f2d70cebea587ac806a
+  9520eea781bcca16c1e15acc0ba14335a0e8e5ba
+  eea13746799a9e0bfd88f29d3c2e9dc9389f524f
+  02de42196ebee42ef284b6780a87cdc96e8eaab6
+  start emission of HG2X stream
+  bundle parameter: 
+  start of parts
+  bundle part: "b2x:changegroup"
+  bundling: 1/4 changesets (25.00%)
+  bundling: 2/4 changesets (50.00%)
+  bundling: 3/4 changesets (75.00%)
+  bundling: 4/4 changesets (100.00%)
+  bundling: 1/4 manifests (25.00%)
+  bundling: 2/4 manifests (50.00%)
+  bundling: 3/4 manifests (75.00%)
+  bundling: 4/4 manifests (100.00%)
+  bundling: D 1/3 files (33.33%)
+  bundling: E 2/3 files (66.67%)
+  bundling: H 3/3 files (100.00%)
+  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)
+  \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\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)
+  \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)
+  \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)
+  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)
+  \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)
+
+  $ hg unbundle2 < ../rev.hg2
+  adding changesets
+  adding manifests
+  adding file changes
+  added 0 changesets with 0 changes to 3 files
+  0 unread bytes
+  addchangegroup return: 1
+
+with reply
+
+  $ hg bundle2 --rev '8+7+5+4' --reply ../rev-rr.hg2
+  $ hg unbundle2 ../rev-reply.hg2 < ../rev-rr.hg2
+  0 unread bytes
+  addchangegroup return: 1
+
+  $ cat ../rev-reply.hg2
+  HG2X\x00\x00\x003\x15b2x:reply:changegroup\x00\x00\x00\x00\x00\x02\x0b\x01\x06\x01in-reply-to1return1\x00\x00\x00\x00\x00\x1f (esc)
+  b2x:output\x00\x00\x00\x01\x00\x01\x0b\x01in-reply-to1\x00\x00\x00dadding changesets (esc)
+  adding manifests
+  adding file changes
+  added 0 changesets with 0 changes to 3 files
+  \x00\x00\x00\x00\x00\x00 (no-eol) (esc)
+
+  $ cd ..
--- a/tests/test-bundle2.t	Sun Sep 28 14:07:56 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1193 +0,0 @@
-
-  $ getmainid() {
-  >    hg -R main log --template '{node}\n' --rev "$1"
-  > }
-
-Create an extension to test bundle2 API
-
-  $ cat > bundle2.py << EOF
-  > """A small extension to test bundle2 implementation
-  > 
-  > Current bundle2 implementation is far too limited to be used in any core
-  > code. We still need to be able to test it while it grow up.
-  > """
-  > 
-  > import sys, os
-  > from mercurial import cmdutil
-  > from mercurial import util
-  > from mercurial import bundle2
-  > from mercurial import scmutil
-  > from mercurial import discovery
-  > from mercurial import changegroup
-  > from mercurial import error
-  > from mercurial import obsolete
-  > 
-  > obsolete._enabled = True
-  > 
-  > try:
-  >     import msvcrt
-  >     msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
-  >     msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
-  >     msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
-  > except ImportError:
-  >     pass
-  > 
-  > cmdtable = {}
-  > command = cmdutil.command(cmdtable)
-  > 
-  > ELEPHANTSSONG = """Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
-  > Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
-  > Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko."""
-  > assert len(ELEPHANTSSONG) == 178 # future test say 178 bytes, trust it.
-  > 
-  > @bundle2.parthandler('test:song')
-  > def songhandler(op, part):
-  >     """handle a "test:song" bundle2 part, printing the lyrics on stdin"""
-  >     op.ui.write('The choir starts singing:\n')
-  >     verses = 0
-  >     for line in part.read().split('\n'):
-  >         op.ui.write('    %s\n' % line)
-  >         verses += 1
-  >     op.records.add('song', {'verses': verses})
-  > 
-  > @bundle2.parthandler('test:ping')
-  > def pinghandler(op, part):
-  >     op.ui.write('received ping request (id %i)\n' % part.id)
-  >     if op.reply is not None and 'ping-pong' in op.reply.capabilities:
-  >         op.ui.write_err('replying to ping request (id %i)\n' % part.id)
-  >         op.reply.newpart('test:pong', [('in-reply-to', str(part.id))])
-  > 
-  > @bundle2.parthandler('test:debugreply')
-  > def debugreply(op, part):
-  >     """print data about the capacity of the bundle reply"""
-  >     if op.reply is None:
-  >         op.ui.write('debugreply: no reply\n')
-  >     else:
-  >         op.ui.write('debugreply: capabilities:\n')
-  >         for cap in sorted(op.reply.capabilities):
-  >             op.ui.write('debugreply:     %r\n' % cap)
-  >             for val in op.reply.capabilities[cap]:
-  >                 op.ui.write('debugreply:         %r\n' % val)
-  > 
-  > @command('bundle2',
-  >          [('', 'param', [], 'stream level parameter'),
-  >           ('', 'unknown', False, 'include an unknown mandatory part in the bundle'),
-  >           ('', 'unknownparams', False, 'include an unknown part parameters in the bundle'),
-  >           ('', 'parts', False, 'include some arbitrary parts to the bundle'),
-  >           ('', 'reply', False, 'produce a reply bundle'),
-  >           ('', 'pushrace', False, 'includes a check:head part with unknown nodes'),
-  >           ('r', 'rev', [], 'includes those changeset in the bundle'),],
-  >          '[OUTPUTFILE]')
-  > def cmdbundle2(ui, repo, path=None, **opts):
-  >     """write a bundle2 container on standard ouput"""
-  >     bundler = bundle2.bundle20(ui)
-  >     for p in opts['param']:
-  >         p = p.split('=', 1)
-  >         try:
-  >             bundler.addparam(*p)
-  >         except ValueError, exc:
-  >             raise util.Abort('%s' % exc)
-  > 
-  >     if opts['reply']:
-  >         capsstring = 'ping-pong\nelephants=babar,celeste\ncity%3D%21=celeste%2Cville'
-  >         bundler.newpart('b2x:replycaps', data=capsstring)
-  > 
-  >     if opts['pushrace']:
-  >         # also serve to test the assignement of data outside of init
-  >         part = bundler.newpart('b2x:check:heads')
-  >         part.data = '01234567890123456789'
-  > 
-  >     revs = opts['rev']
-  >     if 'rev' in opts:
-  >         revs = scmutil.revrange(repo, opts['rev'])
-  >         if revs:
-  >             # very crude version of a changegroup part creation
-  >             bundled = repo.revs('%ld::%ld', revs, revs)
-  >             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())
-  > 
-  >     if opts['parts']:
-  >        bundler.newpart('test:empty')
-  >        # add a second one to make sure we handle multiple parts
-  >        bundler.newpart('test:empty')
-  >        bundler.newpart('test:song', data=ELEPHANTSSONG)
-  >        bundler.newpart('test:debugreply')
-  >        mathpart = bundler.newpart('test:math')
-  >        mathpart.addparam('pi', '3.14')
-  >        mathpart.addparam('e', '2.72')
-  >        mathpart.addparam('cooking', 'raw', mandatory=False)
-  >        mathpart.data = '42'
-  >        # advisory known part with unknown mandatory param
-  >        bundler.newpart('test:song', [('randomparam','')])
-  >     if opts['unknown']:
-  >        bundler.newpart('test:UNKNOWN', data='some random content')
-  >     if opts['unknownparams']:
-  >        bundler.newpart('test:SONG', [('randomparams', '')])
-  >     if opts['parts']:
-  >        bundler.newpart('test:ping')
-  > 
-  >     if path is None:
-  >        file = sys.stdout
-  >     else:
-  >         file = open(path, 'wb')
-  > 
-  >     for chunk in bundler.getchunks():
-  >         file.write(chunk)
-  > 
-  > @command('unbundle2', [], '')
-  > def cmdunbundle2(ui, repo, replypath=None):
-  >     """process a bundle2 stream from stdin on the current repo"""
-  >     try:
-  >         tr = None
-  >         lock = repo.lock()
-  >         tr = repo.transaction('processbundle')
-  >         try:
-  >             unbundler = bundle2.unbundle20(ui, sys.stdin)
-  >             op = bundle2.processbundle(repo, unbundler, lambda: tr)
-  >             tr.close()
-  >         except error.BundleValueError, exc:
-  >             raise util.Abort('missing support for %s' % exc)
-  >         except error.PushRaced, exc:
-  >             raise util.Abort('push race: %s' % exc)
-  >     finally:
-  >         if tr is not None:
-  >             tr.release()
-  >         lock.release()
-  >         remains = sys.stdin.read()
-  >         ui.write('%i unread bytes\n' % len(remains))
-  >     if op.records['song']:
-  >         totalverses = sum(r['verses'] for r in op.records['song'])
-  >         ui.write('%i total verses sung\n' % totalverses)
-  >     for rec in op.records['changegroup']:
-  >         ui.write('addchangegroup return: %i\n' % rec['return'])
-  >     if op.reply is not None and replypath is not None:
-  >         file = open(replypath, 'wb')
-  >         for chunk in op.reply.getchunks():
-  >             file.write(chunk)
-  > 
-  > @command('statbundle2', [], '')
-  > def cmdstatbundle2(ui, repo):
-  >     """print statistic on the bundle2 container read from stdin"""
-  >     unbundler = bundle2.unbundle20(ui, sys.stdin)
-  >     try:
-  >         params = unbundler.params
-  >     except error.BundleValueError, exc:
-  >        raise util.Abort('unknown parameters: %s' % exc)
-  >     ui.write('options count: %i\n' % len(params))
-  >     for key in sorted(params):
-  >         ui.write('- %s\n' % key)
-  >         value = params[key]
-  >         if value is not None:
-  >             ui.write('    %s\n' % value)
-  >     count = 0
-  >     for p in unbundler.iterparts():
-  >         count += 1
-  >         ui.write('  :%s:\n' % p.type)
-  >         ui.write('    mandatory: %i\n' % len(p.mandatoryparams))
-  >         ui.write('    advisory: %i\n' % len(p.advisoryparams))
-  >         ui.write('    payload: %i bytes\n' % len(p.read()))
-  >     ui.write('parts count:   %i\n' % count)
-  > EOF
-  $ cat >> $HGRCPATH << EOF
-  > [extensions]
-  > bundle2=$TESTTMP/bundle2.py
-  > [experimental]
-  > bundle2-exp=True
-  > [ui]
-  > ssh=python "$TESTDIR/dummyssh"
-  > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
-  > [web]
-  > push_ssl = false
-  > allow_push = *
-  > [phases]
-  > publish=False
-  > EOF
-
-The extension requires a repo (currently unused)
-
-  $ hg init main
-  $ cd main
-  $ touch a
-  $ hg add a
-  $ hg commit -m 'a'
-
-
-Empty bundle
-=================
-
-- no option
-- no parts
-
-Test bundling
-
-  $ hg bundle2
-  HG2X\x00\x00\x00\x00 (no-eol) (esc)
-
-Test unbundling
-
-  $ hg bundle2 | hg statbundle2
-  options count: 0
-  parts count:   0
-
-Test old style bundle are detected and refused
-
-  $ hg bundle --all ../bundle.hg
-  1 changesets found
-  $ hg statbundle2 < ../bundle.hg
-  abort: unknown bundle version 10
-  [255]
-
-Test parameters
-=================
-
-- some options
-- no parts
-
-advisory parameters, no value
--------------------------------
-
-Simplest possible parameters form
-
-Test generation simple option
-
-  $ hg bundle2 --param 'caution'
-  HG2X\x00\x07caution\x00\x00 (no-eol) (esc)
-
-Test unbundling
-
-  $ hg bundle2 --param 'caution' | hg statbundle2
-  options count: 1
-  - caution
-  parts count:   0
-
-Test generation multiple option
-
-  $ hg bundle2 --param 'caution' --param 'meal'
-  HG2X\x00\x0ccaution meal\x00\x00 (no-eol) (esc)
-
-Test unbundling
-
-  $ hg bundle2 --param 'caution' --param 'meal' | hg statbundle2
-  options count: 2
-  - caution
-  - meal
-  parts count:   0
-
-advisory parameters, with value
--------------------------------
-
-Test generation
-
-  $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants'
-  HG2X\x00\x1ccaution meal=vegan elephants\x00\x00 (no-eol) (esc)
-
-Test unbundling
-
-  $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | hg statbundle2
-  options count: 3
-  - caution
-  - elephants
-  - meal
-      vegan
-  parts count:   0
-
-parameter with special char in value
----------------------------------------------------
-
-Test generation
-
-  $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple
-  HG2X\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
-
-Test unbundling
-
-  $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | hg statbundle2
-  options count: 2
-  - e|! 7/
-      babar%#==tutu
-  - simple
-  parts count:   0
-
-Test unknown mandatory option
----------------------------------------------------
-
-  $ hg bundle2 --param 'Gravity' | hg statbundle2
-  abort: unknown parameters: Stream Parameter - Gravity
-  [255]
-
-Test debug output
----------------------------------------------------
-
-bundling debug
-
-  $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2
-  start emission of HG2X stream
-  bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
-  start of parts
-  end of bundle
-
-file content is ok
-
-  $ cat ../out.hg2
-  HG2X\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
-
-unbundling debug
-
-  $ hg statbundle2 --debug < ../out.hg2
-  start processing of HG2X stream
-  reading bundle2 stream parameters
-  ignoring unknown parameter 'e|! 7/'
-  ignoring unknown parameter 'simple'
-  options count: 2
-  - e|! 7/
-      babar%#==tutu
-  - simple
-  start extraction of bundle2 parts
-  part header size: 0
-  end of bundle2 stream
-  parts count:   0
-
-
-Test buggy input
----------------------------------------------------
-
-empty parameter name
-
-  $ hg bundle2 --param '' --quiet
-  abort: empty parameter name
-  [255]
-
-bad parameter name
-
-  $ hg bundle2 --param 42babar
-  abort: non letter first character: '42babar'
-  [255]
-
-
-Test part
-=================
-
-  $ hg bundle2 --parts ../parts.hg2 --debug
-  start emission of HG2X stream
-  bundle parameter: 
-  start of parts
-  bundle part: "test:empty"
-  bundle part: "test:empty"
-  bundle part: "test:song"
-  bundle part: "test:debugreply"
-  bundle part: "test:math"
-  bundle part: "test:song"
-  bundle part: "test:ping"
-  end of bundle
-
-  $ cat ../parts.hg2
-  HG2X\x00\x00\x00\x11 (esc)
-  test:empty\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11 (esc)
-  test:empty\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x10	test:song\x00\x00\x00\x02\x00\x00\x00\x00\x00\xb2Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko (esc)
-  Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
-  Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.\x00\x00\x00\x00\x00\x16\x0ftest:debugreply\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00+	test:math\x00\x00\x00\x04\x02\x01\x02\x04\x01\x04\x07\x03pi3.14e2.72cookingraw\x00\x00\x00\x0242\x00\x00\x00\x00\x00\x1d	test:song\x00\x00\x00\x05\x01\x00\x0b\x00randomparam\x00\x00\x00\x00\x00\x10	test:ping\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
-
-
-  $ hg statbundle2 < ../parts.hg2
-  options count: 0
-    :test:empty:
-      mandatory: 0
-      advisory: 0
-      payload: 0 bytes
-    :test:empty:
-      mandatory: 0
-      advisory: 0
-      payload: 0 bytes
-    :test:song:
-      mandatory: 0
-      advisory: 0
-      payload: 178 bytes
-    :test:debugreply:
-      mandatory: 0
-      advisory: 0
-      payload: 0 bytes
-    :test:math:
-      mandatory: 2
-      advisory: 1
-      payload: 2 bytes
-    :test:song:
-      mandatory: 1
-      advisory: 0
-      payload: 0 bytes
-    :test:ping:
-      mandatory: 0
-      advisory: 0
-      payload: 0 bytes
-  parts count:   7
-
-  $ hg statbundle2 --debug < ../parts.hg2
-  start processing of HG2X stream
-  reading bundle2 stream parameters
-  options count: 0
-  start extraction of bundle2 parts
-  part header size: 17
-  part type: "test:empty"
-  part id: "0"
-  part parameters: 0
-    :test:empty:
-      mandatory: 0
-      advisory: 0
-  payload chunk size: 0
-      payload: 0 bytes
-  part header size: 17
-  part type: "test:empty"
-  part id: "1"
-  part parameters: 0
-    :test:empty:
-      mandatory: 0
-      advisory: 0
-  payload chunk size: 0
-      payload: 0 bytes
-  part header size: 16
-  part type: "test:song"
-  part id: "2"
-  part parameters: 0
-    :test:song:
-      mandatory: 0
-      advisory: 0
-  payload chunk size: 178
-  payload chunk size: 0
-      payload: 178 bytes
-  part header size: 22
-  part type: "test:debugreply"
-  part id: "3"
-  part parameters: 0
-    :test:debugreply:
-      mandatory: 0
-      advisory: 0
-  payload chunk size: 0
-      payload: 0 bytes
-  part header size: 43
-  part type: "test:math"
-  part id: "4"
-  part parameters: 3
-    :test:math:
-      mandatory: 2
-      advisory: 1
-  payload chunk size: 2
-  payload chunk size: 0
-      payload: 2 bytes
-  part header size: 29
-  part type: "test:song"
-  part id: "5"
-  part parameters: 1
-    :test:song:
-      mandatory: 1
-      advisory: 0
-  payload chunk size: 0
-      payload: 0 bytes
-  part header size: 16
-  part type: "test:ping"
-  part id: "6"
-  part parameters: 0
-    :test:ping:
-      mandatory: 0
-      advisory: 0
-  payload chunk size: 0
-      payload: 0 bytes
-  part header size: 0
-  end of bundle2 stream
-  parts count:   7
-
-Test actual unbundling of test part
-=======================================
-
-Process the bundle
-
-  $ hg unbundle2 --debug < ../parts.hg2
-  start processing of HG2X stream
-  reading bundle2 stream parameters
-  start extraction of bundle2 parts
-  part header size: 17
-  part type: "test:empty"
-  part id: "0"
-  part parameters: 0
-  ignoring unsupported advisory part test:empty
-  payload chunk size: 0
-  part header size: 17
-  part type: "test:empty"
-  part id: "1"
-  part parameters: 0
-  ignoring unsupported advisory part test:empty
-  payload chunk size: 0
-  part header size: 16
-  part type: "test:song"
-  part id: "2"
-  part parameters: 0
-  found a handler for part 'test:song'
-  The choir starts singing:
-  payload chunk size: 178
-  payload chunk size: 0
-      Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
-      Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
-      Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
-  part header size: 22
-  part type: "test:debugreply"
-  part id: "3"
-  part parameters: 0
-  found a handler for part 'test:debugreply'
-  debugreply: no reply
-  payload chunk size: 0
-  part header size: 43
-  part type: "test:math"
-  part id: "4"
-  part parameters: 3
-  ignoring unsupported advisory part test:math
-  payload chunk size: 2
-  payload chunk size: 0
-  part header size: 29
-  part type: "test:song"
-  part id: "5"
-  part parameters: 1
-  found a handler for part 'test:song'
-  ignoring unsupported advisory part test:song - randomparam
-  payload chunk size: 0
-  part header size: 16
-  part type: "test:ping"
-  part id: "6"
-  part parameters: 0
-  found a handler for part 'test:ping'
-  received ping request (id 6)
-  payload chunk size: 0
-  part header size: 0
-  end of bundle2 stream
-  0 unread bytes
-  3 total verses sung
-
-Unbundle with an unknown mandatory part
-(should abort)
-
-  $ hg bundle2 --parts --unknown ../unknown.hg2
-
-  $ hg unbundle2 < ../unknown.hg2
-  The choir starts singing:
-      Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
-      Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
-      Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
-  debugreply: no reply
-  0 unread bytes
-  abort: missing support for test:unknown
-  [255]
-
-Unbundle with an unknown mandatory part parameters
-(should abort)
-
-  $ hg bundle2 --unknownparams ../unknown.hg2
-
-  $ hg unbundle2 < ../unknown.hg2
-  0 unread bytes
-  abort: missing support for test:song - randomparams
-  [255]
-
-unbundle with a reply
-
-  $ hg bundle2 --parts --reply ../parts-reply.hg2
-  $ hg unbundle2 ../reply.hg2 < ../parts-reply.hg2
-  0 unread bytes
-  3 total verses sung
-
-The reply is a bundle
-
-  $ cat ../reply.hg2
-  HG2X\x00\x00\x00\x1f (esc)
-  b2x:output\x00\x00\x00\x00\x00\x01\x0b\x01in-reply-to3\x00\x00\x00\xd9The choir starts singing: (esc)
-      Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
-      Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
-      Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
-  \x00\x00\x00\x00\x00\x1f (esc)
-  b2x:output\x00\x00\x00\x01\x00\x01\x0b\x01in-reply-to4\x00\x00\x00\xc9debugreply: capabilities: (esc)
-  debugreply:     'city=!'
-  debugreply:         'celeste,ville'
-  debugreply:     'elephants'
-  debugreply:         'babar'
-  debugreply:         'celeste'
-  debugreply:     'ping-pong'
-  \x00\x00\x00\x00\x00\x1e	test:pong\x00\x00\x00\x02\x01\x00\x0b\x01in-reply-to7\x00\x00\x00\x00\x00\x1f (esc)
-  b2x:output\x00\x00\x00\x03\x00\x01\x0b\x01in-reply-to7\x00\x00\x00=received ping request (id 7) (esc)
-  replying to ping request (id 7)
-  \x00\x00\x00\x00\x00\x00 (no-eol) (esc)
-
-The reply is valid
-
-  $ hg statbundle2 < ../reply.hg2
-  options count: 0
-    :b2x:output:
-      mandatory: 0
-      advisory: 1
-      payload: 217 bytes
-    :b2x:output:
-      mandatory: 0
-      advisory: 1
-      payload: 201 bytes
-    :test:pong:
-      mandatory: 1
-      advisory: 0
-      payload: 0 bytes
-    :b2x:output:
-      mandatory: 0
-      advisory: 1
-      payload: 61 bytes
-  parts count:   4
-
-Unbundle the reply to get the output:
-
-  $ hg unbundle2 < ../reply.hg2
-  remote: The choir starts singing:
-  remote:     Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
-  remote:     Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
-  remote:     Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
-  remote: debugreply: capabilities:
-  remote: debugreply:     'city=!'
-  remote: debugreply:         'celeste,ville'
-  remote: debugreply:     'elephants'
-  remote: debugreply:         'babar'
-  remote: debugreply:         'celeste'
-  remote: debugreply:     'ping-pong'
-  remote: received ping request (id 7)
-  remote: replying to ping request (id 7)
-  0 unread bytes
-
-Test push race detection
-
-  $ hg bundle2 --pushrace ../part-race.hg2
-
-  $ hg unbundle2 < ../part-race.hg2
-  0 unread bytes
-  abort: push race: repository changed while pushing - please try again
-  [255]
-
-Support for changegroup
-===================================
-
-  $ hg unbundle $TESTDIR/bundles/rebase.hg
-  adding changesets
-  adding manifests
-  adding file changes
-  added 8 changesets with 7 changes to 7 files (+3 heads)
-  (run 'hg heads' to see heads, 'hg merge' to merge)
-
-  $ hg log -G
-  o  8:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com>  H
-  |
-  | o  7:eea13746799a draft Nicolas Dumazet <nicdumz.commits@gmail.com>  G
-  |/|
-  o |  6:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com>  F
-  | |
-  | o  5:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com>  E
-  |/
-  | o  4:32af7686d403 draft Nicolas Dumazet <nicdumz.commits@gmail.com>  D
-  | |
-  | o  3:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com>  C
-  | |
-  | o  2:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com>  B
-  |/
-  o  1:cd010b8cd998 draft Nicolas Dumazet <nicdumz.commits@gmail.com>  A
-  
-  @  0:3903775176ed draft test  a
-  
-
-  $ hg bundle2 --debug --rev '8+7+5+4' ../rev.hg2
-  4 changesets found
-  list of changesets:
-  32af7686d403cf45b5d95f2d70cebea587ac806a
-  9520eea781bcca16c1e15acc0ba14335a0e8e5ba
-  eea13746799a9e0bfd88f29d3c2e9dc9389f524f
-  02de42196ebee42ef284b6780a87cdc96e8eaab6
-  start emission of HG2X stream
-  bundle parameter: 
-  start of parts
-  bundle part: "b2x:changegroup"
-  bundling: 1/4 changesets (25.00%)
-  bundling: 2/4 changesets (50.00%)
-  bundling: 3/4 changesets (75.00%)
-  bundling: 4/4 changesets (100.00%)
-  bundling: 1/4 manifests (25.00%)
-  bundling: 2/4 manifests (50.00%)
-  bundling: 3/4 manifests (75.00%)
-  bundling: 4/4 manifests (100.00%)
-  bundling: D 1/3 files (33.33%)
-  bundling: E 2/3 files (66.67%)
-  bundling: H 3/3 files (100.00%)
-  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)
-  \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\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)
-  \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)
-  \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)
-  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)
-  \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)
-
-  $ hg unbundle2 < ../rev.hg2
-  adding changesets
-  adding manifests
-  adding file changes
-  added 0 changesets with 0 changes to 3 files
-  0 unread bytes
-  addchangegroup return: 1
-
-with reply
-
-  $ hg bundle2 --rev '8+7+5+4' --reply ../rev-rr.hg2
-  $ hg unbundle2 ../rev-reply.hg2 < ../rev-rr.hg2
-  0 unread bytes
-  addchangegroup return: 1
-
-  $ cat ../rev-reply.hg2
-  HG2X\x00\x00\x003\x15b2x:reply:changegroup\x00\x00\x00\x00\x00\x02\x0b\x01\x06\x01in-reply-to1return1\x00\x00\x00\x00\x00\x1f (esc)
-  b2x:output\x00\x00\x00\x01\x00\x01\x0b\x01in-reply-to1\x00\x00\x00dadding changesets (esc)
-  adding manifests
-  adding file changes
-  added 0 changesets with 0 changes to 3 files
-  \x00\x00\x00\x00\x00\x00 (no-eol) (esc)
-
-  $ cd ..
-
-Real world exchange
-=====================
-
-Add more obsolescence information
-
-  $ hg -R main debugobsolete -d '0 0' 1111111111111111111111111111111111111111 `getmainid 9520eea781bc`
-  $ hg -R main debugobsolete -d '0 0' 2222222222222222222222222222222222222222 `getmainid 24b6387c8c8c`
-
-clone --pull
-
-  $ hg -R main phase --public cd010b8cd998
-  $ hg clone main other --pull --rev 9520eea781bc
-  adding changesets
-  adding manifests
-  adding file changes
-  added 2 changesets with 2 changes to 2 files
-  1 new obsolescence markers
-  updating to branch default
-  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg -R other log -G
-  @  1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com>  E
-  |
-  o  0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com>  A
-  
-  $ hg -R other debugobsolete
-  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-
-pull
-
-  $ hg -R main phase --public 9520eea781bc
-  $ hg -R other pull -r 24b6387c8c8c
-  pulling from $TESTTMP/main (glob)
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files (+1 heads)
-  1 new obsolescence markers
-  (run 'hg heads' to see heads, 'hg merge' to merge)
-  $ hg -R other log -G
-  o  2:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com>  F
-  |
-  | @  1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com>  E
-  |/
-  o  0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com>  A
-  
-  $ hg -R other debugobsolete
-  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-
-pull empty (with phase movement)
-
-  $ hg -R main phase --public 24b6387c8c8c
-  $ hg -R other pull -r 24b6387c8c8c
-  pulling from $TESTTMP/main (glob)
-  no changes found
-  $ hg -R other log -G
-  o  2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com>  F
-  |
-  | @  1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com>  E
-  |/
-  o  0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com>  A
-  
-  $ hg -R other debugobsolete
-  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-
-pull empty
-
-  $ hg -R other pull -r 24b6387c8c8c
-  pulling from $TESTTMP/main (glob)
-  no changes found
-  $ hg -R other log -G
-  o  2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com>  F
-  |
-  | @  1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com>  E
-  |/
-  o  0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com>  A
-  
-  $ hg -R other debugobsolete
-  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-
-add extra data to test their exchange during push
-
-  $ hg -R main bookmark --rev eea13746799a book_eea1
-  $ hg -R main debugobsolete -d '0 0' 3333333333333333333333333333333333333333 `getmainid eea13746799a`
-  $ hg -R main bookmark --rev 02de42196ebe book_02de
-  $ hg -R main debugobsolete -d '0 0' 4444444444444444444444444444444444444444 `getmainid 02de42196ebe`
-  $ hg -R main bookmark --rev 42ccdea3bb16 book_42cc
-  $ hg -R main debugobsolete -d '0 0' 5555555555555555555555555555555555555555 `getmainid 42ccdea3bb16`
-  $ hg -R main bookmark --rev 5fddd98957c8 book_5fdd
-  $ hg -R main debugobsolete -d '0 0' 6666666666666666666666666666666666666666 `getmainid 5fddd98957c8`
-  $ hg -R main bookmark --rev 32af7686d403 book_32af
-  $ hg -R main debugobsolete -d '0 0' 7777777777777777777777777777777777777777 `getmainid 32af7686d403`
-
-  $ hg -R other bookmark --rev cd010b8cd998 book_eea1
-  $ hg -R other bookmark --rev cd010b8cd998 book_02de
-  $ hg -R other bookmark --rev cd010b8cd998 book_42cc
-  $ hg -R other bookmark --rev cd010b8cd998 book_5fdd
-  $ hg -R other bookmark --rev cd010b8cd998 book_32af
-
-  $ hg -R main phase --public eea13746799a
-
-push
-  $ hg -R main push other --rev eea13746799a --bookmark book_eea1
-  pushing to other
-  searching for changes
-  remote: adding changesets
-  remote: adding manifests
-  remote: adding file changes
-  remote: added 1 changesets with 0 changes to 0 files (-1 heads)
-  remote: 1 new obsolescence markers
-  updating bookmark book_eea1
-  $ hg -R other log -G
-  o    3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
-  |\
-  | o  2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com>  F
-  | |
-  @ |  1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com>  E
-  |/
-  o  0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de book_32af book_42cc book_5fdd A
-  
-  $ hg -R other debugobsolete
-  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-
-pull over ssh
-
-  $ hg -R other pull ssh://user@dummy/main -r 02de42196ebe --bookmark book_02de
-  pulling from ssh://user@dummy/main
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files (+1 heads)
-  1 new obsolescence markers
-  updating bookmark book_02de
-  (run 'hg heads' to see heads, 'hg merge' to merge)
-  $ hg -R other debugobsolete
-  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-
-pull over http
-
-  $ hg -R main serve -p $HGPORT -d --pid-file=main.pid -E main-error.log
-  $ cat main.pid >> $DAEMON_PIDS
-
-  $ hg -R other pull http://localhost:$HGPORT/ -r 42ccdea3bb16 --bookmark book_42cc
-  pulling from http://localhost:$HGPORT/
-  searching for changes
-  adding changesets
-  adding manifests
-  adding file changes
-  added 1 changesets with 1 changes to 1 files (+1 heads)
-  1 new obsolescence markers
-  updating bookmark book_42cc
-  (run 'hg heads .' to see heads, 'hg merge' to merge)
-  $ cat main-error.log
-  $ hg -R other debugobsolete
-  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-
-push over ssh
-
-  $ hg -R main push ssh://user@dummy/other -r 5fddd98957c8 --bookmark book_5fdd
-  pushing to ssh://user@dummy/other
-  searching for changes
-  remote: adding changesets
-  remote: adding manifests
-  remote: adding file changes
-  remote: added 1 changesets with 1 changes to 1 files
-  remote: 1 new obsolescence markers
-  updating bookmark book_5fdd
-  $ hg -R other log -G
-  o  6:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
-  |
-  o  5:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
-  |
-  | o  4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
-  | |
-  | | o  3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
-  | |/|
-  | o |  2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com>  F
-  |/ /
-  | @  1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com>  E
-  |/
-  o  0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af A
-  
-  $ hg -R other debugobsolete
-  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-
-push over http
-
-  $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
-  $ cat other.pid >> $DAEMON_PIDS
-
-  $ hg -R main phase --public 32af7686d403
-  $ hg -R main push http://localhost:$HGPORT2/ -r 32af7686d403 --bookmark book_32af
-  pushing to http://localhost:$HGPORT2/
-  searching for changes
-  remote: adding changesets
-  remote: adding manifests
-  remote: adding file changes
-  remote: added 1 changesets with 1 changes to 1 files
-  remote: 1 new obsolescence markers
-  updating bookmark book_32af
-  $ cat other-error.log
-
-Check final content.
-
-  $ hg -R other log -G
-  o  7:32af7686d403 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af D
-  |
-  o  6:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
-  |
-  o  5:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
-  |
-  | o  4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
-  | |
-  | | o  3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
-  | |/|
-  | o |  2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com>  F
-  |/ /
-  | @  1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com>  E
-  |/
-  o  0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com>  A
-  
-  $ hg -R other debugobsolete
-  1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-  7777777777777777777777777777777777777777 32af7686d403cf45b5d95f2d70cebea587ac806a 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
-
-Error Handling
-==============
-
-Check that errors are properly returned to the client during push.
-
-Setting up
-
-  $ cat > failpush.py << EOF
-  > """A small extension that makes push fails when using bundle2
-  > 
-  > used to test error handling in bundle2
-  > """
-  > 
-  > from mercurial import util
-  > from mercurial import bundle2
-  > from mercurial import exchange
-  > from mercurial import extensions
-  > 
-  > def _pushbundle2failpart(pushop, bundler):
-  >     reason = pushop.ui.config('failpush', 'reason', None)
-  >     part = None
-  >     if reason == 'abort':
-  >         bundler.newpart('test:abort')
-  >     if reason == 'unknown':
-  >         bundler.newpart('TEST:UNKNOWN')
-  >     if reason == 'race':
-  >         # 20 Bytes of crap
-  >         bundler.newpart('b2x:check:heads', data='01234567890123456789')
-  > 
-  > @bundle2.parthandler("test:abort")
-  > def handleabort(op, part):
-  >     raise util.Abort('Abandon ship!', hint="don't panic")
-  > 
-  > def uisetup(ui):
-  >     exchange.b2partsgenmapping['failpart'] = _pushbundle2failpart
-  >     exchange.b2partsgenorder.insert(0, 'failpart')
-  > 
-  > EOF
-
-  $ cd main
-  $ hg up tip
-  3 files updated, 0 files merged, 1 files removed, 0 files unresolved
-  $ echo 'I' > I
-  $ hg add I
-  $ hg ci -m 'I'
-  $ hg id
-  e7ec4e813ba6 tip
-  $ cd ..
-
-  $ cat << EOF >> $HGRCPATH
-  > [extensions]
-  > failpush=$TESTTMP/failpush.py
-  > EOF
-
-  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
-  $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
-  $ cat other.pid >> $DAEMON_PIDS
-
-Doing the actual push: Abort error
-
-  $ cat << EOF >> $HGRCPATH
-  > [failpush]
-  > reason = abort
-  > EOF
-
-  $ hg -R main push other -r e7ec4e813ba6
-  pushing to other
-  searching for changes
-  abort: Abandon ship!
-  (don't panic)
-  [255]
-
-  $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
-  pushing to ssh://user@dummy/other
-  searching for changes
-  abort: Abandon ship!
-  (don't panic)
-  [255]
-
-  $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
-  pushing to http://localhost:$HGPORT2/
-  searching for changes
-  abort: Abandon ship!
-  (don't panic)
-  [255]
-
-
-Doing the actual push: unknown mandatory parts
-
-  $ cat << EOF >> $HGRCPATH
-  > [failpush]
-  > reason = unknown
-  > EOF
-
-  $ hg -R main push other -r e7ec4e813ba6
-  pushing to other
-  searching for changes
-  abort: missing support for test:unknown
-  [255]
-
-  $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
-  pushing to ssh://user@dummy/other
-  searching for changes
-  abort: missing support for test:unknown
-  [255]
-
-  $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
-  pushing to http://localhost:$HGPORT2/
-  searching for changes
-  abort: missing support for test:unknown
-  [255]
-
-Doing the actual push: race
-
-  $ cat << EOF >> $HGRCPATH
-  > [failpush]
-  > reason = race
-  > EOF
-
-  $ hg -R main push other -r e7ec4e813ba6
-  pushing to other
-  searching for changes
-  abort: push failed:
-  'repository changed while pushing - please try again'
-  [255]
-
-  $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
-  pushing to ssh://user@dummy/other
-  searching for changes
-  abort: push failed:
-  'repository changed while pushing - please try again'
-  [255]
-
-  $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
-  pushing to http://localhost:$HGPORT2/
-  searching for changes
-  abort: push failed:
-  'repository changed while pushing - please try again'
-  [255]
-
-Doing the actual push: hook abort
-
-  $ cat << EOF >> $HGRCPATH
-  > [failpush]
-  > reason =
-  > [hooks]
-  > b2x-pretransactionclose.failpush = false
-  > EOF
-
-  $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
-  $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
-  $ cat other.pid >> $DAEMON_PIDS
-
-  $ hg -R main push other -r e7ec4e813ba6
-  pushing to other
-  searching for changes
-  transaction abort!
-  rollback completed
-  abort: b2x-pretransactionclose.failpush hook exited with status 1
-  [255]
-
-  $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
-  pushing to ssh://user@dummy/other
-  searching for changes
-  abort: b2x-pretransactionclose.failpush hook exited with status 1
-  remote: transaction abort!
-  remote: rollback completed
-  [255]
-
-  $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
-  pushing to http://localhost:$HGPORT2/
-  searching for changes
-  abort: b2x-pretransactionclose.failpush hook exited with status 1
-  [255]
-
-