[PATCH 2 of 2] bundle2: abort when a mandatory pushkey part fails

Pierre-Yves David pierre-yves.david at ens-lyon.org
Sat Jun 6 15:57:21 CDT 2015


# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at fb.com>
# Date 1432729720 25200
#      Wed May 27 05:28:40 2015 -0700
# Node ID 40c4968954f175698a21ddb11aaa3db4c72c3887
# Parent  1965f29f800fc0761ae9053165d9c2c75943d21f
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 supports it. We'll
introduce a more precise error in the next changesets.

diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
--- a/mercurial/bundle2.py
+++ b/mercurial/bundle2.py
@@ -1327,10 +1327,13 @@ def handlepushkey(op, inpart):
     op.records.add('pushkey', record)
     if op.reply is not None:
         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):
     """retrieve the result of a pushkey request"""
     ret = int(inpart.params['return'])
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
@@ -715,5 +715,147 @@ Check output capture control.
   remote: transaction abort!
   remote: Cleaning up the mess...
   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]
+


More information about the Mercurial-devel mailing list