##// END OF EJS Templates
relnotes: add note about changes to match.{explicit,reverse}dir...
relnotes: add note about changes to match.{explicit,reverse}dir Differential Revision: https://phab.mercurial-scm.org/D7508

File last commit:

r43809:313e3a27 default
r44166:32aa6cef default
Show More
shallowutil.py
536 lines | 15.0 KiB | text/x-python | PythonLexer
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 # shallowutil.py -- remotefilelog utilities
#
# Copyright 2014 Facebook, Inc.
#
# 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
import collections
import errno
import hashlib
import os
import stat
import struct
import tempfile
from mercurial.i18n import _
Gregory Szorc
py3: manually import pycompat.open into files that need it...
r43355 from mercurial.pycompat import open
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 from mercurial import (
error,
Pulkit Goyal
py3: use node.hex(h.digest()) instead of h.hexdigest()...
r40648 node,
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 pycompat,
revlog,
util,
)
from mercurial.utils import (
storageutil,
stringutil,
)
from . import constants
if not pycompat.iswindows:
import grp
Augie Fackler
formatting: blacken the codebase...
r43346
Pulkit Goyal
shallowutil: introduce a helper function isenabled()...
r40549 def isenabled(repo):
"""returns whether the repository is remotefilelog enabled or not"""
return constants.SHALLOWREPO_REQUIREMENT in repo.requirements
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def getcachekey(reponame, file, id):
Pulkit Goyal
py3: use node.hex(h.digest()) instead of h.hexdigest()...
r40648 pathhash = node.hex(hashlib.sha1(file).digest())
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 return os.path.join(reponame, pathhash[:2], pathhash[2:], id)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def getlocalkey(file, id):
Pulkit Goyal
py3: use node.hex(h.digest()) instead of h.hexdigest()...
r40648 pathhash = node.hex(hashlib.sha1(file).digest())
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 return os.path.join(pathhash, id)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def getcachepath(ui, allowempty=False):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 cachepath = ui.config(b"remotefilelog", b"cachepath")
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 if not cachepath:
if allowempty:
return None
else:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.Abort(
Martin von Zweigbergk
cleanup: join string literals that are already on one line...
r43387 _(b"could not find config option remotefilelog.cachepath")
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 return util.expandpath(cachepath)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def getcachepackpath(repo, category):
cachepath = getcachepath(repo.ui)
if category != constants.FILEPACK_CATEGORY:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return os.path.join(cachepath, repo.name, b'packs', category)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return os.path.join(cachepath, repo.name, b'packs')
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def getlocalpackpath(base, category):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return os.path.join(base, b'packs', category)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def createrevlogtext(text, copyfrom=None, copyrev=None):
"""returns a string that matches the revlog contents in a
traditional revlog
"""
meta = {}
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if copyfrom or text.startswith(b'\1\n'):
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 if copyfrom:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 meta[b'copy'] = copyfrom
meta[b'copyrev'] = copyrev
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 text = storageutil.packmeta(meta, text)
return text
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def parsemeta(text):
"""parse mercurial filelog metadata"""
meta, size = storageutil.parsemeta(text)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if text.startswith(b'\1\n'):
s = text.index(b'\1\n', 2)
Augie Fackler
formatting: blacken the codebase...
r43346 text = text[s + 2 :]
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 return meta or {}, text
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def sumdicts(*dicts):
"""Adds all the values of *dicts together into one dictionary. This assumes
the values in *dicts are all summable.
e.g. [{'a': 4', 'b': 2}, {'b': 3, 'c': 1}] -> {'a': 4, 'b': 5, 'c': 1}
"""
result = collections.defaultdict(lambda: 0)
for dict in dicts:
Gregory Szorc
py3: define and use pycompat.iteritems() for hgext/...
r43375 for k, v in pycompat.iteritems(dict):
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 result[k] += v
return result
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def prefixkeys(dict, prefix):
"""Returns ``dict`` with ``prefix`` prepended to all its keys."""
result = {}
Gregory Szorc
py3: define and use pycompat.iteritems() for hgext/...
r43375 for k, v in pycompat.iteritems(dict):
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 result[prefix + k] = v
return result
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def reportpackmetrics(ui, prefix, *stores):
dicts = [s.getmetrics() for s in stores]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 dict = prefixkeys(sumdicts(*dicts), prefix + b'_')
ui.log(prefix + b"_packsizes", b"\n", **pycompat.strkwargs(dict))
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def _parsepackmeta(metabuf):
"""parse datapack meta, bytes (<metadata-list>) -> dict
The dict contains raw content - both keys and values are strings.
Upper-level business may want to convert some of them to other types like
integers, on their own.
raise ValueError if the data is corrupted
"""
metadict = {}
offset = 0
buflen = len(metabuf)
while buflen - offset >= 3:
Augie Fackler
formatting: blacken the codebase...
r43346 key = metabuf[offset : offset + 1]
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 offset += 1
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 metalen = struct.unpack_from(b'!H', metabuf, offset)[0]
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 offset += 2
if offset + metalen > buflen:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise ValueError(b'corrupted metadata: incomplete buffer')
Augie Fackler
formatting: blacken the codebase...
r43346 value = metabuf[offset : offset + metalen]
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 metadict[key] = value
offset += metalen
if offset != buflen:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise ValueError(b'corrupted metadata: redundant data')
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 return metadict
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def _buildpackmeta(metadict):
"""reverse of _parsepackmeta, dict -> bytes (<metadata-list>)
The dict contains raw content - both keys and values are strings.
Upper-level business may want to serialize some of other types (like
integers) to strings before calling this function.
raise ProgrammingError when metadata key is illegal, or ValueError if
length limit is exceeded
"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 metabuf = b''
Gregory Szorc
py3: define and use pycompat.iteritems() for hgext/...
r43375 for k, v in sorted(pycompat.iteritems((metadict or {}))):
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 if len(k) != 1:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ProgrammingError(b'packmeta: illegal key: %s' % k)
Augie Fackler
formatting: blacken the codebase...
r43346 if len(v) > 0xFFFE:
raise ValueError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'metadata value is too long: 0x%x > 0xfffe' % len(v)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 metabuf += k
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 metabuf += struct.pack(b'!H', len(v))
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 metabuf += v
# len(metabuf) is guaranteed representable in 4 bytes, because there are
# only 256 keys, and for each value, len(value) <= 0xfffe.
return metabuf
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 _metaitemtypes = {
Augie Fackler
remotefilelog: fix various minor py3 problems...
r40562 constants.METAKEYFLAG: (int, pycompat.long),
constants.METAKEYSIZE: (int, pycompat.long),
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 }
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def buildpackmeta(metadict):
"""like _buildpackmeta, but typechecks metadict and normalize it.
This means, METAKEYSIZE and METAKEYSIZE should have integers as values,
and METAKEYFLAG will be dropped if its value is 0.
"""
newmeta = {}
Gregory Szorc
py3: define and use pycompat.iteritems() for hgext/...
r43375 for k, v in pycompat.iteritems(metadict or {}):
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 expectedtype = _metaitemtypes.get(k, (bytes,))
if not isinstance(v, expectedtype):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ProgrammingError(b'packmeta: wrong type of key %s' % k)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 # normalize int to binary buffer
if int in expectedtype:
# optimization: remove flag if it's 0 to save space
if k == constants.METAKEYFLAG and v == 0:
continue
v = int2bin(v)
newmeta[k] = v
return _buildpackmeta(newmeta)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def parsepackmeta(metabuf):
"""like _parsepackmeta, but convert fields to desired types automatically.
This means, METAKEYFLAG and METAKEYSIZE fields will be converted to
integers.
"""
metadict = _parsepackmeta(metabuf)
Gregory Szorc
py3: define and use pycompat.iteritems() for hgext/...
r43375 for k, v in pycompat.iteritems(metadict):
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 if k in _metaitemtypes and int in _metaitemtypes[k]:
metadict[k] = bin2int(v)
return metadict
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def int2bin(n):
"""convert a non-negative integer to raw binary buffer"""
buf = bytearray()
while n > 0:
Augie Fackler
formatting: blacken the codebase...
r43346 buf.insert(0, n & 0xFF)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 n >>= 8
return bytes(buf)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def bin2int(buf):
"""the reverse of int2bin, convert a binary buffer to an integer"""
x = 0
for b in bytearray(buf):
x <<= 8
x |= b
return x
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def parsesizeflags(raw):
"""given a remotefilelog blob, return (headersize, rawtextsize, flags)
see remotefilelogserver.createfileblob for the format.
raise RuntimeError if the content is illformed.
"""
flags = revlog.REVIDX_DEFAULT_FLAGS
size = None
try:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 index = raw.index(b'\0')
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 header = raw[:index]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if header.startswith(b'v'):
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 # v1 and above, header starts with 'v'
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if header.startswith(b'v1\n'):
for s in header.split(b'\n'):
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 if s.startswith(constants.METAKEYSIZE):
Augie Fackler
formatting: blacken the codebase...
r43346 size = int(s[len(constants.METAKEYSIZE) :])
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 elif s.startswith(constants.METAKEYFLAG):
Augie Fackler
formatting: blacken the codebase...
r43346 flags = int(s[len(constants.METAKEYFLAG) :])
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 else:
Augie Fackler
formatting: blacken the codebase...
r43346 raise RuntimeError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'unsupported remotefilelog header: %s' % header
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 else:
# v0, str(int(size)) is the header
size = int(header)
except ValueError:
Augie Fackler
cleanup: remove pointless r-prefixes on double-quoted strings...
r43809 raise RuntimeError("unexpected remotefilelog header: illegal format")
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 if size is None:
Augie Fackler
cleanup: remove pointless r-prefixes on double-quoted strings...
r43809 raise RuntimeError("unexpected remotefilelog header: no size found")
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 return index + 1, size, flags
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def buildfileblobheader(size, flags, version=None):
"""return the header of a remotefilelog blob.
see remotefilelogserver.createfileblob for the format.
approximately the reverse of parsesizeflags.
version could be 0 or 1, or None (auto decide).
"""
# choose v0 if flags is empty, otherwise v1
if version is None:
version = int(bool(flags))
if version == 1:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 header = b'v1\n%s%d\n%s%d' % (
Augie Fackler
formatting: blacken the codebase...
r43346 constants.METAKEYSIZE,
size,
constants.METAKEYFLAG,
flags,
)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 elif version == 0:
if flags:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ProgrammingError(b'fileblob v0 does not support flag')
header = b'%d' % size
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ProgrammingError(b'unknown fileblob version %d' % version)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 return header
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def ancestormap(raw):
offset, size, flags = parsesizeflags(raw)
start = offset + size
mapping = {}
while start < len(raw):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 divider = raw.index(b'\0', start + 80)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
Augie Fackler
formatting: blacken the codebase...
r43346 currentnode = raw[start : (start + 20)]
p1 = raw[(start + 20) : (start + 40)]
p2 = raw[(start + 40) : (start + 60)]
linknode = raw[(start + 60) : (start + 80)]
copyfrom = raw[(start + 80) : divider]
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
mapping[currentnode] = (p1, p2, linknode, copyfrom)
start = divider + 1
return mapping
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def readfile(path):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 f = open(path, b'rb')
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 try:
result = f.read()
# we should never have empty files
if not result:
os.remove(path)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise IOError(b"empty file: %s" % path)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
return result
finally:
f.close()
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def unlinkfile(filepath):
if pycompat.iswindows:
# On Windows, os.unlink cannnot delete readonly files
os.chmod(filepath, stat.S_IWUSR)
os.unlink(filepath)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def renamefile(source, destination):
if pycompat.iswindows:
# On Windows, os.rename cannot rename readonly files
# and cannot overwrite destination if it exists
os.chmod(source, stat.S_IWUSR)
if os.path.isfile(destination):
os.chmod(destination, stat.S_IWUSR)
os.unlink(destination)
os.rename(source, destination)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def writefile(path, content, readonly=False):
dirname, filename = os.path.split(path)
if not os.path.exists(dirname):
try:
os.makedirs(dirname)
except OSError as ex:
if ex.errno != errno.EEXIST:
raise
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fd, temp = tempfile.mkstemp(prefix=b'.%s-' % filename, dir=dirname)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 os.close(fd)
try:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 f = util.posixfile(temp, b'wb')
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 f.write(content)
f.close()
if readonly:
mode = 0o444
else:
# tempfiles are created with 0o600, so we need to manually set the
# mode.
oldumask = os.umask(0)
# there's no way to get the umask without modifying it, so set it
# back
os.umask(oldumask)
mode = ~oldumask
renamefile(temp, path)
os.chmod(path, mode)
except Exception:
try:
unlinkfile(temp)
except OSError:
pass
raise
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def sortnodes(nodes, parentfunc):
"""Topologically sorts the nodes, using the parentfunc to find
the parents of nodes."""
nodes = set(nodes)
childmap = {}
parentmap = {}
roots = []
# Build a child and parent map
for n in nodes:
parents = [p for p in parentfunc(n) if p in nodes]
parentmap[n] = set(parents)
for p in parents:
childmap.setdefault(p, set()).add(n)
if not parents:
roots.append(n)
roots.sort()
# Process roots, adding children to the queue as they become roots
results = []
while roots:
n = roots.pop(0)
results.append(n)
if n in childmap:
children = childmap[n]
for c in children:
childparents = parentmap[c]
childparents.remove(n)
if len(childparents) == 0:
# insert at the beginning, that way child nodes
# are likely to be output immediately after their
# parents. This gives better compression results.
roots.insert(0, c)
return results
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def readexactly(stream, n):
'''read n bytes from stream.read and abort if less was available'''
s = stream.read(n)
if len(s) < n:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.Abort(
Martin von Zweigbergk
cleanup: join string literals that are already on one line...
r43387 _(b"stream ended unexpectedly (got %d bytes, expected %d)")
Augie Fackler
formatting: blacken the codebase...
r43346 % (len(s), n)
)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 return s
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def readunpack(stream, fmt):
data = readexactly(stream, struct.calcsize(fmt))
return struct.unpack(fmt, data)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def readpath(stream):
rawlen = readexactly(stream, constants.FILENAMESIZE)
pathlen = struct.unpack(constants.FILENAMESTRUCT, rawlen)[0]
return readexactly(stream, pathlen)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def readnodelist(stream):
rawlen = readexactly(stream, constants.NODECOUNTSIZE)
nodecount = struct.unpack(constants.NODECOUNTSTRUCT, rawlen)[0]
for i in pycompat.xrange(nodecount):
yield readexactly(stream, constants.NODESIZE)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def readpathlist(stream):
rawlen = readexactly(stream, constants.PATHCOUNTSIZE)
pathcount = struct.unpack(constants.PATHCOUNTSTRUCT, rawlen)[0]
for i in pycompat.xrange(pathcount):
yield readpath(stream)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def getgid(groupname):
try:
Augie Fackler
shallowutil: fsdecode the bytes group name before passing to os...
r41294 gid = grp.getgrnam(pycompat.fsdecode(groupname)).gr_gid
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 return gid
except KeyError:
return None
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def setstickygroupdir(path, gid, warn=None):
if gid is None:
return
try:
os.chown(path, -1, gid)
os.chmod(path, 0o2775)
except (IOError, OSError) as ex:
if warn:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 warn(_(b'unable to chown/chmod on %s: %s\n') % (path, ex))
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def mkstickygroupdir(ui, path):
"""Creates the given directory (if it doesn't exist) and give it a
particular group with setgid enabled."""
gid = None
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 groupname = ui.config(b"remotefilelog", b"cachegroup")
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 if groupname:
gid = getgid(groupname)
if gid is None:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.warn(_(b'unable to resolve group name: %s\n') % groupname)
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
# we use a single stat syscall to test the existence and mode / group bit
st = None
try:
st = os.stat(path)
except OSError:
pass
if st:
# exists
if (st.st_mode & 0o2775) != 0o2775 or st.st_gid != gid:
# permission needs to be fixed
setstickygroupdir(path, gid, ui.warn)
return
oldumask = os.umask(0o002)
try:
missingdirs = [path]
path = os.path.dirname(path)
while path and not os.path.exists(path):
missingdirs.append(path)
path = os.path.dirname(path)
for path in reversed(missingdirs):
try:
os.mkdir(path)
except OSError as ex:
if ex.errno != errno.EEXIST:
raise
for path in missingdirs:
setstickygroupdir(path, gid, ui.warn)
finally:
os.umask(oldumask)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def getusername(ui):
try:
return stringutil.shortuser(ui.username())
except Exception:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b'unknown'
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 def getreponame(ui):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 reponame = ui.config(b'paths', b'default')
Augie Fackler
remotefilelog: import pruned-down remotefilelog extension from hg-experimental...
r40530 if reponame:
return os.path.basename(reponame)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b"unknown"