diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -117,6 +117,9 @@ class pushoperation(object): 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 @@ def _pushbundle2(pushop): 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) diff --git a/tests/test-bundle2-exchange.t b/tests/test-bundle2-exchange.t --- a/tests/test-bundle2-exchange.t +++ b/tests/test-bundle2-exchange.t @@ -724,6 +724,7 @@ Check abort from mandatory pushkey > 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 @@ Check abort from mandatory pushkey > 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 @@ Check abort from mandatory pushkey 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 @@ Check abort from mandatory pushkey > 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 @@ Check abort from mandatory pushkey > 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 @@ Check abort from mandatory pushkey 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