# HG changeset patch # User Pierre-Yves David # Date 1432729720 25200 # Node ID 6de96cb31176620b5df72d2b12ca8eae7eab936f # Parent d410336fdb3c7abc741e8fcedf09c85123bedbac bundle2: abort when a mandatory pushkey part fails So far, result of a pushkey operation had no consequence on the transaction (beside the change). We makes it respect the 'mandatory' flag of part so that failed pushkey call abort the whole transaction. This will allow rejecting changes (primary target: changesets) regarding phases or bookmark criteria in the future (when we will push such data in a mandatory part). We currently raise an abort error because all clients support it. We'll introduce a more precise error in the next changesets. diff -r d410336fdb3c -r 6de96cb31176 mercurial/bundle2.py --- a/mercurial/bundle2.py Wed May 27 05:35:00 2015 -0700 +++ b/mercurial/bundle2.py Wed May 27 05:28:40 2015 -0700 @@ -1329,6 +1329,9 @@ rpart = op.reply.newpart('reply:pushkey') rpart.addparam('in-reply-to', str(inpart.id), mandatory=False) rpart.addparam('return', '%i' % ret, mandatory=False) + if inpart.mandatory and not ret: + raise util.Abort(_('failed to update value for "%s/%s"') + % (namespace, key)) @parthandler('reply:pushkey', ('return', 'in-reply-to')) def handlepushkeyreply(op, inpart): diff -r d410336fdb3c -r 6de96cb31176 tests/test-bundle2-exchange.t --- a/tests/test-bundle2-exchange.t Wed May 27 05:35:00 2015 -0700 +++ b/tests/test-bundle2-exchange.t Wed May 27 05:28:40 2015 -0700 @@ -717,3 +717,145 @@ remote: rollback completed abort: pretxnchangegroup hook exited with status 1 [255] + +Check abort from mandatory pushkey + + $ cat > mandatorypart.py << EOF + > from mercurial import exchange + > from mercurial import pushkey + > from mercurial import node + > @exchange.b2partsgenerator('failingpuskey') + > def addfailingpushey(pushop, bundler): + > enc = pushkey.encode + > part = bundler.newpart('pushkey') + > part.addparam('namespace', enc('phases')) + > part.addparam('key', enc(pushop.repo['cd010b8cd998'].hex())) + > part.addparam('old', enc(str(0))) # successful update + > part.addparam('new', enc(str(0))) + > EOF + $ cat >> $HGRCPATH << EOF + > [hooks] + > pretxnchangegroup= + > pretxnclose.failpush= + > prepushkey.failpush = sh -c "echo 'do not push the key !'; false" + > [extensions] + > mandatorypart=$TESTTMP/mandatorypart.py + > EOF + $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config + $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log + $ cat other.pid >> $DAEMON_PIDS + +(Failure from a hook) + + $ hg -R main push other -r e7ec4e813ba6 + pushing to other + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + do not push the key ! + pushkey-abort: prepushkey.failpush hook exited with status 1 + transaction abort! + Cleaning up the mess... + rollback completed + abort: failed to update value for "phases/cd010b8cd998f3981a5a8115f94f8da4ab506089" + [255] + $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6 + 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: do not push the key ! + remote: pushkey-abort: prepushkey.failpush hook exited with status 1 + remote: transaction abort! + remote: Cleaning up the mess... + remote: rollback completed + abort: failed to update value for "phases/cd010b8cd998f3981a5a8115f94f8da4ab506089" + [255] + $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6 + 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: do not push the key ! + remote: pushkey-abort: prepushkey.failpush hook exited with status 1 + remote: transaction abort! + remote: Cleaning up the mess... + remote: rollback completed + abort: failed to update value for "phases/cd010b8cd998f3981a5a8115f94f8da4ab506089" + [255] + +(Failure from a the pushkey) + + $ cat > mandatorypart.py << EOF + > from mercurial import exchange + > from mercurial import pushkey + > from mercurial import node + > @exchange.b2partsgenerator('failingpuskey') + > def addfailingpushey(pushop, bundler): + > enc = pushkey.encode + > part = bundler.newpart('pushkey') + > part.addparam('namespace', enc('phases')) + > part.addparam('key', enc(pushop.repo['cd010b8cd998'].hex())) + > part.addparam('old', enc(str(4))) # will fail + > part.addparam('new', enc(str(3))) + > EOF + $ cat >> $HGRCPATH << EOF + > [hooks] + > prepushkey.failpush = + > EOF + $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config + $ 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 + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + transaction abort! + Cleaning up the mess... + rollback completed + pushkey: lock state after "phases" + lock: free + wlock: free + abort: failed to update value for "phases/cd010b8cd998f3981a5a8115f94f8da4ab506089" + [255] + $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6 + 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: transaction abort! + remote: Cleaning up the mess... + remote: rollback completed + remote: pushkey: lock state after "phases" + remote: lock: free + remote: wlock: free + abort: failed to update value for "phases/cd010b8cd998f3981a5a8115f94f8da4ab506089" + [255] + $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6 + 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: transaction abort! + remote: Cleaning up the mess... + remote: rollback completed + remote: pushkey: lock state after "phases" + remote: lock: free + remote: wlock: free + abort: failed to update value for "phases/cd010b8cd998f3981a5a8115f94f8da4ab506089" + [255] +