Mercurial > public > mercurial-scm > hg
diff mercurial/exchange.py @ 37498:aacfca6f9767
wireproto: support for pullbundles
Pullbundles are similar to clonebundles, but served as normal inline
bundle streams. They are almost transparent to the client -- the only
visible effect is that the client might get less changes than what it
asked for, i.e. not all requested head revisions are provided.
The client announces support for the necessary retries with the
partial-pull capability. After receiving a partial bundle, it updates
the set of revisions shared with the server and drops all now-known
heads from the request list. It will then rerun getbundle until
no changes are received or all remote heads are present.
Extend badserverext to support per-socket limit, i.e. don't assume that
the same limits should be applied to all sockets.
Differential Revision: https://phab.mercurial-scm.org/D1856
author | Joerg Sonnenberger <joerg@bec.de> |
---|---|
date | Thu, 18 Jan 2018 12:54:01 +0100 |
parents | f7d3915d5b3a |
children | cc8c06835097 |
line wrap: on
line diff
--- a/mercurial/exchange.py Fri Apr 06 22:39:58 2018 -0700 +++ b/mercurial/exchange.py Thu Jan 18 12:54:01 2018 +0100 @@ -1375,6 +1375,44 @@ if self._tr is not None: self._tr.release() +def _fullpullbundle2(repo, pullop): + # The server may send a partial reply, i.e. when inlining + # pre-computed bundles. In that case, update the common + # set based on the results and pull another bundle. + # + # There are two indicators that the process is finished: + # - no changeset has been added, or + # - all remote heads are known locally. + # The head check must use the unfiltered view as obsoletion + # markers can hide heads. + unfi = repo.unfiltered() + unficl = unfi.changelog + def headsofdiff(h1, h2): + """Returns heads(h1 % h2)""" + res = unfi.set('heads(%ln %% %ln)', h1, h2) + return set(ctx.node() for ctx in res) + def headsofunion(h1, h2): + """Returns heads((h1 + h2) - null)""" + res = unfi.set('heads((%ln + %ln - null))', h1, h2) + return set(ctx.node() for ctx in res) + while True: + old_heads = unficl.heads() + clstart = len(unficl) + _pullbundle2(pullop) + if changegroup.NARROW_REQUIREMENT in repo.requirements: + # XXX narrow clones filter the heads on the server side during + # XXX getbundle and result in partial replies as well. + # XXX Disable pull bundles in this case as band aid to avoid + # XXX extra round trips. + break + if clstart == len(unficl): + break + if all(unficl.hasnode(n) for n in pullop.rheads): + break + new_heads = headsofdiff(unficl.heads(), old_heads) + pullop.common = headsofunion(new_heads, pullop.common) + pullop.rheads = set(pullop.rheads) - pullop.common + def pull(repo, remote, heads=None, force=False, bookmarks=(), opargs=None, streamclonerequested=None): """Fetch repository data from a remote. @@ -1420,7 +1458,7 @@ streamclone.maybeperformlegacystreamclone(pullop) _pulldiscovery(pullop) if pullop.canusebundle2: - _pullbundle2(pullop) + _fullpullbundle2(repo, pullop) _pullchangeset(pullop) _pullphase(pullop) _pullbookmarks(pullop)