Mercurial > evolve
changeset 2053:f3765c4a352a
exchange: rename the module
Let's add a 'obs' prefix to make the module role clearer.
author | Pierre-Yves David <pierre-yves.david@ens-lyon.org> |
---|---|
date | Tue, 07 Mar 2017 14:19:12 +0100 |
parents | 05170e635e26 |
children | f9d65d24b9f9 |
files | hgext3rd/evolve/__init__.py hgext3rd/evolve/exchange.py hgext3rd/evolve/obsexchange.py |
diffstat | 3 files changed, 420 insertions(+), 417 deletions(-) [+] |
line wrap: on
line diff
--- a/hgext3rd/evolve/__init__.py Wed Mar 08 23:04:45 2017 +0100 +++ b/hgext3rd/evolve/__init__.py Tue Mar 07 14:19:12 2017 +0100 @@ -111,7 +111,7 @@ from mercurial.node import nullid from . import ( - exchange, + obsexchange, exthelper, metadata, serveronly, @@ -148,7 +148,7 @@ eh = exthelper.exthelper() -eh.merge(exchange.eh) +eh.merge(obsexchange.eh) uisetup = eh.final_uisetup extsetup = eh.final_extsetup reposetup = eh.final_reposetup @@ -3093,7 +3093,7 @@ @eh.command( 'debugobsconvert', - [('', 'new-format', exchange._bestformat, _('Destination format for markers.'))], + [('', 'new-format', obsexchange._bestformat, _('Destination format for markers.'))], '') def debugobsconvert(ui, repo, new_format): origmarkers = repo.obsstore._all # settle version
--- a/hgext3rd/evolve/exchange.py Wed Mar 08 23:04:45 2017 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,414 +0,0 @@ -##################################################################### -### Obsolescence marker exchange experimenation ### -##################################################################### - -from __future__ import absolute_import - -try: - import StringIO as io - StringIO = io.StringIO -except ImportError: - import io - StringIO = io.StringIO - -import errno -import socket - -from mercurial import ( - error, - exchange, - httppeer, - localrepo, - node, - obsolete, - util, - wireproto, -) -from mercurial.i18n import _ - -from . import ( - exthelper, - serveronly, - utility, -) - -eh = exthelper.exthelper() -obsexcmsg = utility.obsexcmsg - -def obsexcprg(ui, *args, **kwargs): - topic = 'obsmarkers exchange' - if ui.configbool('experimental', 'verbose-obsolescence-exchange', False): - topic = 'OBSEXC' - ui.progress(topic, *args, **kwargs) - -@eh.wrapfunction(exchange, '_pushdiscoveryobsmarkers') -def _pushdiscoveryobsmarkers(orig, pushop): - if (obsolete.isenabled(pushop.repo, obsolete.exchangeopt) - and pushop.repo.obsstore - and 'obsolete' in pushop.remote.listkeys('namespaces')): - repo = pushop.repo - obsexcmsg(repo.ui, "computing relevant nodes\n") - revs = list(repo.revs('::%ln', pushop.futureheads)) - unfi = repo.unfiltered() - cl = unfi.changelog - if not pushop.remote.capable('_evoext_obshash_0'): - # do not trust core yet - # return orig(pushop) - nodes = [cl.node(r) for r in revs] - if nodes: - obsexcmsg(repo.ui, "computing markers relevant to %i nodes\n" - % len(nodes)) - pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes) - else: - obsexcmsg(repo.ui, "markers already in sync\n") - pushop.outobsmarkers = [] - pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes) - return - - common = [] - obsexcmsg(repo.ui, "looking for common markers in %i nodes\n" - % len(revs)) - commonrevs = list(unfi.revs('::%ln', pushop.outgoing.commonheads)) - common = findcommonobsmarkers(pushop.ui, unfi, pushop.remote, - commonrevs) - - revs = list(unfi.revs('%ld - (::%ln)', revs, common)) - nodes = [cl.node(r) for r in revs] - if nodes: - obsexcmsg(repo.ui, "computing markers relevant to %i nodes\n" - % len(nodes)) - pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes) - else: - obsexcmsg(repo.ui, "markers already in sync\n") - pushop.outobsmarkers = [] - -@eh.extsetup -def _installobsmarkersdiscovery(ui): - olddisco = exchange.pushdiscoverymapping['obsmarker'] - - def newdisco(pushop): - _pushdiscoveryobsmarkers(olddisco, pushop) - exchange.pushdiscoverymapping['obsmarker'] = newdisco - -### Set discovery START - -from mercurial import dagutil -from mercurial import setdiscovery - -@eh.addattr(localrepo.localpeer, 'evoext_obshash') -def local_obshash(peer, nodes): - return serveronly._obshash(peer._repo, nodes) - -@eh.addattr(localrepo.localpeer, 'evoext_obshash1') -def local_obshash1(peer, nodes): - return serveronly._obshash(peer._repo, nodes, version=1) - -@eh.addattr(wireproto.wirepeer, 'evoext_obshash') -def peer_obshash(self, nodes): - d = self._call("evoext_obshash", nodes=wireproto.encodelist(nodes)) - try: - return wireproto.decodelist(d) - except ValueError: - self._abort(error.ResponseError(_("unexpected response:"), d)) - -@eh.addattr(wireproto.wirepeer, 'evoext_obshash1') -def peer_obshash1(self, nodes): - d = self._call("evoext_obshash1", nodes=wireproto.encodelist(nodes)) - try: - return wireproto.decodelist(d) - except ValueError: - self._abort(error.ResponseError(_("unexpected response:"), d)) - -def findcommonobsmarkers(ui, local, remote, probeset, - initialsamplesize=100, - fullsamplesize=200): - # from discovery - roundtrips = 0 - cl = local.changelog - dag = dagutil.revlogdag(cl) - missing = set() - common = set() - undecided = set(probeset) - totalnb = len(undecided) - ui.progress(_("comparing with other"), 0, total=totalnb) - _takefullsample = setdiscovery._takefullsample - if remote.capable('_evoext_obshash_1'): - getremotehash = remote.evoext_obshash1 - localhash = serveronly._obsrelsethashtreefm1(local) - else: - getremotehash = remote.evoext_obshash - localhash = serveronly._obsrelsethashtreefm0(local) - - while undecided: - - ui.note(_("sampling from both directions\n")) - if len(undecided) < fullsamplesize: - sample = set(undecided) - else: - sample = _takefullsample(dag, undecided, size=fullsamplesize) - - roundtrips += 1 - ui.progress(_("comparing with other"), totalnb - len(undecided), - total=totalnb) - ui.debug("query %i; still undecided: %i, sample size is: %i\n" - % (roundtrips, len(undecided), len(sample))) - # indices between sample and externalized version must match - sample = list(sample) - remotehash = getremotehash(dag.externalizeall(sample)) - - yesno = [localhash[ix][1] == remotehash[si] - for si, ix in enumerate(sample)] - - commoninsample = set(n for i, n in enumerate(sample) if yesno[i]) - common.update(dag.ancestorset(commoninsample, common)) - - missinginsample = [n for i, n in enumerate(sample) if not yesno[i]] - missing.update(dag.descendantset(missinginsample, missing)) - - undecided.difference_update(missing) - undecided.difference_update(common) - - ui.progress(_("comparing with other"), None) - result = dag.headsetofconnecteds(common) - ui.debug("%d total queries\n" % roundtrips) - - if not result: - return set([node.nullid]) - return dag.externalizeall(result) - - -_pushkeyescape = getattr(obsolete, '_pushkeyescape', None) - -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) - -@eh.wrapfunction(exchange, '_pushobsolete') -def _pushobsolete(orig, pushop): - """utility function to push obsolete markers to a remote""" - stepsdone = getattr(pushop, 'stepsdone', None) - if stepsdone is not None: - if 'obsmarkers' in stepsdone: - return - 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 (obsolete.isenabled(repo, obsolete.exchangeopt) and 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'): - 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: - rslts = [] - remotedata = _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") - - -@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() - serveronly._pushobsmarkers(peer._repo, data) - -def _buildpullobsmarkersboundaries(pullop): - """small funtion returning the argument for pull markers call - may to contains 'heads' and 'common'. skip the key for None. - - Its a separed functio to play around with strategy for that.""" - repo = pullop.repo - remote = pullop.remote - unfi = repo.unfiltered() - revs = unfi.revs('::(%ln - null)', pullop.common) - common = [node.nullid] - if remote.capable('_evoext_obshash_0'): - obsexcmsg(repo.ui, "looking for common markers in %i nodes\n" - % len(revs)) - common = findcommonobsmarkers(repo.ui, repo, remote, revs) - return {'heads': pullop.pulledsubset, 'common': common} - -@eh.uisetup -def addgetbundleargs(self): - wireproto.gboptsmap['evo_obscommon'] = 'nodes' - -@eh.wrapfunction(exchange, '_pullbundle2extraprepare') -def _addobscommontob2pull(orig, pullop, kwargs): - ret = orig(pullop, kwargs) - if ('obsmarkers' in kwargs and - pullop.remote.capable('_evoext_getbundle_obscommon')): - boundaries = _buildpullobsmarkersboundaries(pullop) - common = boundaries['common'] - if common != [node.nullid]: - kwargs['evo_obscommon'] = common - return ret - -@eh.wrapfunction(exchange, '_pullobsolete') -def _pullobsolete(orig, pullop): - if not obsolete.isenabled(pullop.repo, obsolete.exchangeopt): - return None - if 'obsmarkers' not in getattr(pullop, 'todosteps', ['obsmarkers']): - return None - if 'obsmarkers' in getattr(pullop, 'stepsdone', []): - return None - wirepull = pullop.remote.capable('_evoext_pullobsmarkers_0') - if not wirepull: - return orig(pullop) - if 'obsolete' not in pullop.remote.listkeys('namespaces'): - return None # remote opted out of obsolescence marker exchange - tr = None - ui = pullop.repo.ui - boundaries = _buildpullobsmarkersboundaries(pullop) - if not set(boundaries['heads']) - set(boundaries['common']): - obsexcmsg(ui, "nothing to pull\n") - return None - - obsexcmsg(ui, "pull obsolescence markers\n", True) - new = 0 - - if wirepull: - 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 serveronly._getobsmarkersstream(self._repo, heads=heads, - common=common) - -@eh.command( - 'debugobsrelsethashtree', - [('', 'v0', None, 'hash on marker format "0"'), - ('', 'v1', None, 'hash on marker format "1" (default)')], _('')) -def debugobsrelsethashtree(ui, repo, v0=False, v1=False): - """display Obsolete markers, Relevant Set, Hash Tree - changeset-node obsrelsethashtree-node - - It computed form the "orsht" of its parent and markers - relevant to the changeset itself.""" - if v0 and v1: - raise error.Abort('cannot only specify one format') - elif v0: - treefunc = serveronly._obsrelsethashtreefm0 - else: - treefunc = serveronly._obsrelsethashtreefm1 - - for chg, obs in treefunc(repo): - ui.status('%s %s\n' % (node.hex(chg), node.hex(obs))) - -_bestformat = max(obsolete.formats.keys())
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext3rd/evolve/obsexchange.py Tue Mar 07 14:19:12 2017 +0100 @@ -0,0 +1,417 @@ +# Code dedicated to the exchange of obsolescence markers +# +# Copyright 2017 Pierre-Yves David <pierre-yves.david@ens-lyon.org> +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from __future__ import absolute_import + +try: + import StringIO as io + StringIO = io.StringIO +except ImportError: + import io + StringIO = io.StringIO + +import errno +import socket + +from mercurial import ( + error, + exchange, + httppeer, + localrepo, + node, + obsolete, + util, + wireproto, +) +from mercurial.i18n import _ + +from . import ( + exthelper, + serveronly, + utility, +) + +eh = exthelper.exthelper() +obsexcmsg = utility.obsexcmsg + +def obsexcprg(ui, *args, **kwargs): + topic = 'obsmarkers exchange' + if ui.configbool('experimental', 'verbose-obsolescence-exchange', False): + topic = 'OBSEXC' + ui.progress(topic, *args, **kwargs) + +@eh.wrapfunction(exchange, '_pushdiscoveryobsmarkers') +def _pushdiscoveryobsmarkers(orig, pushop): + if (obsolete.isenabled(pushop.repo, obsolete.exchangeopt) + and pushop.repo.obsstore + and 'obsolete' in pushop.remote.listkeys('namespaces')): + repo = pushop.repo + obsexcmsg(repo.ui, "computing relevant nodes\n") + revs = list(repo.revs('::%ln', pushop.futureheads)) + unfi = repo.unfiltered() + cl = unfi.changelog + if not pushop.remote.capable('_evoext_obshash_0'): + # do not trust core yet + # return orig(pushop) + nodes = [cl.node(r) for r in revs] + if nodes: + obsexcmsg(repo.ui, "computing markers relevant to %i nodes\n" + % len(nodes)) + pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes) + else: + obsexcmsg(repo.ui, "markers already in sync\n") + pushop.outobsmarkers = [] + pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes) + return + + common = [] + obsexcmsg(repo.ui, "looking for common markers in %i nodes\n" + % len(revs)) + commonrevs = list(unfi.revs('::%ln', pushop.outgoing.commonheads)) + common = findcommonobsmarkers(pushop.ui, unfi, pushop.remote, + commonrevs) + + revs = list(unfi.revs('%ld - (::%ln)', revs, common)) + nodes = [cl.node(r) for r in revs] + if nodes: + obsexcmsg(repo.ui, "computing markers relevant to %i nodes\n" + % len(nodes)) + pushop.outobsmarkers = repo.obsstore.relevantmarkers(nodes) + else: + obsexcmsg(repo.ui, "markers already in sync\n") + pushop.outobsmarkers = [] + +@eh.extsetup +def _installobsmarkersdiscovery(ui): + olddisco = exchange.pushdiscoverymapping['obsmarker'] + + def newdisco(pushop): + _pushdiscoveryobsmarkers(olddisco, pushop) + exchange.pushdiscoverymapping['obsmarker'] = newdisco + +### Set discovery START + +from mercurial import dagutil +from mercurial import setdiscovery + +@eh.addattr(localrepo.localpeer, 'evoext_obshash') +def local_obshash(peer, nodes): + return serveronly._obshash(peer._repo, nodes) + +@eh.addattr(localrepo.localpeer, 'evoext_obshash1') +def local_obshash1(peer, nodes): + return serveronly._obshash(peer._repo, nodes, version=1) + +@eh.addattr(wireproto.wirepeer, 'evoext_obshash') +def peer_obshash(self, nodes): + d = self._call("evoext_obshash", nodes=wireproto.encodelist(nodes)) + try: + return wireproto.decodelist(d) + except ValueError: + self._abort(error.ResponseError(_("unexpected response:"), d)) + +@eh.addattr(wireproto.wirepeer, 'evoext_obshash1') +def peer_obshash1(self, nodes): + d = self._call("evoext_obshash1", nodes=wireproto.encodelist(nodes)) + try: + return wireproto.decodelist(d) + except ValueError: + self._abort(error.ResponseError(_("unexpected response:"), d)) + +def findcommonobsmarkers(ui, local, remote, probeset, + initialsamplesize=100, + fullsamplesize=200): + # from discovery + roundtrips = 0 + cl = local.changelog + dag = dagutil.revlogdag(cl) + missing = set() + common = set() + undecided = set(probeset) + totalnb = len(undecided) + ui.progress(_("comparing with other"), 0, total=totalnb) + _takefullsample = setdiscovery._takefullsample + if remote.capable('_evoext_obshash_1'): + getremotehash = remote.evoext_obshash1 + localhash = serveronly._obsrelsethashtreefm1(local) + else: + getremotehash = remote.evoext_obshash + localhash = serveronly._obsrelsethashtreefm0(local) + + while undecided: + + ui.note(_("sampling from both directions\n")) + if len(undecided) < fullsamplesize: + sample = set(undecided) + else: + sample = _takefullsample(dag, undecided, size=fullsamplesize) + + roundtrips += 1 + ui.progress(_("comparing with other"), totalnb - len(undecided), + total=totalnb) + ui.debug("query %i; still undecided: %i, sample size is: %i\n" + % (roundtrips, len(undecided), len(sample))) + # indices between sample and externalized version must match + sample = list(sample) + remotehash = getremotehash(dag.externalizeall(sample)) + + yesno = [localhash[ix][1] == remotehash[si] + for si, ix in enumerate(sample)] + + commoninsample = set(n for i, n in enumerate(sample) if yesno[i]) + common.update(dag.ancestorset(commoninsample, common)) + + missinginsample = [n for i, n in enumerate(sample) if not yesno[i]] + missing.update(dag.descendantset(missinginsample, missing)) + + undecided.difference_update(missing) + undecided.difference_update(common) + + ui.progress(_("comparing with other"), None) + result = dag.headsetofconnecteds(common) + ui.debug("%d total queries\n" % roundtrips) + + if not result: + return set([node.nullid]) + return dag.externalizeall(result) + + +_pushkeyescape = getattr(obsolete, '_pushkeyescape', None) + +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) + +@eh.wrapfunction(exchange, '_pushobsolete') +def _pushobsolete(orig, pushop): + """utility function to push obsolete markers to a remote""" + stepsdone = getattr(pushop, 'stepsdone', None) + if stepsdone is not None: + if 'obsmarkers' in stepsdone: + return + 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 (obsolete.isenabled(repo, obsolete.exchangeopt) and 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'): + 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: + rslts = [] + remotedata = _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") + + +@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() + serveronly._pushobsmarkers(peer._repo, data) + +def _buildpullobsmarkersboundaries(pullop): + """small funtion returning the argument for pull markers call + may to contains 'heads' and 'common'. skip the key for None. + + Its a separed functio to play around with strategy for that.""" + repo = pullop.repo + remote = pullop.remote + unfi = repo.unfiltered() + revs = unfi.revs('::(%ln - null)', pullop.common) + common = [node.nullid] + if remote.capable('_evoext_obshash_0'): + obsexcmsg(repo.ui, "looking for common markers in %i nodes\n" + % len(revs)) + common = findcommonobsmarkers(repo.ui, repo, remote, revs) + return {'heads': pullop.pulledsubset, 'common': common} + +@eh.uisetup +def addgetbundleargs(self): + wireproto.gboptsmap['evo_obscommon'] = 'nodes' + +@eh.wrapfunction(exchange, '_pullbundle2extraprepare') +def _addobscommontob2pull(orig, pullop, kwargs): + ret = orig(pullop, kwargs) + if ('obsmarkers' in kwargs and + pullop.remote.capable('_evoext_getbundle_obscommon')): + boundaries = _buildpullobsmarkersboundaries(pullop) + common = boundaries['common'] + if common != [node.nullid]: + kwargs['evo_obscommon'] = common + return ret + +@eh.wrapfunction(exchange, '_pullobsolete') +def _pullobsolete(orig, pullop): + if not obsolete.isenabled(pullop.repo, obsolete.exchangeopt): + return None + if 'obsmarkers' not in getattr(pullop, 'todosteps', ['obsmarkers']): + return None + if 'obsmarkers' in getattr(pullop, 'stepsdone', []): + return None + wirepull = pullop.remote.capable('_evoext_pullobsmarkers_0') + if not wirepull: + return orig(pullop) + if 'obsolete' not in pullop.remote.listkeys('namespaces'): + return None # remote opted out of obsolescence marker exchange + tr = None + ui = pullop.repo.ui + boundaries = _buildpullobsmarkersboundaries(pullop) + if not set(boundaries['heads']) - set(boundaries['common']): + obsexcmsg(ui, "nothing to pull\n") + return None + + obsexcmsg(ui, "pull obsolescence markers\n", True) + new = 0 + + if wirepull: + 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 serveronly._getobsmarkersstream(self._repo, heads=heads, + common=common) + +@eh.command( + 'debugobsrelsethashtree', + [('', 'v0', None, 'hash on marker format "0"'), + ('', 'v1', None, 'hash on marker format "1" (default)')], _('')) +def debugobsrelsethashtree(ui, repo, v0=False, v1=False): + """display Obsolete markers, Relevant Set, Hash Tree + changeset-node obsrelsethashtree-node + + It computed form the "orsht" of its parent and markers + relevant to the changeset itself.""" + if v0 and v1: + raise error.Abort('cannot only specify one format') + elif v0: + treefunc = serveronly._obsrelsethashtreefm0 + else: + treefunc = serveronly._obsrelsethashtreefm1 + + for chg, obs in treefunc(repo): + ui.status('%s %s\n' % (node.hex(chg), node.hex(obs))) + +_bestformat = max(obsolete.formats.keys())