bundle2: attribute remote failures to remote (issue4788) stable
authorGregory Szorc <gregory.szorc@gmail.com>
Sat, 24 Oct 2015 00:39:22 +0100
branchstable
changeset 28017 58f1645f72c3cd48191e1b8da412a5ddf31541c1
parent 28016 00e75baa810f5e42080d7c2dd826dadc1a22d4c4
child 28018 65387a30430e8c0d2cf4851cdb2057d5b05e1cc8
push id48
push usergszorc@mozilla.com
push dateWed, 04 Nov 2015 20:44:50 +0000
bundle2: attribute remote failures to remote (issue4788) Before bundle2, hook output from hook failures was prefixed with "remote: ". Up to this point with bundle2, the output was converted to the message to print in an Abort exception. This had 2 implications: 1) It was unclear whether an error message came from the local repo or the remote 2) The exit code changed from 1 to 255 This patch changes the handling of error:abort bundle2 parts during push to prefix the error message with "remote: ". This restores the old behavior. We still preserve the behavior of raising an Abort during bundle2 application failure. This is a regression from pre-bundle2 because the exit code changed. Because we no longer raise an Abort with the remote's message, we needed to insert a message for the new Abort. So, I invented a new error message for that. This is another change from pre-bundle2. However, I like the new error message because it states unambiguously who aborted the push failed, which I think is important for users so they can decide what's next.
mercurial/bundle2.py
mercurial/exchange.py
tests/test-bundle2-exchange.t
tests/test-ssh-bundle1.t
tests/test-ssh.t
--- a/mercurial/bundle2.py
+++ b/mercurial/bundle2.py
@@ -1403,20 +1403,24 @@ def handleoutput(op, inpart):
 def handlereplycaps(op, inpart):
     """Notify that a reply bundle should be created
 
     The payload contains the capabilities information for the reply"""
     caps = decodecaps(inpart.read())
     if op.reply is None:
         op.reply = bundle20(op.ui, caps)
 
+class AbortFromPart(error.Abort):
+    """Sub-class of Abort that denotes an error from a bundle2 part."""
+
 @parthandler('error:abort', ('message', 'hint'))
 def handleerrorabort(op, inpart):
     """Used to transmit abort error over the wire"""
-    raise error.Abort(inpart.params['message'], hint=inpart.params.get('hint'))
+    raise AbortFromPart(inpart.params['message'],
+                        hint=inpart.params.get('hint'))
 
 @parthandler('error:pushkey', ('namespace', 'key', 'new', 'old', 'ret',
                                'in-reply-to'))
 def handleerrorpushkey(op, inpart):
     """Used to transmit failure of a mandatory pushkey over the wire"""
     kwargs = {}
     for name in ('namespace', 'key', 'new', 'old', 'ret'):
         value = inpart.params.get(name)
--- a/mercurial/exchange.py
+++ b/mercurial/exchange.py
@@ -793,16 +793,19 @@ def _pushbundle2(pushop):
             raise error.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 as exc:
             raise error.Abort('missing support for %s' % exc)
+        except bundle2.AbortFromPart as exc:
+            pushop.ui.status(_('remote: %s\n') % exc)
+            raise error.Abort(_('push failed on remote'), hint=exc.hint)
     except error.PushkeyFailed as 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
+++ b/tests/test-bundle2-exchange.t
@@ -513,24 +513,26 @@ Doing the actual push: Abort error
   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!
