##// END OF EJS Templates
exchangev2: fetch manifest revisions...
exchangev2: fetch manifest revisions Now that the server has support for retrieving manifest data, we can implement the client bits to call it. We teach the changeset fetching code to capture the manifest revisions that are encountered on incoming changesets. We then feed this into a new function which filters out known manifests and then batches up manifest data requests to the server. This is different from the previous wire protocol in a few notable ways. First, the client fetches manifest data separately and explicitly. Before, we'd ask the server for data pertaining to some changesets (via a "getbundle" command) and manifests (and files) would be sent automatically. Providing an API for looking up just manifest data separately gives clients much more flexibility for manifest management. For example, a client may choose to only fetch manifest data on demand instead of prefetching it (i.e. partial clone). Second, we send N commands to the server for manifest retrieval instead of 1. This property has a few nice side-effects. One is that the deterministic nature of the requests lends itself to server-side caching. For example, say the remote has 50,000 manifests. If the server is configured to cache responses, each time a new commit arrives, you will have a cache miss and need to regenerate all outgoing data. But if you makes N requests requesting 10,000 manifests each, a new commit will still yield cache hits on the initial, unchanged manifest batches/requests. A derived benefit from these properties is that resumable clone is conceptually simpler to implement. When making a monolithic request for all of the repository data, recovering from an interrupted clone is hard because the server was in the driver's seat and was maintaining state about all the data that needed transferred. With the client driving fetching, the client can persist the set of unfetched entities and retry/resume a fetch if something goes wrong. Or we can fetch all data N changesets at a time and slowly build up a repository. This approach is drastically easier to implement when we have server APIs exposing low-level repository primitives (such as manifests and files). We don't yet support tree manifests. But it should be possible to implement that with the existing wire protocol command. Differential Revision: https://phab.mercurial-scm.org/D4489

File last commit:

