Mercurial > public > mercurial-scm > hg
diff mercurial/exchangev2.py @ 40327:55836a34f41b
exchangev2: recognize narrow patterns when pulling
pulloperation instances were recently taught to record file
include and exclude patterns to facilitate narrow file transfer.
Teaching the exchangev2 code to transfer a subset of files is
as simple as constructing a narrow matcher from these patterns and
filtering all seen file paths through it.
Keep in mind that this change only influences file data: we're
still fetching all changeset and manifest data. So, there's still
a ton of "partial clone" to implement in exchangev2.
On a personal note, I derive gratification that this feature requires
very few lines of new code to implement.
To test this, we implemented a minimal extension which allows us to specify
--include/--exclude to clone. While the narrow extension provides these
arguments, I explicitly wanted to test this functionality without the
narrow extension enabled, as that extension monkeypatches various things
and I want to isolate the behavior of core Mercurial.
Differential Revision: https://phab.mercurial-scm.org/D5132
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Wed, 26 Sep 2018 14:38:43 -0700 |
parents | b843356d4ae1 |
children | 00a4cd368e3f |
line wrap: on
line diff
--- a/mercurial/exchangev2.py Tue Oct 09 08:50:13 2018 -0700 +++ b/mercurial/exchangev2.py Wed Sep 26 14:38:43 2018 -0700 @@ -19,6 +19,7 @@ bookmarks, error, mdiff, + narrowspec, phases, pycompat, setdiscovery, @@ -30,6 +31,23 @@ remote = pullop.remote tr = pullop.trmanager.transaction() + # We don't use the repo's narrow matcher here because the patterns passed + # to exchange.pull() could be different. + narrowmatcher = narrowspec.match(repo.root, + # Empty maps to nevermatcher. So always + # set includes if missing. + pullop.includepats or {'path:.'}, + pullop.excludepats) + + if pullop.includepats or pullop.excludepats: + pathfilter = {} + if pullop.includepats: + pathfilter[b'include'] = sorted(pullop.includepats) + if pullop.excludepats: + pathfilter[b'exclude'] = sorted(pullop.excludepats) + else: + pathfilter = None + # Figure out what needs to be fetched. common, fetch, remoteheads = _pullchangesetdiscovery( repo, remote, pullop.heads, abortwhenunrelated=pullop.force) @@ -63,8 +81,8 @@ # Find all file nodes referenced by added manifests and fetch those # revisions. - fnodes = _derivefilesfrommanifests(repo, manres['added']) - _fetchfilesfromcsets(repo, tr, remote, fnodes, csetres['added'], + fnodes = _derivefilesfrommanifests(repo, narrowmatcher, manres['added']) + _fetchfilesfromcsets(repo, tr, remote, pathfilter, fnodes, csetres['added'], manres['linkrevs']) def _pullchangesetdiscovery(repo, remote, heads, abortwhenunrelated=True): @@ -315,7 +333,7 @@ 'linkrevs': linkrevs, } -def _derivefilesfrommanifests(repo, manifestnodes): +def _derivefilesfrommanifests(repo, matcher, manifestnodes): """Determine what file nodes are relevant given a set of manifest nodes. Returns a dict mapping file paths to dicts of file node to first manifest @@ -340,7 +358,8 @@ md = m.readfast() for path, fnode in md.items(): - fnodes[path].setdefault(fnode, manifestnode) + if matcher(path): + fnodes[path].setdefault(fnode, manifestnode) progress.increment() @@ -421,7 +440,8 @@ locallinkrevs[path].__getitem__, weakref.proxy(tr)) -def _fetchfilesfromcsets(repo, tr, remote, fnodes, csets, manlinkrevs): +def _fetchfilesfromcsets(repo, tr, remote, pathfilter, fnodes, csets, + manlinkrevs): """Fetch file data from explicit changeset revisions.""" def iterrevisions(objs, remaining, progress): @@ -481,6 +501,9 @@ b'haveparents': True, } + if pathfilter: + args[b'pathfilter'] = pathfilter + objs = e.callcommand(b'filesdata', args).result() # First object is an overall header.