+  remote: Abandon ship!
+  abort: push failed on remote
   (don't panic)
   [255]
 
   $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
   pushing to http://localhost:$HGPORT2/
   searching for changes
-  abort: Abandon ship!
+  remote: Abandon ship!
+  abort: push failed on remote
   (don't panic)
   [255]
 
 
 Doing the actual push: unknown mandatory parts
 
   $ cat << EOF >> $HGRCPATH
   > [failpush]
@@ -619,32 +621,34 @@ Doing the actual push: hook abort
   remote: adding manifests
   remote: adding file changes
   remote: added 1 changesets with 1 changes to 1 files
   remote: pre-close-tip:e7ec4e813ba6 draft 
   remote: You shall not pass!
   remote: transaction abort!
   remote: Cleaning up the mess...
   remote: rollback completed
-  abort: pretxnclose.failpush hook exited with status 1
+  remote: pretxnclose.failpush hook exited with status 1
+  abort: push failed on remote
   [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: pre-close-tip:e7ec4e813ba6 draft 
   remote: You shall not pass!
   remote: transaction abort!
   remote: Cleaning up the mess...
   remote: rollback completed
-  abort: pretxnclose.failpush hook exited with status 1
+  remote: pretxnclose.failpush hook exited with status 1
+  abort: push failed on remote
   [255]
 
 (check that no 'pending' files remain)
 
   $ ls -1 other/.hg/bookmarks*
   other/.hg/bookmarks
   $ ls -1 other/.hg/store/phaseroots*
   other/.hg/store/phaseroots
@@ -679,30 +683,32 @@ Check error from hook during the unbundl
   remote: adding changesets
   remote: adding manifests
   remote: adding file changes
   remote: added 1 changesets with 1 changes to 1 files
   remote: Fail early!
   remote: transaction abort!
   remote: Cleaning up the mess...
   remote: rollback completed
-  abort: pretxnchangegroup hook exited with status 1
+  remote: pretxnchangegroup hook exited with status 1
+  abort: push failed on remote
   [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: Fail early!
   remote: transaction abort!
   remote: Cleaning up the mess...
   remote: rollback completed
-  abort: pretxnchangegroup hook exited with status 1
+  remote: pretxnchangegroup hook exited with status 1
+  abort: push failed on remote
   [255]
 
 Check output capture control.
 
 (should be still forced for http, disabled for local and ssh)
 
   $ cat >> $HGRCPATH << EOF
   > [experimental]
@@ -728,30 +734,32 @@ Check output capture control.
   remote: adding changesets
   remote: adding manifests
   remote: adding file changes
   remote: added 1 changesets with 1 changes to 1 files
   remote: Fail early!
   remote: transaction abort!
   remote: Cleaning up the mess...
   remote: rollback completed
-  abort: pretxnchangegroup hook exited with status 1
+  remote: pretxnchangegroup hook exited with status 1
+  abort: push failed on remote
   [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: Fail early!
   remote: transaction abort!
   remote: Cleaning up the mess...
   remote: rollback completed
-  abort: pretxnchangegroup hook exited with status 1
+  remote: pretxnchangegroup hook exited with status 1
+  abort: push failed on remote
   [255]
 
 Check abort from mandatory pushkey
 
   $ cat > mandatorypart.py << EOF
   > from mercurial import exchange
   > from mercurial import pushkey
   > from mercurial import node
@@ -923,17 +931,18 @@ Test lazily acquiring the lock during un
   > EOF
 
   $ hg clone -q ssh://user@dummy/lazylock lazylockclient
   $ cd lazylockclient
   $ touch a && hg ci -Aqm a
   $ hg push
   pushing to ssh://user@dummy/lazylock
   searching for changes
-  abort: Lock should not be taken
+  remote: Lock should not be taken
+  abort: push failed on remote
   [255]
 
   $ cat >> ../lazylock/.hg/hgrc <<EOF
   > [experimental]
   > bundle2lazylocking=True
   > EOF
   $ hg push
   pushing to ssh://user@dummy/lazylock
--- a/tests/test-ssh-bundle1.t
+++ b/tests/test-ssh-bundle1.t
@@ -528,13 +528,14 @@ remote hook failure is attributed to rem
   $ hg -q commit -A -m 'remote hook failure'
   $ hg --config ui.ssh="python '$TESTDIR/dummyssh'" push
   pushing to ssh://user@dummy/remote
   searching for changes
   remote: adding changesets
   remote: adding manifests
   remote: adding file changes
   remote: added 1 changesets with 1 changes to 1 files
-  remote: hook failure!transaction abort!
+  remote: hook failure!
+  remote: transaction abort!
   remote: rollback completed
   remote: abort: pretxnchangegroup.fail hook failed
   [1]
 
--- a/tests/test-ssh.t
+++ b/tests/test-ssh.t
@@ -392,17 +392,18 @@ Test hg-ssh in read-only mode:
 
   $ cd read-only-local
   $ echo "baz" > bar
   $ hg ci -A -m "unpushable commit" bar
   $ hg push --ssh "sh ../ssh.sh"
   pushing to ssh://user@dummy/*/remote (glob)
   searching for changes
   remote: Permission denied
-  abort: pretxnopen.hg-ssh hook failed
+  remote: pretxnopen.hg-ssh hook failed
+  abort: push failed on remote
   [255]
 
   $ cd ..
 
 stderr from remote commands should be printed before stdout from local code (issue4336)
 
   $ hg clone remote stderr-ordering
   updating to branch default
@@ -522,11 +523,12 @@ remote hook failure is attributed to rem
   searching for changes
   remote: adding changesets
   remote: adding manifests
   remote: adding file changes
   remote: added 1 changesets with 1 changes to 1 files
   remote: hook failure!
   remote: transaction abort!
   remote: rollback completed
-  abort: pretxnchangegroup.fail hook failed
+  remote: pretxnchangegroup.fail hook failed
+  abort: push failed on remote
   [255]