author | Pierre-Yves David <pierre-yves.david@octobus.net> |
Wed, 25 Oct 2017 04:45:31 +0200 | |
changeset 3149 | 79a926b557f1 |
parent 3148 | 0ed75e700ad8 |
child 3150 | 95c77ef938ef |
--- a/hgext3rd/evolve/__init__.py Tue Oct 24 14:30:48 2017 +0200 +++ b/hgext3rd/evolve/__init__.py Wed Oct 25 04:45:31 2017 +0200 @@ -420,6 +420,8 @@ if not evolveopts: evolveopts = ['all'] ui.setconfig('experimental', 'evolution', evolveopts, 'evolve') + if obsolete.isenabled(repo, 'exchange'): + repo.ui.setconfig('server', 'bundle1', False) @eh.uisetup def _configurecmdoptions(ui):
--- a/hgext3rd/evolve/obsexchange.py Tue Oct 24 14:30:48 2017 +0200 +++ b/hgext3rd/evolve/obsexchange.py Wed Oct 25 04:45:31 2017 +0200 @@ -14,24 +14,16 @@ import io StringIO = io.StringIO -import errno -import socket - from mercurial import ( bundle2, - error, exchange, extensions, - httppeer, - localrepo, lock as lockmod, node, obsolete, util, wireproto, ) -from mercurial.hgweb import hgweb_mod -from mercurial.i18n import _ from . import ( exthelper, @@ -199,244 +191,3 @@ finaldata.write(obsdata) finaldata.seek(0) return wireproto.streamres(reader=finaldata, v1compressible=True) - -############################################### -### Support for old legacy exchange methods ### -############################################### - -class pushobsmarkerStringIO(StringIO): - """hacky string io for progress""" - - @util.propertycache - def length(self): - return len(self.getvalue()) - - def read(self, size=None): - obsexcprg(self.ui, self.tell(), unit=_("bytes"), total=self.length) - return StringIO.read(self, size) - - def __iter__(self): - d = self.read(4096) - while d: - yield d - d = self.read(4096) - -# compat-code: _pushobsolete -# -# the _pushobsolete function is a core function used to exchange -# obsmarker with repository that does not support bundle2 - -@eh.wrapfunction(exchange, '_pushobsolete') -def _pushobsolete(orig, pushop): - """utility function to push obsolete markers to a remote""" - if not obsolete.isenabled(pushop.repo, obsolete.exchangeopt): - return - if 'obsmarkers' in pushop.stepsdone: - return - pushop.stepsdone.add('obsmarkers') - if pushop.cgresult == 0: - return - pushop.ui.debug('try to push obsolete markers to remote\n') - repo = pushop.repo - remote = pushop.remote - if (repo.obsstore and 'obsolete' in remote.listkeys('namespaces')): - markers = pushop.outobsmarkers - if not markers: - obsexcmsg(repo.ui, "no marker to push\n") - elif remote.capable('_evoext_pushobsmarkers_0'): - msg = ('the remote repository use years old versions of Mercurial' - ' and evolve\npushing obsmarker using legacy method\n') - repo.ui.warn(msg) - repo.ui.warn('(please upgrade your server)\n') - obsdata = pushobsmarkerStringIO() - for chunk in obsolete.encodemarkers(markers, True): - obsdata.write(chunk) - obsdata.seek(0) - obsdata.ui = repo.ui - obsexcmsg(repo.ui, "pushing %i obsolescence markers (%i bytes)\n" - % (len(markers), len(obsdata.getvalue())), - True) - remote.evoext_pushobsmarkers_0(obsdata) - obsexcprg(repo.ui, None) - - else: - # XXX core could be able do the same things but without the debug - # and progress output. - msg = ('the remote repository usea years old version of Mercurial' - ' and not evolve extension\n') - repo.ui.warn(msg) - msg = 'pushing obsmarker using and extremely slow legacy method\n' - repo.ui.warn(msg) - repo.ui.warn('(please upgrade your server and enable evolve.serveronly on it)\n') - rslts = [] - remotedata = obsolete._pushkeyescape(markers).items() - totalbytes = sum(len(d) for k, d in remotedata) - sentbytes = 0 - obsexcmsg(repo.ui, "pushing %i obsolescence markers in %i " - "pushkey payload (%i bytes)\n" - % (len(markers), len(remotedata), totalbytes), - True) - for key, data in remotedata: - obsexcprg(repo.ui, sentbytes, item=key, unit=_("bytes"), - total=totalbytes) - rslts.append(remote.pushkey('obsolete', key, '', data)) - sentbytes += len(data) - obsexcprg(repo.ui, sentbytes, item=key, unit=_("bytes"), - total=totalbytes) - obsexcprg(repo.ui, None) - if [r for r in rslts if not r]: - msg = _('failed to push some obsolete markers!\n') - repo.ui.warn(msg) - obsexcmsg(repo.ui, "DONE\n") - -# Supporting legacy way to push obsmarker so that old client can still push -# them somewhat efficiently - -@eh.addattr(wireproto.wirepeer, 'evoext_pushobsmarkers_0') -def client_pushobsmarkers(self, obsfile): - """wireprotocol peer method""" - self.requirecap('_evoext_pushobsmarkers_0', - _('push obsolete markers faster')) - ret, output = self._callpush('evoext_pushobsmarkers_0', obsfile) - for l in output.splitlines(True): - self.ui.status(_('remote: '), l) - return ret - -@eh.addattr(httppeer.httppeer, 'evoext_pushobsmarkers_0') -def httpclient_pushobsmarkers(self, obsfile): - """httpprotocol peer method - (Cannot simply use _callpush as http is doing some special handling)""" - self.requirecap('_evoext_pushobsmarkers_0', - _('push obsolete markers faster')) - try: - r = self._call('evoext_pushobsmarkers_0', data=obsfile) - vals = r.split('\n', 1) - if len(vals) < 2: - raise error.ResponseError(_("unexpected response:"), r) - - for l in vals[1].splitlines(True): - if l.strip(): - self.ui.status(_('remote: '), l) - return vals[0] - except socket.error as err: - if err.args[0] in (errno.ECONNRESET, errno.EPIPE): - raise error.Abort(_('push failed: %s') % err.args[1]) - raise error.Abort(err.args[1]) - -@eh.wrapfunction(localrepo.localrepository, '_restrictcapabilities') -def local_pushobsmarker_capabilities(orig, repo, caps): - caps = orig(repo, caps) - caps.add('_evoext_pushobsmarkers_0') - return caps - -@eh.addattr(localrepo.localpeer, 'evoext_pushobsmarkers_0') -def local_pushobsmarkers(peer, obsfile): - data = obsfile.read() - _pushobsmarkers(peer._repo, data) - -# compat-code: _pullobsolete -# -# the _pullobsolete function is a core function used to exchange -# obsmarker with repository that does not support bundle2 - -@eh.wrapfunction(exchange, '_pullobsolete') -def _pullobsolete(orig, pullop): - if not obsolete.isenabled(pullop.repo, obsolete.exchangeopt): - return None - if 'obsmarkers' in pullop.stepsdone: - return None - wirepull = pullop.remote.capable('_evoext_pullobsmarkers_0') - if 'obsolete' not in pullop.remote.listkeys('namespaces'): - return None # remote opted out of obsolescence marker exchange - if not wirepull: - return orig(pullop) - tr = None - ui = pullop.repo.ui - boundaries = obsdiscovery.buildpullobsmarkersboundaries(pullop, bundle2=False) - if 'missing' in boundaries and not boundaries['missing']: - obsexcmsg(ui, "nothing to pull\n") - return None - elif not set(boundaries['heads']) - set(boundaries['common']): - obsexcmsg(ui, "nothing to pull\n") - return None - - obsexcmsg(ui, "pull obsolescence markers\n", True) - new = 0 - - msg = ('the remote repository use years old versions of Mercurial and evolve\n' - 'pulling obsmarker using legacy method\n') - ui.warn(msg) - ui.warn('(please upgrade your server)\n') - - obsdata = pullop.remote.evoext_pullobsmarkers_0(**boundaries) - obsdata = obsdata.read() - if len(obsdata) > 5: - msg = "merging obsolescence markers (%i bytes)\n" % len(obsdata) - obsexcmsg(ui, msg) - tr = pullop.gettransaction() - old = len(pullop.repo.obsstore._all) - pullop.repo.obsstore.mergemarkers(tr, obsdata) - new = len(pullop.repo.obsstore._all) - old - obsexcmsg(ui, "%i obsolescence markers added\n" % new, True) - else: - obsexcmsg(ui, "no unknown remote markers\n") - obsexcmsg(ui, "DONE\n") - if new: - pullop.repo.invalidatevolatilesets() - return tr - -@eh.addattr(wireproto.wirepeer, 'evoext_pullobsmarkers_0') -def client_pullobsmarkers(self, heads=None, common=None): - self.requirecap('_evoext_pullobsmarkers_0', _('look up remote obsmarkers')) - opts = {} - if heads is not None: - opts['heads'] = wireproto.encodelist(heads) - if common is not None: - opts['common'] = wireproto.encodelist(common) - f = self._callcompressable("evoext_pullobsmarkers_0", **opts) - length = int(f.read(20)) - chunk = 4096 - current = 0 - data = StringIO() - ui = self.ui - obsexcprg(ui, current, unit=_("bytes"), total=length) - while current < length: - readsize = min(length - current, chunk) - data.write(f.read(readsize)) - current += readsize - obsexcprg(ui, current, unit=_("bytes"), total=length) - obsexcprg(ui, None) - data.seek(0) - return data - -@eh.addattr(localrepo.localpeer, 'evoext_pullobsmarkers_0') -def local_pullobsmarkers(self, heads=None, common=None): - return _getobsmarkersstream(self._repo, heads=heads, - common=common) - -def _legacypush_capabilities(orig, repo, proto): - """wrapper to advertise new capability""" - caps = orig(repo, proto) - if obsolete.isenabled(repo, obsolete.exchangeopt): - caps = caps.split() - caps.append('_evoext_pushobsmarkers_0') - caps.append('_evoext_pullobsmarkers_0') - caps.sort() - caps = ' '.join(caps) - return caps - -@eh.extsetup -def extsetup(ui): - # legacy standalone method - hgweb_mod.perms['evoext_pushobsmarkers_0'] = 'push' - hgweb_mod.perms['evoext_pullobsmarkers_0'] = 'pull' - wireproto.commands['evoext_pushobsmarkers_0'] = (srv_pushobsmarkers, '') - wireproto.commands['evoext_pullobsmarkers_0'] = (srv_pullobsmarkers, '*') - - extensions.wrapfunction(wireproto, 'capabilities', _legacypush_capabilities) - # wrap command content - oldcap, args = wireproto.commands['capabilities'] - - def newcap(repo, proto): - return _legacypush_capabilities(oldcap, repo, proto) - wireproto.commands['capabilities'] = (newcap, args)
--- a/hgext3rd/evolve/serveronly.py Tue Oct 24 14:30:48 2017 +0200 +++ b/hgext3rd/evolve/serveronly.py Wed Oct 25 04:45:31 2017 +0200 @@ -13,6 +13,8 @@ import sys import os +from mercurial import obsolete + try: from . import ( compat, @@ -55,3 +57,5 @@ if not evolveopts: evolveopts = 'all' ui.setconfig('experimental', 'evolution', evolveopts) + if obsolete.isenabled(repo, 'exchange'): + repo.ui.setconfig('server', 'bundle1', False)
--- a/tests/test-discovery-obshashrange.t Tue Oct 24 14:30:48 2017 +0200 +++ b/tests/test-discovery-obshashrange.t Wed Oct 25 04:45:31 2017 +0200 @@ -183,8 +183,8 @@ running python "*/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob) sending hello command sending between command - remote: 533 - remote: capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_obshashrange_v0 _evoext_pullobsmarkers_0 _evoext_pushobsmarkers_0 batch branchmap bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Aobsmarkers%3DV0%2CV1%0Aphases%3Dheads%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps changegroupsubset getbundle known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash + remote: 483 + remote: capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_obshashrange_v0 batch * (glob) remote: 1 preparing listkeys for "phases" sending listkeys command
--- a/tests/test-evolve-serveronly-bundle2.t Tue Oct 24 14:30:48 2017 +0200 +++ b/tests/test-evolve-serveronly-bundle2.t Wed Oct 25 04:45:31 2017 +0200 @@ -86,9 +86,9 @@ =================== $ curl -s http://localhost:$HGPORT/?cmd=hello - capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_pullobsmarkers_0 _evoext_pushobsmarkers_0 batch * (glob) + capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 batch * (glob) $ curl -s http://localhost:$HGPORT/?cmd=capabilities - _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_pullobsmarkers_0 _evoext_pushobsmarkers_0 batch * (no-eol) (glob) + _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 batch * (no-eol) (glob) $ curl -s "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort bookmarks @@ -151,9 +151,9 @@ obsolete phases $ curl -s http://localhost:$HGPORT/?cmd=hello - capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_pullobsmarkers_0 _evoext_pushobsmarkers_0 batch * (glob) + capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 batch * (glob) $ curl -s http://localhost:$HGPORT/?cmd=capabilities - _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_pullobsmarkers_0 _evoext_pushobsmarkers_0 batch * (no-eol) (glob) + _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 batch * (no-eol) (glob) $ echo '[experimental]' >> server/.hg/hgrc $ echo 'evolution=!' >> server/.hg/hgrc @@ -178,9 +178,9 @@ phases $ curl -s http://localhost:$HGPORT/?cmd=hello - capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_pullobsmarkers_0 _evoext_pushobsmarkers_0 batch * (glob) + capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 batch * (glob) $ curl -s http://localhost:$HGPORT/?cmd=capabilities - _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_pullobsmarkers_0 _evoext_pushobsmarkers_0 batch * (no-eol) (glob) + _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 batch * (no-eol) (glob) Test obshashrange discover ===========================================
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-evolve-serveronly-legacy.t Wed Oct 25 04:45:31 2017 +0200 @@ -0,0 +1,63 @@ + + $ . ${TESTDIR}/testlib/pythonpath.sh + + $ cat >> $HGRCPATH <<EOF + > [defaults] + > amend=-d "0 0" + > [web] + > push_ssl = false + > allow_push = * + > [phases] + > publish = False + > [experimental] + > bundle2-exp=False # < Mercurial-4.0 + > [devel] + > legacy.exchange=bundle1 + > [extensions] + > EOF + + $ mkcommit() { + > echo "$1" > "$1" + > hg add "$1" + > hg ci -m "add $1" + > } + + + $ hg init server + +Try the multiple ways to setup the extension + + $ hg -R server log --config 'extensions.evolve.serveronly=' + $ hg -R server log --config "extensions.evolve.serveronly=${SRCDIR}/hgext3rd/evolve/serveronly.py" + $ PYTHONPATH=$HGTEST_ORIG_PYTHONPATH hg -R server log --config "extensions.evolve.serveronly=${SRCDIR}/hgext3rd/evolve/serveronly.py" + +setup repo + + $ echo "[extensions]" >> ./server/.hg/hgrc + $ echo "evolve.serveronly=" >> ./server/.hg/hgrc + $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log --traceback + $ cat hg.pid >> $DAEMON_PIDS + + $ hg clone http://localhost:$HGPORT/ client + no changes found + updating to branch default + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cat ./errors.log + $ echo "[extensions]" >> ./client/.hg/hgrc + $ echo "evolve=" >> ./client/.hg/hgrc + $ cp -r client other + +Smoke testing +=============== + + $ cd client + $ mkcommit 0 + $ mkcommit a + $ hg push + pushing to http://localhost:$HGPORT/ + searching for changes + abort: remote error: + incompatible Mercurial client; bundle2 required + (see https://www.mercurial-scm.org/wiki/IncompatibleClient) + [255] + $ cat ../errors.log
--- a/tests/test-evolve-serveronly.t Tue Oct 24 14:30:48 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,198 +0,0 @@ - - $ . ${TESTDIR}/testlib/pythonpath.sh - - $ cat >> $HGRCPATH <<EOF - > [defaults] - > amend=-d "0 0" - > [web] - > push_ssl = false - > allow_push = * - > [phases] - > publish = False - > [experimental] - > bundle2-exp=False # < Mercurial-4.0 - > [devel] - > legacy.exchange=bundle1 - > [extensions] - > EOF - - $ mkcommit() { - > echo "$1" > "$1" - > hg add "$1" - > hg ci -m "add $1" - > } - - - $ hg init server - -Try the multiple ways to setup the extension - - $ hg -R server log --config 'extensions.evolve.serveronly=' - $ hg -R server log --config "extensions.evolve.serveronly=${SRCDIR}/hgext3rd/evolve/serveronly.py" - $ PYTHONPATH=$HGTEST_ORIG_PYTHONPATH hg -R server log --config "extensions.evolve.serveronly=${SRCDIR}/hgext3rd/evolve/serveronly.py" - -setup repo - - $ echo "[extensions]" >> ./server/.hg/hgrc - $ echo "evolve.serveronly=" >> ./server/.hg/hgrc - $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log --traceback - $ cat hg.pid >> $DAEMON_PIDS - - $ hg clone http://localhost:$HGPORT/ client - no changes found - updating to branch default - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ cat ./errors.log - $ echo "[extensions]" >> ./client/.hg/hgrc - $ echo "evolve=" >> ./client/.hg/hgrc - $ cp -r client other - -Smoke testing -=============== - - $ cd client - $ mkcommit 0 - $ mkcommit a - $ hg push - pushing to http://localhost:$HGPORT/ - searching for changes - remote: adding changesets - remote: adding manifests - remote: adding file changes - remote: added 2 changesets with 2 changes to 2 files - $ hg pull - pulling from http://localhost:$HGPORT/ - searching for changes - no changes found - $ cat ../errors.log - $ hg pull -R ../other - pulling from http://localhost:$HGPORT/ - requesting all changes - adding changesets - adding manifests - adding file changes - added 2 changesets with 2 changes to 2 files - pull obsolescence markers - the remote repository use years old versions of Mercurial and evolve - pulling obsmarker using legacy method - (please upgrade your server) - new changesets 8685c6d34325:4957bfdac07e - (run 'hg update' to get a working copy) - $ cat ../errors.log - $ hg push -R ../other - pushing to http://localhost:$HGPORT/ - searching for changes - no changes found - [1] - $ cat ../errors.log - -Capacity testing -=================== - - $ curl -s http://localhost:$HGPORT/?cmd=hello - capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_pullobsmarkers_0 _evoext_pushobsmarkers_0 batch * (glob) - $ curl -s http://localhost:$HGPORT/?cmd=capabilities - _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_pullobsmarkers_0 _evoext_pushobsmarkers_0 batch * (no-eol) (glob) - - $ curl -s "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort - bookmarks - namespaces - obsolete - phases - -Push -============= - - $ echo 'A' > a - $ hg amend - $ hg push - pushing to http://localhost:$HGPORT/ - searching for changes - remote: adding changesets - remote: adding manifests - remote: adding file changes - remote: added 1 changesets with 1 changes to 1 files (+1 heads) - the remote repository use years old versions of Mercurial and evolve - pushing obsmarker using legacy method - (please upgrade your server) - pushing 1 obsolescence markers (* bytes) (glob) - remote: 1 obsolescence markers added - remote: obsoleted 1 changesets - $ cat ../errors.log - $ hg push - pushing to http://localhost:$HGPORT/ - searching for changes - no changes found - [1] - $ cat ../errors.log - -Pull -============= - - $ hg -R ../other pull - pulling from http://localhost:$HGPORT/ - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to [12] files \(\+1 heads\) (re) - pull obsolescence markers - the remote repository use years old versions of Mercurial and evolve - pulling obsmarker using legacy method - (please upgrade your server) - 1 obsolescence markers added - obsoleted 1 changesets - new changesets 9d1c114e7797 - (run 'hg heads' to see heads) - $ cat ../errors.log - $ hg -R ../other pull - pulling from http://localhost:$HGPORT/ - searching for changes - no changes found - $ cat ../errors.log - - $ cd .. - -Test disabling obsolete advertisement -=========================================== -(used by bitbucket to select which repo use evolve) - - $ curl -s "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort - bookmarks - namespaces - obsolete - phases - $ curl -s http://localhost:$HGPORT/?cmd=hello - capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_pullobsmarkers_0 _evoext_pushobsmarkers_0 batch * (glob) - $ curl -s http://localhost:$HGPORT/?cmd=capabilities - _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_pullobsmarkers_0 _evoext_pushobsmarkers_0 batch * (no-eol) (glob) - - $ echo '[experimental]' >> server/.hg/hgrc - $ echo 'evolution=!' >> server/.hg/hgrc - $ $RUNTESTDIR/killdaemons.py $DAEMON_PIDS - $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log - $ cat hg.pid >> $DAEMON_PIDS - - $ curl -s "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort - bookmarks - namespaces - phases - $ curl -s http://localhost:$HGPORT/?cmd=hello | grep _evoext_pushobsmarkers_0 - [1] - $ curl -s http://localhost:$HGPORT/?cmd=capabilities | grep _evoext_pushobsmarkers_0 - [1] - - $ echo 'evolution=' >> server/.hg/hgrc - $ $RUNTESTDIR/killdaemons.py $DAEMON_PIDS - $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log - $ cat hg.pid >> $DAEMON_PIDS - - $ curl -s "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort - bookmarks - namespaces - obsolete - phases - $ curl -s http://localhost:$HGPORT/?cmd=hello - capabilities: _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_pullobsmarkers_0 _evoext_pushobsmarkers_0 batch * (glob) - $ curl -s http://localhost:$HGPORT/?cmd=capabilities - _evoext_getbundle_obscommon _evoext_obshash_0 _evoext_obshash_1 _evoext_pullobsmarkers_0 _evoext_pushobsmarkers_0 batch * (no-eol) (glob)