r38611:21be76e0 default
r39674:d292328e default
Show More
pathutil.py
264 lines | 9.5 KiB | text/x-python | PythonLexer
Gregory Szorc
pathutil: use absolute_import
r25964 from __future__ import absolute_import
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033
Gregory Szorc
pathutil: use absolute_import
r25964 import errno
import os
import posixpath
import stat
from .i18n import _
from . import (
encoding,
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 error,
Pulkit Goyal
py3: replace os.sep with pycompat.ossep (part 2 of 4)...
r30614 pycompat,
Gregory Szorc
pathutil: use absolute_import
r25964 util,
)
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033
Augie Fackler
pathauditor: check for codepoints ignored on OS X
r23598 def _lowerclean(s):
return encoding.hfsignoreclean(s.lower())
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 class pathauditor(object):
'''ensure that a filesystem path contains no banned components.
the following properties of a path are checked:
- ends with a directory separator
- under top-level .hg
- starts at the root of a windows drive
- contains ".."
Pierre-Yves David
pathauditor: add a way to skip file system check...
r27232
More check are also done about the file system states:
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 - traverses a symlink (e.g. a/symlink_here/b)
- inside a nested repository (a callback can be used to approve
some nested repositories, e.g., subrepositories)
Pierre-Yves David
pathauditor: add a way to skip file system check...
r27232
The file system checks are only done when 'realfs' is set to True (the
default). They should be disable then we are auditing path for operation on
stored history.
Yuya Nishihara
pathauditor: disable cache of audited paths by default (issue5628)...
r33722
If 'cached' is set to True, audited paths and sub-directories are cached.
Be careful to not keep the cache of unmanaged directories for long because
audited paths may be replaced with symlinks.
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 '''
Yuya Nishihara
pathauditor: disable cache of audited paths by default (issue5628)...
r33722 def __init__(self, root, callback=None, realfs=True, cached=False):
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 self.audited = set()
self.auditeddir = set()
self.root = root
Pierre-Yves David
pathauditor: add a way to skip file system check...
r27232 self._realfs = realfs
Yuya Nishihara
pathauditor: disable cache of audited paths by default (issue5628)...
r33722 self._cached = cached
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 self.callback = callback
Martin von Zweigbergk
util: rename checkcase() to fscasesensitive() (API)...
r29889 if os.path.lexists(root) and not util.fscasesensitive(root):
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 self.normcase = util.normcase
else:
self.normcase = lambda x: x
Boris Feld
vfs: allow to pass more argument to audit...
r33435 def __call__(self, path, mode=None):
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 '''Check the relative path.
path may contain a pattern (e.g. foodir/**.txt)'''
path = util.localpath(path)
normpath = self.normcase(path)
if normpath in self.audited:
return
# AIX ignores "/" at end of path, others raise EISDIR.
if util.endswithsep(path):
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("path ends in directory separator: %s") % path)
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 parts = util.splitpath(path)
if (os.path.splitdrive(path)[0]
Augie Fackler
pathauditor: check for codepoints ignored on OS X
r23598 or _lowerclean(parts[0]) in ('.hg', '.hg.', '')
Yuya Nishihara
py3: make os.pardir a bytes
r36665 or pycompat.ospardir in parts):
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("path contains illegal component: %s") % path)
Matt Mackall
pathauditor: check for Windows shortname aliases
r23599 # Windows shortname aliases
for p in parts:
if "~" in p:
first, last = p.split("~", 1)
if last.isdigit() and first.upper() in ["HG", "HG8B6C"]:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("path contains illegal component: %s")
Matt Mackall
pathauditor: check for Windows shortname aliases
r23599 % path)
Augie Fackler
pathauditor: check for codepoints ignored on OS X
r23598 if '.hg' in _lowerclean(path):
lparts = [_lowerclean(p.lower()) for p in parts]
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 for p in '.hg', '.hg.':
if p in lparts[1:]:
pos = lparts.index(p)
base = os.path.join(*parts[:pos])
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("path '%s' is inside nested repo %r")
Yuya Nishihara
py3: fix formatting of path-auditing errors
r36667 % (path, pycompat.bytestr(base)))
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033
normparts = util.splitpath(normpath)
assert len(parts) == len(normparts)
parts.pop()
normparts.pop()
prefixes = []
Durham Goode
pathauditor: change parts verification order to be root first...
r28087 # It's important that we check the path parts starting from the root.
Mads Kiilerich
spelling: fixes of non-dictionary words
r30332 # This means we won't accidentally traverse a symlink into some other
Durham Goode
pathauditor: change parts verification order to be root first...
r28087 # filesystem (which is potentially expensive to access).
for i in range(len(parts)):
Pulkit Goyal
py3: replace os.sep with pycompat.ossep (part 2 of 4)...
r30614 prefix = pycompat.ossep.join(parts[:i + 1])
normprefix = pycompat.ossep.join(normparts[:i + 1])
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 if normprefix in self.auditeddir:
Durham Goode
pathauditor: change parts verification order to be root first...
r28087 continue
Pierre-Yves David
pathauditor: add a way to skip file system check...
r27232 if self._realfs:
self._checkfs(prefix, path)
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 prefixes.append(normprefix)
Yuya Nishihara
pathauditor: disable cache of audited paths by default (issue5628)...
r33722 if self._cached:
self.audited.add(normpath)
# only add prefixes to the cache after checking everything: we don't
# want to add "foo/bar/baz" before checking if there's a "foo/.hg"
self.auditeddir.update(prefixes)
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033
Pierre-Yves David
pathauditor: move file system specific check in their own function...
r27231 def _checkfs(self, prefix, path):
"""raise exception if a file system backed check fails"""
curpath = os.path.join(self.root, prefix)
try:
st = os.lstat(curpath)
except OSError as err:
# EINVAL can be raised as invalid path syntax under win32.
# They must be ignored for patterns can be checked too.
if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
raise
else:
if stat.S_ISLNK(st.st_mode):
Yuya Nishihara
py3: fix formatting of path-auditing errors
r36667 msg = (_('path %r traverses symbolic link %r')
% (pycompat.bytestr(path), pycompat.bytestr(prefix)))
Pierre-Yves David
pathutil: use temporary variables instead of complicated wrapping...
r27235 raise error.Abort(msg)
Pierre-Yves David
pathauditor: move file system specific check in their own function...
r27231 elif (stat.S_ISDIR(st.st_mode) and
os.path.isdir(os.path.join(curpath, '.hg'))):
if not self.callback or not self.callback(curpath):
Pierre-Yves David
pathutil: use temporary variables instead of complicated wrapping...
r27235 msg = _("path '%s' is inside nested repo %r")
Yuya Nishihara
py3: fix formatting of path-auditing errors
r36667 raise error.Abort(msg % (path, pycompat.bytestr(prefix)))
Pierre-Yves David
pathauditor: move file system specific check in their own function...
r27231
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 def check(self, path):
try:
self(path)
return True
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 except (OSError, error.Abort):
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 return False
def canonpath(root, cwd, myname, auditor=None):
Matt Harbison
pathutil: add doctests for canonpath()...
r34981 '''return the canonical path of myname, given cwd and root
>>> def check(root, cwd, myname):
... a = pathauditor(root, realfs=False)
... try:
... return canonpath(root, cwd, myname, a)
... except error.Abort:
... return 'aborted'
>>> def unixonly(root, cwd, myname, expected='aborted'):
... if pycompat.iswindows:
... return expected
... return check(root, cwd, myname)
>>> def winonly(root, cwd, myname, expected='aborted'):
... if not pycompat.iswindows:
... return expected
... return check(root, cwd, myname)
>>> winonly(b'd:\\\\repo', b'c:\\\\dir', b'filename')
'aborted'
>>> winonly(b'c:\\\\repo', b'c:\\\\dir', b'filename')
'aborted'
>>> winonly(b'c:\\\\repo', b'c:\\\\', b'filename')
'aborted'
>>> winonly(b'c:\\\\repo', b'c:\\\\', b'repo\\\\filename',
... b'filename')
'filename'
>>> winonly(b'c:\\\\repo', b'c:\\\\repo', b'filename', b'filename')
'filename'
>>> winonly(b'c:\\\\repo', b'c:\\\\repo\\\\subdir', b'filename',
... b'subdir/filename')
'subdir/filename'
>>> unixonly(b'/repo', b'/dir', b'filename')
'aborted'
>>> unixonly(b'/repo', b'/', b'filename')
'aborted'
>>> unixonly(b'/repo', b'/', b'repo/filename', b'filename')
'filename'
>>> unixonly(b'/repo', b'/repo', b'filename', b'filename')
'filename'
>>> unixonly(b'/repo', b'/repo/subdir', b'filename', b'subdir/filename')
'subdir/filename'
'''
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 if util.endswithsep(root):
rootsep = root
else:
Pulkit Goyal
py3: replace os.sep with pycompat.ossep (part 2 of 4)...
r30614 rootsep = root + pycompat.ossep
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 name = myname
if not os.path.isabs(name):
name = os.path.join(root, cwd, name)
name = os.path.normpath(name)
if auditor is None:
auditor = pathauditor(root)
if name != rootsep and name.startswith(rootsep):
name = name[len(rootsep):]
auditor(name)
return util.pconvert(name)
elif name == root:
return ''
else:
# Determine whether `name' is in the hierarchy at or beneath `root',
# by iterating name=dirname(name) until that causes no change (can't
# check name == '/', because that doesn't work on windows). The list
# `rel' holds the reversed list of components making up the relative
# file name we want.
rel = []
while True:
try:
s = util.samefile(name, root)
except OSError:
s = False
if s:
if not rel:
# name was actually the same as root (maybe a symlink)
return ''
rel.reverse()
name = os.path.join(*rel)
auditor(name)
return util.pconvert(name)
dirname, basename = util.split(name)
rel.append(basename)
if dirname == name:
break
name = dirname
Matt Harbison
pathutil: hint if a path is root relative instead of cwd relative (issue4663)...
r25011 # A common mistake is to use -R, but specify a file relative to the repo
# instead of cwd. Detect that case, and provide a hint to the user.
hint = None
try:
Matt Mackall
canonpath: fix infinite recursion
r25022 if cwd != root:
canonpath(root, root, myname, auditor)
Matt Harbison
pathutil: use util.pathto() to calculate relative cwd in canonpath()...
r34966 relpath = util.pathto(root, cwd, '')
Yuya Nishihara
py3: use bytes.endswith() instead of bytes[n]
r38611 if relpath.endswith(pycompat.ossep):
Matt Harbison
pathutil: use util.pathto() to calculate relative cwd in canonpath()...
r34966 relpath = relpath[:-1]
hint = (_("consider using '--cwd %s'") % relpath)
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 except error.Abort:
Matt Harbison
pathutil: hint if a path is root relative instead of cwd relative (issue4663)...
r25011 pass
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("%s not under root '%s'") % (myname, root),
Matt Harbison
pathutil: hint if a path is root relative instead of cwd relative (issue4663)...
r25011 hint=hint)
FUJIWARA Katsunori
subrepo: normalize path in the specific way for problematic encodings...
r21568
def normasprefix(path):
'''normalize the specified path as path prefix
Mads Kiilerich
spelling: fixes from proofreading of spell checker issues
r23139 Returned value can be used safely for "p.startswith(prefix)",
FUJIWARA Katsunori
subrepo: normalize path in the specific way for problematic encodings...
r21568 "p[len(prefix):]", and so on.
For efficiency, this expects "path" argument to be already
normalized by "os.path.normpath", "os.path.realpath", and so on.
See also issue3033 for detail about need of this function.
Yuya Nishihara
py3: use bytes os.sep in doctest of pathutil.py
r34255 >>> normasprefix(b'/foo/bar').replace(pycompat.ossep, b'/')
FUJIWARA Katsunori
subrepo: normalize path in the specific way for problematic encodings...
r21568 '/foo/bar/'
Yuya Nishihara
py3: use bytes os.sep in doctest of pathutil.py
r34255 >>> normasprefix(b'/').replace(pycompat.ossep, b'/')
FUJIWARA Katsunori
subrepo: normalize path in the specific way for problematic encodings...
r21568 '/'
'''
d, p = os.path.splitdrive(path)
Pulkit Goyal
py3: replace os.sep with pycompat.ossep (part 2 of 4)...
r30614 if len(p) != len(pycompat.ossep):
return path + pycompat.ossep
FUJIWARA Katsunori
subrepo: normalize path in the specific way for problematic encodings...
r21568 else:
return path
Durham Goode
pathutil: add dirname and join functions...
r25281
Augie Fackler
pathutil: demote two local functions to just be forwards...
r25286 # forward two methods from posixpath that do what we need, but we'd
# rather not let our internals know that we're thinking in posix terms
# - instead we'll let them be oblivious.
join = posixpath.join
dirname = posixpath.dirname