diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -145,7 +145,7 @@ import struct import urllib import string -import changegroup +import changegroup, error from i18n import _ _pack = struct.pack @@ -730,7 +730,7 @@ def handlechangegroup(op, inpart): h = inpart.read(20) assert not h if heads != op.repo.heads(): - raise exchange.PushRaced() + raise error.PushRaced() @parthandler('b2x:output') def handleoutput(op, inpart): diff --git a/mercurial/error.py b/mercurial/error.py --- a/mercurial/error.py +++ b/mercurial/error.py @@ -94,3 +94,7 @@ class SignalInterrupt(KeyboardInterrupt) class SignatureError(Exception): pass + +class PushRaced(RuntimeError): + """An exception raised during unbundling that indicate a push race""" + diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -8,7 +8,7 @@ from i18n import _ from node import hex, nullid import errno, urllib -import util, scmutil, changegroup, base85 +import util, scmutil, changegroup, base85, error import discovery, phases, obsolete, bookmarks, bundle2 def readbundle(ui, fh, fname, vfs=None): @@ -708,9 +708,6 @@ def _getbundleextrapart(bundler, repo, s """hook function to let extensions add parts to the requested bundle""" pass -class PushRaced(RuntimeError): - """An exception raised during unbundling that indicate a push race""" - def check_heads(repo, their_heads, context): """check if the heads of a repo have been modified @@ -722,8 +719,8 @@ def check_heads(repo, their_heads, conte their_heads == ['hashed', heads_hash]): # someone else committed/pushed/unbundled while we # were transferring data - raise PushRaced('repository changed while %s - ' - 'please try again' % context) + raise error.PushRaced('repository changed while %s - ' + 'please try again' % context) def unbundle(repo, cg, heads, source, url): """Apply a bundle to a repo. diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -133,7 +133,7 @@ class localpeer(peer.peerrepository): stream = util.chunkbuffer(ret.getchunks()) ret = bundle2.unbundle20(self.ui, stream) return ret - except exchange.PushRaced, exc: + except error.PushRaced, exc: raise error.ResponseError(_('push failed:'), exc.message) def lock(self): diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py --- a/mercurial/wireproto.py +++ b/mercurial/wireproto.py @@ -826,5 +826,5 @@ def unbundle(repo, proto, heads): else: sys.stderr.write("abort: %s\n" % inst) return pushres(0) - except exchange.PushRaced, exc: + except error.PushRaced, exc: return pusherr(str(exc)) diff --git a/tests/test-bundle2.t b/tests/test-bundle2.t --- a/tests/test-bundle2.t +++ b/tests/test-bundle2.t @@ -15,6 +15,7 @@ Create an extension to test bundle2 API > from mercurial import scmutil > from mercurial import discovery > from mercurial import changegroup + > from mercurial import error > cmdtable = {} > command = cmdutil.command(cmdtable) > @@ -59,6 +60,7 @@ Create an extension to test bundle2 API > ('', 'unknown', False, 'include an unknown mandatory part in the bundle'), > ('', 'parts', False, 'include some arbitrary parts to the bundle'), > ('', 'reply', False, 'produce a reply bundle'), + > ('', 'pushrace', False, 'includes a check:head part with unknown nodes'), > ('r', 'rev', [], 'includes those changeset in the bundle'),], > '[OUTPUTFILE]') > def cmdbundle2(ui, repo, path=None, **opts): @@ -75,6 +77,10 @@ Create an extension to test bundle2 API > capsstring = 'ping-pong\nelephants=babar,celeste\ncity%3D%21=celeste%2Cville' > bundler.addpart(bundle2.bundlepart('b2x:replycaps', data=capsstring)) > + > if opts['pushrace']: + > dummynode = '01234567890123456789' + > bundler.addpart(bundle2.bundlepart('b2x:check:heads', data=dummynode)) + > > revs = opts['rev'] > if 'rev' in opts: > revs = scmutil.revrange(repo, opts['rev']) @@ -132,6 +138,8 @@ Create an extension to test bundle2 API > tr.close() > except KeyError, exc: > raise util.Abort('missing support for %s' % exc) + > except error.PushRaced, exc: + > raise util.Abort('push race') > finally: > if tr is not None: > tr.release() @@ -601,6 +609,15 @@ Unbundle the reply to get the output: remote: replying to ping request (id 6) 0 unread bytes +Test push race detection + + $ hg bundle2 --pushrace ../part-race.hg2 + + $ hg unbundle2 < ../part-race.hg2 + 0 unread bytes + abort: push race + [255] + Support for changegroup ===================================