push: catch and process PushkeyFailed error
We add a way to register "pushkey failure callback" that will be used if the
push is aborted by a pushkey failure. A part generator adding mandatory pushkey
parts should register a failure callback for all of them. The callback will be
in charge of generating a meaningful abort if this part fails.
If no callback is registered, the error is propagated.
Catch PushkeyFailed error in exchange.
--- a/mercurial/exchange.py Wed May 27 23:48:54 2015 -0700
+++ b/mercurial/exchange.py Fri Jun 05 16:30:11 2015 -0700
@@ -117,6 +117,9 @@
self.outbookmarks = []
# transaction manager
self.trmanager = None
+ # map { pushkey partid -> callback handling failure}
+ # used to handle exception from mandatory pushkey part failure
+ self.pkfailcb = {}
@util.propertycache
def futureheads(self):
@@ -623,16 +626,22 @@
return
stream = util.chunkbuffer(bundler.getchunks())
try:
- reply = pushop.remote.unbundle(stream, ['force'], 'push')
- except error.BundleValueError, exc:
- raise util.Abort('missing support for %s' % exc)
- try:
- trgetter = None
- if pushback:
- trgetter = pushop.trmanager.transaction
- op = bundle2.processbundle(pushop.repo, reply, trgetter)
- except error.BundleValueError, exc:
- raise util.Abort('missing support for %s' % exc)
+ try:
+ reply = pushop.remote.unbundle(stream, ['force'], 'push')
+ except error.BundleValueError, exc:
+ raise util.Abort('missing support for %s' % exc)
+ try:
+ trgetter = None
+ if pushback:
+ trgetter = pushop.trmanager.transaction
+ op = bundle2.processbundle(pushop.repo, reply, trgetter)
+ except error.BundleValueError, exc:
+ raise util.Abort('missing support for %s' % exc)
+ except error.PushkeyFailed, exc:
+ partid = int(exc.partid)
+ if partid not in pushop.pkfailcb:
+ raise
+ pushop.pkfailcb[partid](pushop, exc)
for rephand in replyhandlers:
rephand(op)
--- a/tests/test-bundle2-exchange.t Wed May 27 23:48:54 2015 -0700
+++ b/tests/test-bundle2-exchange.t Fri Jun 05 16:30:11 2015 -0700
@@ -724,6 +724,7 @@
> from mercurial import exchange
> from mercurial import pushkey
> from mercurial import node
+ > from mercurial import error
> @exchange.b2partsgenerator('failingpuskey')
> def addfailingpushey(pushop, bundler):
> enc = pushkey.encode
@@ -732,6 +733,9 @@
> part.addparam('key', enc(pushop.repo['cd010b8cd998'].hex()))
> part.addparam('old', enc(str(0))) # successful update
> part.addparam('new', enc(str(0)))
+ > def fail(pushop, exc):
+ > raise error.Abort('Correct phase push failed (because hooks)')
+ > pushop.pkfailcb[part.id] = fail
> EOF
$ cat >> $HGRCPATH << EOF
> [hooks]
@@ -759,7 +763,7 @@
transaction abort!
Cleaning up the mess...
rollback completed
- abort: failed to update value for "phases/cd010b8cd998f3981a5a8115f94f8da4ab506089"
+ abort: Correct phase push failed (because hooks)
[255]
$ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
pushing to ssh://user@dummy/other
@@ -796,6 +800,7 @@
> from mercurial import exchange
> from mercurial import pushkey
> from mercurial import node
+ > from mercurial import error
> @exchange.b2partsgenerator('failingpuskey')
> def addfailingpushey(pushop, bundler):
> enc = pushkey.encode
@@ -804,6 +809,9 @@
> part.addparam('key', enc(pushop.repo['cd010b8cd998'].hex()))
> part.addparam('old', enc(str(4))) # will fail
> part.addparam('new', enc(str(3)))
+ > def fail(pushop, exc):
+ > raise error.Abort('Clown phase push failed')
+ > pushop.pkfailcb[part.id] = fail
> EOF
$ cat >> $HGRCPATH << EOF
> [hooks]
@@ -826,7 +834,7 @@
pushkey: lock state after "phases"
lock: free
wlock: free
- abort: failed to update value for "phases/cd010b8cd998f3981a5a8115f94f8da4ab506089"
+ abort: Clown phase push failed
[255]
$ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
pushing to ssh://user@dummy/other