##// END OF EJS Templates
merge with stable
merge with stable

File last commit:

r29417:526b027b default
r29459:fd93b15b merge default
Show More
scmutil.py
1423 lines | 47.4 KiB | text/x-python | PythonLexer
Adrian Buehlmann
add: introduce a warning message for non-portable filenames (issue2756) (BC)...
r13962 # scmutil.py - Mercurial core utility functions
#
# Copyright Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Gregory Szorc
scmutil: use absolute_import
r27482 from __future__ import absolute_import
Gregory Szorc
scmutil: support background file closing...
r27895 import contextlib
Gregory Szorc
scmutil: use absolute_import
r27482 import errno
import glob
Augie Fackler
cleanup: replace uses of util.(md5|sha1|sha256|sha512) with hashlib.\1...
r29341 import hashlib
Gregory Szorc
scmutil: use absolute_import
r27482 import os
import re
import shutil
import stat
import tempfile
Gregory Szorc
scmutil: support background file closing...
r27895 import threading
Gregory Szorc
scmutil: use absolute_import
r27482
from .i18n import _
from .node import wdirrev
from . import (
encoding,
error,
match as matchmod,
osutil,
pathutil,
phases,
revset,
similar,
util,
)
Kevin Bullock
scmutil: split platform-specific bits into their own modules...
r18690
if os.name == 'nt':
Gregory Szorc
scmutil: use absolute_import
r27482 from . import scmwindows as scmplatform
Kevin Bullock
scmutil: split platform-specific bits into their own modules...
r18690 else:
Gregory Szorc
scmutil: use absolute_import
r27482 from . import scmposix as scmplatform
Kevin Bullock
scmutil: split platform-specific bits into their own modules...
r18690
systemrcpath = scmplatform.systemrcpath
userrcpath = scmplatform.userrcpath
Adrian Buehlmann
add: introduce a warning message for non-portable filenames (issue2756) (BC)...
r13962
Martin von Zweigbergk
status: create class for status lists...
r22913 class status(tuple):
'''Named tuple with a list of files per status. The 'deleted', 'unknown'
and 'ignored' properties are only relevant to the working copy.
'''
__slots__ = ()
def __new__(cls, modified, added, removed, deleted, unknown, ignored,
clean):
return tuple.__new__(cls, (modified, added, removed, deleted, unknown,
ignored, clean))
@property
def modified(self):
Martin von Zweigbergk
status: update and move documentation of status types to status class...
r22915 '''files that have been modified'''
Martin von Zweigbergk
status: create class for status lists...
r22913 return self[0]
@property
def added(self):
Martin von Zweigbergk
status: update and move documentation of status types to status class...
r22915 '''files that have been added'''
Martin von Zweigbergk
status: create class for status lists...
r22913 return self[1]
@property
def removed(self):
Martin von Zweigbergk
status: update and move documentation of status types to status class...
r22915 '''files that have been removed'''
Martin von Zweigbergk
status: create class for status lists...
r22913 return self[2]
@property
def deleted(self):
Martin von Zweigbergk
status: update and move documentation of status types to status class...
r22915 '''files that are in the dirstate, but have been deleted from the
working copy (aka "missing")
'''
Martin von Zweigbergk
status: create class for status lists...
r22913 return self[3]
@property
def unknown(self):
Martin von Zweigbergk
status: update and move documentation of status types to status class...
r22915 '''files not in the dirstate that are not ignored'''
Martin von Zweigbergk
status: create class for status lists...
r22913 return self[4]
@property
def ignored(self):
Martin von Zweigbergk
status: update and move documentation of status types to status class...
r22915 '''files not in the dirstate that are ignored (by _dirignore())'''
Martin von Zweigbergk
status: create class for status lists...
r22913 return self[5]
@property
def clean(self):
Martin von Zweigbergk
status: update and move documentation of status types to status class...
r22915 '''files that have not been modified'''
Martin von Zweigbergk
status: create class for status lists...
r22913 return self[6]
def __repr__(self, *args, **kwargs):
return (('<status modified=%r, added=%r, removed=%r, deleted=%r, '
'unknown=%r, ignored=%r, clean=%r>') % self)
Augie Fackler
itersubrepos: move to scmutil to break a direct import cycle
r20392 def itersubrepos(ctx1, ctx2):
"""find subrepos in ctx1 or ctx2"""
# Create a (subpath, ctx) mapping where we prefer subpaths from
# ctx1. The subpaths from ctx2 are important when the .hgsub file
# has been modified (in ctx2) but not yet committed (in ctx1).
subpaths = dict.fromkeys(ctx2.substate, ctx2)
subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
Matt Harbison
scmutil: consistently return subrepos relative to ctx1 from itersubrepos()...
r25418
missing = set()
for subpath in ctx2.substate:
if subpath not in ctx1.substate:
del subpaths[subpath]
missing.add(subpath)
Augie Fackler
itersubrepos: move to scmutil to break a direct import cycle
r20392 for subpath, ctx in sorted(subpaths.iteritems()):
yield subpath, ctx.sub(subpath)
Matt Harbison
scmutil: consistently return subrepos relative to ctx1 from itersubrepos()...
r25418 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
# status and diff will have an accurate result when it does
# 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
# against itself.
for subpath in missing:
yield subpath, ctx2.nullsub(subpath, ctx1)
Patrick Mezard
discovery: add extinct changesets to outgoing.excluded...
r17248 def nochangesfound(ui, repo, excluded=None):
'''Report no changes for push/pull, excluded is None or a list of
nodes excluded from the push/pull.
'''
secretlist = []
if excluded:
for n in excluded:
Pierre-Yves David
outgoing: fix possible filtering crash in outgoing (issue3814)...
r18617 if n not in repo:
# discovery should not have included the filtered revision,
# we have to explicitly exclude it until discovery is cleanup.
continue
Patrick Mezard
discovery: add extinct changesets to outgoing.excluded...
r17248 ctx = repo[n]
if ctx.phase() >= phases.secret and not ctx.extinct():
secretlist.append(n)
Matt Mackall
scmutil: unify some 'no changes found' messages...
r15993 if secretlist:
ui.status(_("no changes found (ignored %d secret changesets)\n")
% len(secretlist))
else:
ui.status(_("no changes found\n"))
Kevin Bullock
scmutil: add bad character checking to checknewlabel...
r17821 def checknewlabel(repo, lbl, kind):
Durham Goode
translations: change label integer error to not specify the kind of label...
r19070 # Do not use the "kind" parameter in ui output.
# It makes strings difficult to translate.
Kevin Bullock
scmutil: add function to validate new branch, tag, and bookmark names...
r17817 if lbl in ['tip', '.', 'null']:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("the name '%s' is reserved") % lbl)
Kevin Bullock
scmutil: add bad character checking to checknewlabel...
r17821 for c in (':', '\0', '\n', '\r'):
if c in lbl:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("%r cannot be used in a name") % c)
Durham Goode
bookmark: don't allow integers as bookmark/branch/tag names...
r18566 try:
int(lbl)
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("cannot use an integer as a name"))
Durham Goode
bookmark: don't allow integers as bookmark/branch/tag names...
r18566 except ValueError:
pass
Kevin Bullock
scmutil: add function to validate new branch, tag, and bookmark names...
r17817
Adrian Buehlmann
move checkfilename from util to scmutil...
r13974 def checkfilename(f):
'''Check that the filename f is an acceptable filename for a tracked file'''
if '\r' in f or '\n' in f:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
Adrian Buehlmann
move checkfilename from util to scmutil...
r13974
Adrian Buehlmann
add: introduce a warning message for non-portable filenames (issue2756) (BC)...
r13962 def checkportable(ui, f):
'''Check if filename f is portable and warn or abort depending on config'''
Adrian Buehlmann
move checkfilename from util to scmutil...
r13974 checkfilename(f)
Adrian Buehlmann
scmutil: introduce casecollisionauditor...
r14138 abort, warn = checkportabilityalert(ui)
if abort or warn:
Adrian Buehlmann
add: introduce a warning message for non-portable filenames (issue2756) (BC)...
r13962 msg = util.checkwinfilename(f)
if msg:
Adrian Buehlmann
scmutil: introduce casecollisionauditor...
r14138 msg = "%s: %r" % (msg, f)
if abort:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(msg)
Adrian Buehlmann
scmutil: introduce casecollisionauditor...
r14138 ui.warn(_("warning: %s\n") % msg)
Kevin Gessner
add: notify when adding a file that would cause a case-folding collision...
r14068
Kevin Gessner
scmutil: refactor ui.portablefilenames processing...
r14067 def checkportabilityalert(ui):
'''check if the user's config requests nothing, a warning, or abort for
non-portable filenames'''
val = ui.config('ui', 'portablefilenames', 'warn')
lval = val.lower()
bval = util.parsebool(val)
abort = os.name == 'nt' or lval == 'abort'
warn = bval or lval == 'warn'
if bval is None and not (warn or abort or lval == 'ignore'):
Adrian Buehlmann
add: introduce a warning message for non-portable filenames (issue2756) (BC)...
r13962 raise error.ConfigError(
_("ui.portablefilenames value is invalid ('%s')") % val)
Kevin Gessner
scmutil: refactor ui.portablefilenames processing...
r14067 return abort, warn
Adrian Buehlmann
scmutil: introduce casecollisionauditor...
r14138 class casecollisionauditor(object):
Joshua Redstone
scmutil: 25% speedup in casecollisionauditor...
r17201 def __init__(self, ui, abort, dirstate):
Adrian Buehlmann
scmutil: introduce casecollisionauditor...
r14138 self._ui = ui
self._abort = abort
Joshua Redstone
scmutil: 25% speedup in casecollisionauditor...
r17201 allfiles = '\0'.join(dirstate._map)
self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
self._dirstate = dirstate
# The purpose of _newfiles is so that we don't complain about
# case collisions if someone were to call this object with the
# same filename twice.
self._newfiles = set()
Kevin Gessner
scmutil: refactor ui.portablefilenames processing...
r14067
Adrian Buehlmann
scmutil: introduce casecollisionauditor...
r14138 def __call__(self, f):
FUJIWARA Katsunori
scmutil: skip checks in "casecollisionauditor" if filename is already checked...
r20006 if f in self._newfiles:
return
FUJIWARA Katsunori
i18n: use UTF-8 string to lower filename for case collision check...
r14980 fl = encoding.lower(f)
FUJIWARA Katsunori
scmutil: skip checks in "casecollisionauditor" if filename is already checked...
r20006 if fl in self._loweredfiles and f not in self._dirstate:
Adrian Buehlmann
scmutil: introduce casecollisionauditor...
r14138 msg = _('possible case-folding collision for %s') % f
if self._abort:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(msg)
Adrian Buehlmann
scmutil: introduce casecollisionauditor...
r14138 self._ui.warn(_("warning: %s\n") % msg)
Joshua Redstone
scmutil: 25% speedup in casecollisionauditor...
r17201 self._loweredfiles.add(fl)
self._newfiles.add(f)
Adrian Buehlmann
move opener from util to scmutil
r13970
Gregory Szorc
repoview: move function for computing filtered hash...
r24723 def filteredhash(repo, maxrev):
"""build hash of filtered revisions in the current repoview.
Multiple caches perform up-to-date validation by checking that the
tiprev and tipnode stored in the cache file match the current repository.
However, this is not sufficient for validating repoviews because the set
of revisions in the view may change without the repository tiprev and
tipnode changing.
This function hashes all the revs filtered from the view and returns
that SHA-1 digest.
"""
cl = repo.changelog
if not cl.filteredrevs:
return None
key = None
revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
if revs:
Augie Fackler
cleanup: replace uses of util.(md5|sha1|sha256|sha512) with hashlib.\1...
r29341 s = hashlib.sha1()
Gregory Szorc
repoview: move function for computing filtered hash...
r24723 for rev in revs:
s.update('%s;' % rev)
key = s.digest()
return key
FUJIWARA Katsunori
scmutil: rename classes from "opener" to "vfs"...
r17649 class abstractvfs(object):
Dan Villiom Podlaski Christiansen
opener: introduce an abstact superclass of it...
r14089 """Abstract base class; cannot be instantiated"""
def __init__(self, *args, **kwargs):
'''Prevent instantiation; don't call this from subclasses.'''
raise NotImplementedError('attempted instantiating ' + str(type(self)))
Matt Mackall
opener: introduce tryread helper...
r16455 def tryread(self, path):
Thomas Arendsen Hein
opener: coding style, use triple quotes for doc string
r16479 '''gracefully return an empty string for missing files'''
Matt Mackall
opener: introduce tryread helper...
r16455 try:
return self.read(path)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except IOError as inst:
Matt Mackall
opener: introduce tryread helper...
r16455 if inst.errno != errno.ENOENT:
raise
return ""
FUJIWARA Katsunori
vfs: add "readlines" and "tryreadlines"...
r23368 def tryreadlines(self, path, mode='rb'):
'''gracefully return an empty array for missing files'''
try:
return self.readlines(path, mode=mode)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except IOError as inst:
FUJIWARA Katsunori
vfs: add "readlines" and "tryreadlines"...
r23368 if inst.errno != errno.ENOENT:
raise
return []
FUJIWARA Katsunori
vfs: add "notindexed" argument to invoke "ensuredir" with it in write mode...
r23370 def open(self, path, mode="r", text=False, atomictemp=False,
Gregory Szorc
scmutil: support background file closing...
r27895 notindexed=False, backgroundclose=False):
FUJIWARA Katsunori
vfs: add "notindexed" argument to invoke "ensuredir" with it in write mode...
r23370 '''Open ``path`` file, which is relative to vfs root.
Newly created directories are marked as "not to be indexed by
the content indexing service", if ``notindexed`` is specified
for "write" mode access.
'''
FUJIWARA Katsunori
vfs: add "open()" for newly added code paths which open files via vfs...
r19897 self.open = self.__call__
Gregory Szorc
scmutil: support background file closing...
r27895 return self.__call__(path, mode, text, atomictemp, notindexed,
backgroundclose=backgroundclose)
FUJIWARA Katsunori
vfs: add "open()" for newly added code paths which open files via vfs...
r19897
Dan Villiom Podlaski Christiansen
util & scmutil: adapt read/write helpers as request by mpm
r14167 def read(self, path):
Gregory Szorc
scmutil: use context managers for file handles...
r27706 with self(path, 'rb') as fp:
Dan Villiom Podlaski Christiansen
opener: add read & write utility methods...
r14097 return fp.read()
FUJIWARA Katsunori
vfs: add "readlines" and "tryreadlines"...
r23368 def readlines(self, path, mode='rb'):
Gregory Szorc
scmutil: use context managers for file handles...
r27706 with self(path, mode=mode) as fp:
FUJIWARA Katsunori
vfs: add "readlines" and "tryreadlines"...
r23368 return fp.readlines()
Gregory Szorc
scmutil: support background closing for write()...
r28197 def write(self, path, data, backgroundclose=False):
with self(path, 'wb', backgroundclose=backgroundclose) as fp:
Dan Villiom Podlaski Christiansen
util & scmutil: adapt read/write helpers as request by mpm
r14167 return fp.write(data)
FUJIWARA Katsunori
vfs: add "writelines"...
r23371 def writelines(self, path, data, mode='wb', notindexed=False):
Gregory Szorc
scmutil: use context managers for file handles...
r27706 with self(path, mode=mode, notindexed=notindexed) as fp:
FUJIWARA Katsunori
vfs: add "writelines"...
r23371 return fp.writelines(data)
Dan Villiom Podlaski Christiansen
util & scmutil: adapt read/write helpers as request by mpm
r14167 def append(self, path, data):
Gregory Szorc
scmutil: use context managers for file handles...
r27706 with self(path, 'ab') as fp:
Dan Villiom Podlaski Christiansen
opener: add read & write utility methods...
r14097 return fp.write(data)
FUJIWARA Katsunori
vfs: add basename
r25770 def basename(self, path):
"""return base element of a path (as os.path.basename would do)
This exists to allow handling of strange encoding if needed."""
return os.path.basename(path)
FUJIWARA Katsunori
vfs: add "chmod()"
r20086 def chmod(self, path, mode):
return os.chmod(self.join(path), mode)
FUJIWARA Katsunori
vfs: add dirname
r25772 def dirname(self, path):
"""return dirname element of a path (as os.path.dirname would do)
This exists to allow handling of strange encoding if needed."""
return os.path.dirname(path)
FUJIWARA Katsunori
localrepo: use file API via vfs while ensuring repository directory...
r17161 def exists(self, path=None):
return os.path.exists(self.join(path))
FUJIWARA Katsunori
changelog: use "vfs.fstat()" instead of "util.fstat()"...
r19899 def fstat(self, fp):
return util.fstat(fp)
FUJIWARA Katsunori
localrepo: use file API via vfs while ensuring repository directory...
r17161 def isdir(self, path=None):
return os.path.isdir(self.join(path))
FUJIWARA Katsunori
vfs: add "isfile()"
r20085 def isfile(self, path=None):
return os.path.isfile(self.join(path))
FUJIWARA Katsunori
localrepo: use "vfs.islink()" instead of "os.path.islink()"
r18949 def islink(self, path=None):
return os.path.islink(self.join(path))
Siddharth Agarwal
merge: while checking for unknown files don't follow symlinks (issue5027)...
r27571 def isfileorlink(self, path=None):
'''return whether path is a regular file or a symlink
Unlike isfile, this doesn't follow symlinks.'''
try:
st = self.lstat(path)
except OSError:
return False
mode = st.st_mode
return stat.S_ISREG(mode) or stat.S_ISLNK(mode)
Pierre-Yves David
vfs: add a 'reljoin' function for joining relative paths...
r23581 def reljoin(self, *paths):
"""join various elements of a path together (as os.path.join would do)
The vfs base is not injected so that path stay relative. This exists
to allow handling of strange encoding if needed."""
return os.path.join(*paths)
Pierre-Yves David
vfs: add a 'split' method...
r23582 def split(self, path):
"""split top-most element of a path (as os.path.split would do)
This exists to allow handling of strange encoding if needed."""
return os.path.split(path)
Chinmay Joshi
vfs: add lexists() in current api...
r21563 def lexists(self, path=None):
return os.path.lexists(self.join(path))
FUJIWARA Katsunori
context: use "vfs.lstat()" instead of "os.lstat()"...
r19900 def lstat(self, path=None):
return os.lstat(self.join(path))
Chinmay Joshi
vfs: add listdir for os.listdir in vfs...
r21799 def listdir(self, path=None):
return os.listdir(self.join(path))
FUJIWARA Katsunori
localrepo: use file API via vfs while ensuring repository directory...
r17161 def makedir(self, path=None, notindexed=True):
return util.makedir(self.join(path), notindexed)
def makedirs(self, path=None, mode=None):
return util.makedirs(self.join(path), mode)
FUJIWARA Katsunori
vfs: add "makelock()" and "readlock()"
r20090 def makelock(self, info, path):
return util.makelock(info, self.join(path))
FUJIWARA Katsunori
scmutil: reorder newly added functions for vfs support in dictionary order...
r17723 def mkdir(self, path=None):
return os.mkdir(self.join(path))
FUJIWARA Katsunori
vfs: add "mkstemp()"
r20980 def mkstemp(self, suffix='', prefix='tmp', dir=None, text=False):
fd, name = tempfile.mkstemp(suffix=suffix, prefix=prefix,
dir=self.join(dir), text=text)
dname, fname = util.split(name)
if dir:
return fd, os.path.join(dir, fname)
else:
return fd, fname
FUJIWARA Katsunori
store: invoke "osutil.listdir()" via vfs...
r17747 def readdir(self, path=None, stat=None, skip=None):
return osutil.listdir(self.join(path), stat, skip)
FUJIWARA Katsunori
vfs: add "makelock()" and "readlock()"
r20090 def readlock(self, path):
return util.readlock(self.join(path))
FUJIWARA Katsunori
vfs: make rename avoid ambiguity of file stat if needed...
r29203 def rename(self, src, dst, checkambig=False):
FUJIWARA Katsunori
doc: describe detail about checkambig optional argument...
r29367 """Rename from src to dst
checkambig argument is used with util.filestat, and is useful
only if destination file is guarded by any lock
(e.g. repo.lock or repo.wlock).
"""
FUJIWARA Katsunori
vfs: make rename avoid ambiguity of file stat if needed...
r29203 dstpath = self.join(dst)
oldstat = checkambig and util.filestat(dstpath)
if oldstat and oldstat.stat:
ret = util.rename(self.join(src), dstpath)
newstat = util.filestat(dstpath)
if newstat.isambig(oldstat):
# stat of renamed file is ambiguous to original one
advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
os.utime(dstpath, (advanced, advanced))
return ret
return util.rename(self.join(src), dstpath)
FUJIWARA Katsunori
localrepo: use "vfs.rename()" instead of "util.rename()"
r18948
FUJIWARA Katsunori
localrepo: use "vfs.readlink()" instead of "os.readlink()"
r18950 def readlink(self, path):
return os.readlink(self.join(path))
FUJIWARA Katsunori
vfs: add removedirs
r24693 def removedirs(self, path=None):
"""Remove a leaf directory and all empty intermediate ones
"""
return util.removedirs(self.join(path))
FUJIWARA Katsunori
vfs: add rmtree...
r24689 def rmtree(self, path=None, ignore_errors=False, forcibly=False):
"""Remove a directory tree recursively
If ``forcibly``, this tries to remove READ-ONLY files, too.
"""
if forcibly:
def onerror(function, path, excinfo):
if function is not os.remove:
raise
# read-only files cannot be unlinked under Windows
s = os.stat(path)
if (s.st_mode & stat.S_IWRITE) != 0:
raise
os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
os.remove(path)
else:
onerror = None
return shutil.rmtree(self.join(path),
ignore_errors=ignore_errors, onerror=onerror)
FUJIWARA Katsunori
localrepo: use "vfs.setflags()" instead of "util.setflags()"
r18951 def setflags(self, path, l, x):
return util.setflags(self.join(path), l, x)
FUJIWARA Katsunori
store: invoke "os.stat()" for "createmode" initialization via vfs...
r17726 def stat(self, path=None):
return os.stat(self.join(path))
FUJIWARA Katsunori
bookmarks: use "vfs.unlink()" instead of "util.unlink()"
r19895 def unlink(self, path=None):
return util.unlink(self.join(path))
Chinmay Joshi
vfs: add unlinkpath to vfs...
r21716 def unlinkpath(self, path=None, ignoremissing=False):
return util.unlinkpath(self.join(path), ignoremissing)
FUJIWARA Katsunori
bookmarks: use "vfs.utime()" instead of "os.utime()"
r19896 def utime(self, path=None, t=None):
return os.utime(self.join(path), t)
FUJIWARA Katsunori
vfs: add walk...
r24725 def walk(self, path=None, onerror=None):
"""Yield (dirpath, dirs, files) tuple for each directories under path
``dirpath`` is relative one from the root of this vfs. This
uses ``os.sep`` as path separator, even you specify POSIX
style ``path``.
"The root of this vfs" is represented as empty ``dirpath``.
"""
root = os.path.normpath(self.join(None))
# when dirpath == root, dirpath[prefixlen:] becomes empty
# because len(dirpath) < prefixlen.
prefixlen = len(pathutil.normasprefix(root))
for dirpath, dirs, files in os.walk(self.join(path), onerror=onerror):
yield (dirpath[prefixlen:], dirs, files)
Gregory Szorc
scmutil: support background file closing...
r27895 @contextlib.contextmanager
def backgroundclosing(self, ui, expectedcount=-1):
"""Allow files to be closed asynchronously.
When this context manager is active, ``backgroundclose`` can be passed
to ``__call__``/``open`` to result in the file possibly being closed
asynchronously, on a background thread.
"""
# This is an arbitrary restriction and could be changed if we ever
# have a use case.
vfs = getattr(self, 'vfs', self)
if getattr(vfs, '_backgroundfilecloser', None):
liscju
i18n: translate abort messages...
r29389 raise error.Abort(
_('can only have 1 active background file closer'))
Gregory Szorc
scmutil: support background file closing...
r27895
with backgroundfilecloser(ui, expectedcount=expectedcount) as bfc:
try:
vfs._backgroundfilecloser = bfc
yield bfc
finally:
vfs._backgroundfilecloser = None
FUJIWARA Katsunori
scmutil: rename classes from "opener" to "vfs"...
r17649 class vfs(abstractvfs):
'''Operate files relative to a base directory
Adrian Buehlmann
move opener from util to scmutil
r13970
This class is used to hide the details of COW semantics and
remote file access from higher level code.
'''
FUJIWARA Katsunori
vfs: split "expand" into "realpath"/"expandpath" to apply each separately...
r18945 def __init__(self, base, audit=True, expandpath=False, realpath=False):
if expandpath:
base = util.expandpath(base)
if realpath:
base = os.path.realpath(base)
Adrian Buehlmann
move opener from util to scmutil
r13970 self.base = base
Augie Fackler
cleanup: use modern @property/@foo.setter property specification...
r27879 self.mustaudit = audit
Bryan O'Sullivan
scmutil: turn opener._audit into a property, mustaudit...
r17554 self.createmode = None
self._trustnlink = None
Augie Fackler
cleanup: use modern @property/@foo.setter property specification...
r27879 @property
def mustaudit(self):
Bryan O'Sullivan
scmutil: turn opener._audit into a property, mustaudit...
r17554 return self._audit
Augie Fackler
cleanup: use modern @property/@foo.setter property specification...
r27879 @mustaudit.setter
def mustaudit(self, onoff):
Bryan O'Sullivan
scmutil: turn opener._audit into a property, mustaudit...
r17554 self._audit = onoff
if onoff:
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 self.audit = pathutil.pathauditor(self.base)
Adrian Buehlmann
move opener from util to scmutil
r13970 else:
Mads Kiilerich
scmutil: simplify vfs.audit - drop wrapped vfs.auditor
r18327 self.audit = util.always
Bryan O'Sullivan
scmutil: turn opener._audit into a property, mustaudit...
r17554
Adrian Buehlmann
move opener from util to scmutil
r13970 @util.propertycache
Adrian Buehlmann
opener: rename _can_symlink to _cansymlink
r14261 def _cansymlink(self):
Adrian Buehlmann
move opener from util to scmutil
r13970 return util.checklink(self.base)
Matt Mackall
scmutil: don't try to match modes on filesystems without modes (issue3740)
r18192 @util.propertycache
def _chmod(self):
return util.checkexec(self.base)
Matt Mackall
vfs: backout fchmod change from 76b73ce0ffac...
r17763 def _fixfilemode(self, name):
Matt Mackall
scmutil: don't try to match modes on filesystems without modes (issue3740)
r18192 if self.createmode is None or not self._chmod:
Adrian Buehlmann
move opener from util to scmutil
r13970 return
Gregory Szorc
global: mass rewrite to use modern octal syntax...
r25658 os.chmod(name, self.createmode & 0o666)
Adrian Buehlmann
move opener from util to scmutil
r13970
FUJIWARA Katsunori
vfs: add "notindexed" argument to invoke "ensuredir" with it in write mode...
r23370 def __call__(self, path, mode="r", text=False, atomictemp=False,
FUJIWARA Katsunori
vfs: make atomictempfile avoid ambiguity of file stat if needed...
r29202 notindexed=False, backgroundclose=False, checkambig=False):
FUJIWARA Katsunori
vfs: add "notindexed" argument to invoke "ensuredir" with it in write mode...
r23370 '''Open ``path`` file, which is relative to vfs root.
Newly created directories are marked as "not to be indexed by
the content indexing service", if ``notindexed`` is specified
for "write" mode access.
Gregory Szorc
scmutil: support background file closing...
r27895
If ``backgroundclose`` is passed, the file may be closed asynchronously.
It can only be used if the ``self.backgroundclosing()`` context manager
is active. This should only be specified if the following criteria hold:
1. There is a potential for writing thousands of files. Unless you
are writing thousands of files, the performance benefits of
asynchronously closing files is not realized.
2. Files are opened exactly once for the ``backgroundclosing``
active duration and are therefore free of race conditions between
closing a file on a background thread and reopening it. (If the
file were opened multiple times, there could be unflushed data
because the original file handle hasn't been flushed/closed yet.)
FUJIWARA Katsunori
vfs: make atomictempfile avoid ambiguity of file stat if needed...
r29202
FUJIWARA Katsunori
doc: describe detail about checkambig optional argument...
r29367 ``checkambig`` argument is passed to atomictemplfile (valid
only for writing), and is useful only if target file is
guarded by any lock (e.g. repo.lock or repo.wlock).
FUJIWARA Katsunori
vfs: add "notindexed" argument to invoke "ensuredir" with it in write mode...
r23370 '''
Adrian Buehlmann
opener: add self._audit (issue2862)
r14720 if self._audit:
r = util.checkosfilename(path)
if r:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort("%s: %r" % (r, path))
Mads Kiilerich
scmutil: simplify vfs.audit - drop wrapped vfs.auditor
r18327 self.audit(path)
Idan Kamara
scmutil: add join method to opener to construct path relative to base...
r16199 f = self.join(path)
Adrian Buehlmann
move opener from util to scmutil
r13970
if not text and "b" not in mode:
mode += "b" # for that other OS
nlink = -1
Adrian Buehlmann
vfs: optimize __call__ by not calling util.split for reads...
r17937 if mode not in ('r', 'rb'):
dirname, basename = util.split(f)
# If basename is empty, then the path is malformed because it points
# to a directory. Let the posixfile() call below raise IOError.
if basename:
if atomictemp:
Adam Simpkins
util: fix race in makedirs()...
r29017 util.makedirs(dirname, self.createmode, notindexed)
FUJIWARA Katsunori
vfs: make atomictempfile avoid ambiguity of file stat if needed...
r29202 return util.atomictempfile(f, mode, self.createmode,
checkambig=checkambig)
Adrian Buehlmann
vfs: optimize __call__ by not calling util.split for reads...
r17937 try:
if 'w' in mode:
util.unlink(f)
nlink = 0
else:
# nlinks() may behave differently for files on Windows
# shares if the file is open.
Gregory Szorc
scmutil: use context managers for file handles...
r27706 with util.posixfile(f):
nlink = util.nlinks(f)
if nlink < 1:
nlink = 2 # force mktempcopy (issue1922)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except (OSError, IOError) as e:
Adrian Buehlmann
vfs: optimize __call__ by not calling util.split for reads...
r17937 if e.errno != errno.ENOENT:
raise
Adrian Buehlmann
move opener from util to scmutil
r13970 nlink = 0
Adam Simpkins
util: fix race in makedirs()...
r29017 util.makedirs(dirname, self.createmode, notindexed)
Adrian Buehlmann
vfs: optimize __call__ by not calling util.split for reads...
r17937 if nlink > 0:
if self._trustnlink is None:
self._trustnlink = nlink > 1 or util.checknlink(f)
if nlink > 1 or not self._trustnlink:
util.rename(util.mktempcopy(f), f)
Adrian Buehlmann
move opener from util to scmutil
r13970 fp = util.posixfile(f, mode)
if nlink == 0:
Matt Mackall
vfs: backout fchmod change from 76b73ce0ffac...
r17763 self._fixfilemode(f)
Gregory Szorc
scmutil: support background file closing...
r27895
if backgroundclose:
if not self._backgroundfilecloser:
liscju
i18n: translate abort messages...
r29389 raise error.Abort(_('backgroundclose can only be used when a '
Gregory Szorc
scmutil: support background file closing...
r27895 'backgroundclosing context manager is active')
liscju
i18n: translate abort messages...
r29389 )
Gregory Szorc
scmutil: support background file closing...
r27895
fp = delayclosedfile(fp, self._backgroundfilecloser)
Adrian Buehlmann
move opener from util to scmutil
r13970 return fp
def symlink(self, src, dst):
Mads Kiilerich
scmutil: simplify vfs.audit - drop wrapped vfs.auditor
r18327 self.audit(dst)
Idan Kamara
scmutil: add join method to opener to construct path relative to base...
r16199 linkname = self.join(dst)
Adrian Buehlmann
move opener from util to scmutil
r13970 try:
os.unlink(linkname)
except OSError:
pass
Adam Simpkins
util: fix race in makedirs()...
r29017 util.makedirs(os.path.dirname(linkname), self.createmode)
Adrian Buehlmann
move opener from util to scmutil
r13970
Adrian Buehlmann
opener: rename _can_symlink to _cansymlink
r14261 if self._cansymlink:
Adrian Buehlmann
move opener from util to scmutil
r13970 try:
os.symlink(src, linkname)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except OSError as err:
Adrian Buehlmann
move opener from util to scmutil
r13970 raise OSError(err.errno, _('could not symlink to %r: %s') %
(src, err.strerror), linkname)
else:
Matt Mackall
vfs: use self.write to write symlink placeholders...
r17768 self.write(dst, src)
Adrian Buehlmann
move canonpath from util to scmutil
r13971
Matt Harbison
vfs: make it possible to pass multiple path elements to join...
r24628 def join(self, path, *insidef):
FUJIWARA Katsunori
localrepo: use file API via vfs while ensuring repository directory...
r17161 if path:
Matt Harbison
vfs: make it possible to pass multiple path elements to join...
r24628 return os.path.join(self.base, path, *insidef)
Matt Mackall
scmutil: backout 83785bb56062 (issue3643)
r17681 else:
return self.base
Idan Kamara
scmutil: add join method to opener to construct path relative to base...
r16199
FUJIWARA Katsunori
scmutil: rename classes from "opener" to "vfs"...
r17649 opener = vfs
Bryan O'Sullivan
scmutil: abstract out mustaudit delegation
r17845 class auditvfs(object):
def __init__(self, vfs):
self.vfs = vfs
Augie Fackler
cleanup: use modern @property/@foo.setter property specification...
r27879 @property
def mustaudit(self):
Bryan O'Sullivan
scmutil: abstract out mustaudit delegation
r17845 return self.vfs.mustaudit
Augie Fackler
cleanup: use modern @property/@foo.setter property specification...
r27879 @mustaudit.setter
def mustaudit(self, onoff):
Bryan O'Sullivan
scmutil: abstract out mustaudit delegation
r17845 self.vfs.mustaudit = onoff
Bryan O'Sullivan
scmutil: add mustaudit delegation to filtervfs (issue3673)
r17846 class filtervfs(abstractvfs, auditvfs):
FUJIWARA Katsunori
scmutil: rename classes from "opener" to "vfs"...
r17649 '''Wrapper vfs for filtering filenames with a function.'''
Dan Villiom Podlaski Christiansen
add filteropener abstraction for store openers
r14090
Bryan O'Sullivan
scmutil: add mustaudit delegation to filtervfs (issue3673)
r17846 def __init__(self, vfs, filter):
auditvfs.__init__(self, vfs)
Dan Villiom Podlaski Christiansen
add filteropener abstraction for store openers
r14090 self._filter = filter
def __call__(self, path, *args, **kwargs):
Bryan O'Sullivan
scmutil: add mustaudit delegation to filtervfs (issue3673)
r17846 return self.vfs(self._filter(path), *args, **kwargs)
Dan Villiom Podlaski Christiansen
add filteropener abstraction for store openers
r14090
Matt Harbison
vfs: make it possible to pass multiple path elements to join...
r24628 def join(self, path, *insidef):
FUJIWARA Katsunori
vfs: define "join()" in each classes derived from "abstractvfs"...
r17725 if path:
Matt Harbison
vfs: make it possible to pass multiple path elements to join...
r24628 return self.vfs.join(self._filter(self.vfs.reljoin(path, *insidef)))
FUJIWARA Katsunori
vfs: define "join()" in each classes derived from "abstractvfs"...
r17725 else:
Bryan O'Sullivan
scmutil: add mustaudit delegation to filtervfs (issue3673)
r17846 return self.vfs.join(path)
FUJIWARA Katsunori
vfs: define "join()" in each classes derived from "abstractvfs"...
r17725
FUJIWARA Katsunori
scmutil: rename classes from "opener" to "vfs"...
r17649 filteropener = filtervfs
Pierre-Yves David
vfs: add a read only vfs...
r18213 class readonlyvfs(abstractvfs, auditvfs):
'''Wrapper vfs preventing any writing.'''
def __init__(self, vfs):
auditvfs.__init__(self, vfs)
def __call__(self, path, mode='r', *args, **kw):
if mode not in ('r', 'rb'):
liscju
i18n: translate abort messages...
r29389 raise error.Abort(_('this vfs is read only'))
Pierre-Yves David
vfs: add a read only vfs...
r18213 return self.vfs(path, mode, *args, **kw)
Siddharth Agarwal
scmutil.readonlyvfs: implement join...
r26156 def join(self, path, *insidef):
return self.vfs.join(path, *insidef)
Pierre-Yves David
vfs: add a read only vfs...
r18213
Adrian Buehlmann
move walkrepos from util to scmutil
r13975 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
Mads Kiilerich
help: improve hgweb help...
r17104 '''yield every hg repository under path, always recursively.
The recurse flag will only control recursion into repo working dirs'''
Adrian Buehlmann
move walkrepos from util to scmutil
r13975 def errhandler(err):
if err.filename == path:
raise err
Augie Fackler
walkrepos: use getattr instead of hasattr for samestat
r14961 samestat = getattr(os.path, 'samestat', None)
if followsym and samestat is not None:
Adrian Buehlmann
scmutil: rename local function _add_dir_if_not_there
r14227 def adddir(dirlst, dirname):
Adrian Buehlmann
move walkrepos from util to scmutil
r13975 match = False
dirstat = os.stat(dirname)
for lstdirstat in dirlst:
if samestat(dirstat, lstdirstat):
match = True
break
if not match:
dirlst.append(dirstat)
return not match
else:
followsym = False
if (seen_dirs is None) and followsym:
seen_dirs = []
Adrian Buehlmann
scmutil: rename local function _add_dir_if_not_there
r14227 adddir(seen_dirs, path)
Adrian Buehlmann
move walkrepos from util to scmutil
r13975 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
dirs.sort()
if '.hg' in dirs:
yield root # found a repository
qroot = os.path.join(root, '.hg', 'patches')
if os.path.isdir(os.path.join(qroot, '.hg')):
yield qroot # we have a patch queue repo here
if recurse:
# avoid recursing inside the .hg directory
dirs.remove('.hg')
else:
dirs[:] = [] # don't descend further
elif followsym:
newdirs = []
for d in dirs:
fname = os.path.join(root, d)
Adrian Buehlmann
scmutil: rename local function _add_dir_if_not_there
r14227 if adddir(seen_dirs, fname):
Adrian Buehlmann
move walkrepos from util to scmutil
r13975 if os.path.islink(fname):
for hgname in walkrepos(fname, True, seen_dirs):
yield hgname
else:
newdirs.append(d)
dirs[:] = newdirs
Adrian Buehlmann
move rcpath from util to scmutil
r13984
Adrian Buehlmann
rename scmutil.os_rcpath to osrcpath
r14224 def osrcpath():
Adrian Buehlmann
move os_rcpath from util to scmutil
r13985 '''return default os-specific hgrc search path'''
Mads Kiilerich
config: introduce "built-in" default configuration settings in default.d...
r23142 path = []
defaultpath = os.path.join(util.datapath, 'default.d')
if os.path.isdir(defaultpath):
for f, kind in osutil.listdir(defaultpath):
if f.endswith('.rc'):
path.append(os.path.join(defaultpath, f))
path.extend(systemrcpath())
Adrian Buehlmann
rename scmutil.user_rcpath to userrcpath
r14226 path.extend(userrcpath())
Adrian Buehlmann
move os_rcpath from util to scmutil
r13985 path = [os.path.normpath(f) for f in path]
return path
Adrian Buehlmann
move rcpath from util to scmutil
r13984 _rcpath = None
def rcpath():
'''return hgrc search path. if env var HGRCPATH is set, use it.
for each item in path, if directory, use files ending in .rc,
else use item.
make HGRCPATH empty to only look in .hg/hgrc of current repo.
if no HGRCPATH, use default os-specific path.'''
global _rcpath
if _rcpath is None:
if 'HGRCPATH' in os.environ:
_rcpath = []
for p in os.environ['HGRCPATH'].split(os.pathsep):
if not p:
continue
p = util.expandpath(p)
if os.path.isdir(p):
for f, kind in osutil.listdir(p):
if f.endswith('.rc'):
_rcpath.append(os.path.join(p, f))
else:
_rcpath.append(p)
else:
Adrian Buehlmann
rename scmutil.os_rcpath to osrcpath
r14224 _rcpath = osrcpath()
Adrian Buehlmann
move rcpath from util to scmutil
r13984 return _rcpath
Adrian Buehlmann
move system_rcpath and user_rcpath to scmutil
r13986
Yuya Nishihara
changeset_printer: use node.wdirrev to calculate meaningful parentrevs...
r25739 def intrev(rev):
Yuya Nishihara
scmutil: add function to help handling workingctx in arithmetic operation...
r24582 """Return integer for a given revision that can be used in comparison or
arithmetic operation"""
if rev is None:
Yuya Nishihara
changeset_printer: use node.wdirrev to calculate meaningful parentrevs...
r25739 return wdirrev
Yuya Nishihara
scmutil: add function to help handling workingctx in arithmetic operation...
r24582 return rev
Matt Mackall
scmutil: move revsingle/pair/range from cmdutil...
r14319 def revsingle(repo, revspec, default='.'):
Matt Mackall
revsingle: fix silly API issue (issue2992)
r19509 if not revspec and revspec != 0:
Matt Mackall
scmutil: move revsingle/pair/range from cmdutil...
r14319 return repo[default]
l = revrange(repo, [revspec])
Pierre-Yves David
revset-limit: use boolean testing instead of `len(revs) < 1`...
r22814 if not l:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('empty revision set'))
Pierre-Yves David
revsingle: use `last` instead of direct indexing...
r22815 return repo[l.last()]
Matt Mackall
scmutil: move revsingle/pair/range from cmdutil...
r14319
Yuya Nishihara
revpair: restrict odd-range handling to top-level x:y expression (issue4774)...
r26020 def _pairspec(revspec):
tree = revset.parse(revspec)
Yuya Nishihara
revset: factor out public optimize() function from recursion...
r29119 tree = revset.optimize(tree) # fix up "x^:y" -> "(x^):y"
Yuya Nishihara
revpair: restrict odd-range handling to top-level x:y expression (issue4774)...
r26020 return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall')
Matt Mackall
scmutil: move revsingle/pair/range from cmdutil...
r14319 def revpair(repo, revs):
if not revs:
return repo.dirstate.p1(), None
l = revrange(repo, revs)
Pierre-Yves David
revpair: smartset compatibility...
r20862 if not l:
first = second = None
elif l.isascending():
first = l.min()
second = l.max()
elif l.isdescending():
first = l.max()
second = l.min()
else:
Pierre-Yves David
revpair: use `first` and `last` instead of direct indexing...
r22816 first = l.first()
second = l.last()
Pierre-Yves David
revpair: smartset compatibility...
r20862
if first is None:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('empty revision range'))
Matt Harbison
scmutil: abort if an empty revision is given to revpair()...
r26836 if (first == second and len(revs) >= 2
and not all(revrange(repo, [r]) for r in revs)):
raise error.Abort(_('empty revision on one side of range'))
Matt Mackall
scmutil: move revsingle/pair/range from cmdutil...
r14319
Yuya Nishihara
revpair: restrict odd-range handling to top-level x:y expression (issue4774)...
r26020 # if top-level is range expression, the result must always be a pair
if first == second and len(revs) == 1 and not _pairspec(revs[0]):
Pierre-Yves David
revpair: smartset compatibility...
r20862 return repo.lookup(first), None
Matt Mackall
scmutil: move revsingle/pair/range from cmdutil...
r14319
Pierre-Yves David
revpair: smartset compatibility...
r20862 return repo.lookup(first), repo.lookup(second)
Matt Mackall
scmutil: move revsingle/pair/range from cmdutil...
r14319
Gregory Szorc
scmutil: improve documentation of revset APIs...
r29417 def revrange(repo, specs):
"""Execute 1 to many revsets and return the union.
This is the preferred mechanism for executing revsets using user-specified
config options, such as revset aliases.
The revsets specified by ``specs`` will be executed via a chained ``OR``
expression. If ``specs`` is empty, an empty result is returned.
``specs`` can contain integers, in which case they are assumed to be
revision numbers.
It is assumed the revsets are already formatted. If you have arguments
that need to be expanded in the revset, call ``revset.formatspec()``
and pass the result as an element of ``specs``.
Specifying a single revset is allowed.
Returns a ``revset.abstractsmartset`` which is a list-like interface over
integer revisions.
"""
Yuya Nishihara
revrange: evaluate all revset specs at once...
r25928 allspecs = []
Gregory Szorc
scmutil: improve documentation of revset APIs...
r29417 for spec in specs:
Yuya Nishihara
revrange: drop old-style parser in favor of revset (API)...
r25904 if isinstance(spec, int):
spec = revset.formatspec('rev(%d)', spec)
Yuya Nishihara
revrange: evaluate all revset specs at once...
r25928 allspecs.append(spec)
m = revset.matchany(repo.ui, allspecs, repo)
return m(repo)
Matt Mackall
scmutil: fold in wdutil
r14320
Yuya Nishihara
changeset_printer: move _meaningful_parentrevs() to scmutil...
r26433 def meaningfulparents(repo, ctx):
"""Return list of meaningful (or all if debug) parentrevs for rev.
For merges (two non-nullrev revisions) both parents are meaningful.
Otherwise the first parent revision is considered meaningful if it
is not the preceding revision.
"""
parents = ctx.parents()
if len(parents) > 1:
return parents
if repo.ui.debugflag:
return [parents[0], repo['null']]
if parents[0].rev() >= intrev(ctx.rev()) - 1:
return []
return parents
Matt Mackall
scmutil: fold in wdutil
r14320 def expandpats(pats):
Mads Kiilerich
match: improve documentation - docstrings and more descriptive variable naming...
r21111 '''Expand bare globs when running on windows.
On posix we assume it already has already been done by sh.'''
Matt Mackall
scmutil: fold in wdutil
r14320 if not util.expandglobs:
return list(pats)
ret = []
Mads Kiilerich
match: improve documentation - docstrings and more descriptive variable naming...
r21111 for kindpat in pats:
kind, pat = matchmod._patsplit(kindpat, None)
Matt Mackall
scmutil: fold in wdutil
r14320 if kind is None:
try:
Mads Kiilerich
match: improve documentation - docstrings and more descriptive variable naming...
r21111 globbed = glob.glob(pat)
Matt Mackall
scmutil: fold in wdutil
r14320 except re.error:
Mads Kiilerich
match: improve documentation - docstrings and more descriptive variable naming...
r21111 globbed = [pat]
Matt Mackall
scmutil: fold in wdutil
r14320 if globbed:
ret.extend(globbed)
continue
Mads Kiilerich
match: improve documentation - docstrings and more descriptive variable naming...
r21111 ret.append(kindpat)
Matt Mackall
scmutil: fold in wdutil
r14320 return ret
Pierre-Yves David
match: remove a mutable default argument...
r26326 def matchandpats(ctx, pats=(), opts=None, globbed=False, default='relpath',
Matt Harbison
scmutil: add an optional parameter to matcher factories for a bad() override...
r25467 badfn=None):
Mads Kiilerich
match: improve documentation - docstrings and more descriptive variable naming...
r21111 '''Return a matcher and the patterns that were used.
Matt Harbison
scmutil: add an optional parameter to matcher factories for a bad() override...
r25467 The matcher will warn about bad matches, unless an alternate badfn callback
is provided.'''
Matt Mackall
scmutil: fold in wdutil
r14320 if pats == ("",):
pats = []
Pierre-Yves David
match: remove a mutable default argument...
r26326 if opts is None:
opts = {}
Matt Mackall
scmutil: fold in wdutil
r14320 if not globbed and default == 'relpath':
pats = expandpats(pats or [])
Matt Mackall
scmutil: match now accepts a context or a repo
r14670
Matt Harbison
scmutil: add an optional parameter to matcher factories for a bad() override...
r25467 def bad(f, msg):
Matt Harbison
scmutil: replace 'ctx._repo' with 'ctx.repo()'
r24338 ctx.repo().ui.warn("%s: %s\n" % (m.rel(f), msg))
Matt Harbison
scmutil: use the optional badfn argument when building a matcher
r25466
Matt Harbison
scmutil: add an optional parameter to matcher factories for a bad() override...
r25467 if badfn is None:
badfn = bad
Matt Harbison
scmutil: use the optional badfn argument when building a matcher
r25466 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
default, listsubrepos=opts.get('subrepos'), badfn=badfn)
Martin von Zweigbergk
matcher: make e.g. 'relpath:.' lead to fast paths...
r24447 if m.always():
pats = []
Patrick Mezard
graphlog: restore FILE glob expansion on Windows...
r16171 return m, pats
Pierre-Yves David
match: remove a mutable default argument...
r26328 def match(ctx, pats=(), opts=None, globbed=False, default='relpath',
badfn=None):
Mads Kiilerich
match: improve documentation - docstrings and more descriptive variable naming...
r21111 '''Return a matcher that will warn about bad matches.'''
Matt Harbison
scmutil: add an optional parameter to matcher factories for a bad() override...
r25467 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
Matt Mackall
scmutil: fold in wdutil
r14320
def matchall(repo):
Mads Kiilerich
match: improve documentation - docstrings and more descriptive variable naming...
r21111 '''Return a matcher that will efficiently match everything.'''
Matt Mackall
scmutil: fold in wdutil
r14320 return matchmod.always(repo.root, repo.getcwd())
Matt Harbison
scmutil: add an optional parameter to matcher factories for a bad() override...
r25467 def matchfiles(repo, files, badfn=None):
Mads Kiilerich
match: improve documentation - docstrings and more descriptive variable naming...
r21111 '''Return a matcher that will efficiently match exactly these files.'''
Matt Harbison
scmutil: add an optional parameter to matcher factories for a bad() override...
r25467 return matchmod.exact(repo.root, repo.getcwd(), files, badfn=badfn)
Matt Mackall
scmutil: fold in wdutil
r14320
Siddharth Agarwal
origpath: move from cmdutil to scmutil...
r27651 def origpath(ui, repo, filepath):
'''customize where .orig files are created
Fetch user defined path from config file: [ui] origbackuppath = <path>
Fall back to default (filepath) if not specified
'''
origbackuppath = ui.config('ui', 'origbackuppath', None)
if origbackuppath is None:
return filepath + ".orig"
filepathfromroot = os.path.relpath(filepath, start=repo.root)
fullorigpath = repo.wjoin(origbackuppath, filepathfromroot)
origbackupdir = repo.vfs.dirname(fullorigpath)
if not repo.vfs.exists(origbackupdir):
ui.note(_('creating directory: %s\n') % origbackupdir)
util.makedirs(origbackupdir)
return fullorigpath + ".orig"
Pierre-Yves David
addremove: remove a mutable default argument...
r26329 def addremove(repo, matcher, prefix, opts=None, dry_run=None, similarity=None):
if opts is None:
opts = {}
Matt Harbison
scmutil: pass a matcher to scmutil.addremove() instead of a list of patterns...
r23533 m = matcher
Matt Mackall
scmutil: fold in wdutil
r14320 if dry_run is None:
dry_run = opts.get('dry_run')
if similarity is None:
similarity = float(opts.get('similarity') or 0)
Matt Harbison
scmutil: pass a matcher to scmutil.addremove() instead of a list of patterns...
r23533
Matt Harbison
commit: propagate --addremove to subrepos if -S is specified (issue3759)...
r23537 ret = 0
join = lambda f: os.path.join(prefix, f)
Matt Harbison
addremove: support addremove with explicit paths in subrepos...
r23539 def matchessubrepo(matcher, subpath):
if matcher.exact(subpath):
return True
for f in matcher.files():
if f.startswith(subpath):
return True
return False
Matt Harbison
commit: propagate --addremove to subrepos if -S is specified (issue3759)...
r23537 wctx = repo[None]
for subpath in sorted(wctx.substate):
Matt Harbison
addremove: support addremove with explicit paths in subrepos...
r23539 if opts.get('subrepos') or matchessubrepo(m, subpath):
Matt Harbison
commit: propagate --addremove to subrepos if -S is specified (issue3759)...
r23537 sub = wctx.sub(subpath)
try:
Martin von Zweigbergk
match: rename "narrowmatcher" to "subdirmatcher" (API)...
r28017 submatch = matchmod.subdirmatcher(subpath, m)
Matt Harbison
commit: propagate --addremove to subrepos if -S is specified (issue3759)...
r23537 if sub.addremove(submatch, prefix, opts, dry_run, similarity):
ret = 1
except error.LookupError:
repo.ui.status(_("skipping missing subrepository: %s\n")
% join(subpath))
Matt Mackall
addremove: return 1 if we failed to handle any explicit files
r16167 rejected = []
Matt Harbison
addremove: warn when addremove fails to operate on a named path...
r23534 def badfn(f, msg):
if f in m.files():
Matt Harbison
addremove: replace match.bad() monkey patching with match.badmatch()...
r25434 m.bad(f, msg)
Matt Harbison
addremove: warn when addremove fails to operate on a named path...
r23534 rejected.append(f)
Matt Mackall
addremove: return 1 if we failed to handle any explicit files
r16167
Matt Harbison
addremove: replace match.bad() monkey patching with match.badmatch()...
r25434 badmatch = matchmod.badmatch(m, badfn)
added, unknown, deleted, removed, forgotten = _interestingfiles(repo,
badmatch)
Siddharth Agarwal
scmutil.addremove: pull ui.status printing out of the loop...
r18863
Martin von Zweigbergk
addremove: add back forgotten files (BC)...
r23259 unknownset = set(unknown + forgotten)
Siddharth Agarwal
scmutil.addremove: pull ui.status printing out of the loop...
r18863 toprint = unknownset.copy()
toprint.update(deleted)
for abs in sorted(toprint):
if repo.ui.verbose or not m.exact(abs):
if abs in unknownset:
Matt Harbison
narrowmatcher: propagate the rel() method...
r23686 status = _('adding %s\n') % m.uipath(abs)
Siddharth Agarwal
scmutil.addremove: pull ui.status printing out of the loop...
r18863 else:
Matt Harbison
narrowmatcher: propagate the rel() method...
r23686 status = _('removing %s\n') % m.uipath(abs)
Siddharth Agarwal
scmutil.addremove: pull ui.status printing out of the loop...
r18863 repo.ui.status(status)
Siddharth Agarwal
scmutil.addremove: factor out code to find renames...
r19152 renames = _findrenames(repo, m, added + unknown, removed + deleted,
similarity)
Matt Mackall
scmutil: fold in wdutil
r14320
if not dry_run:
Martin von Zweigbergk
addremove: add back forgotten files (BC)...
r23259 _markchanges(repo, unknown + forgotten, deleted, renames)
Matt Mackall
scmutil: fold in wdutil
r14320
Matt Mackall
addremove: return 1 if we failed to handle any explicit files
r16167 for f in rejected:
if f in m.files():
return 1
Matt Harbison
commit: propagate --addremove to subrepos if -S is specified (issue3759)...
r23537 return ret
Matt Mackall
addremove: return 1 if we failed to handle any explicit files
r16167
Siddharth Agarwal
scmutil: add a function to mark that files have been operated on...
r19154 def marktouched(repo, files, similarity=0.0):
'''Assert that files have somehow been operated upon. files are relative to
the repo root.'''
Matt Harbison
scmutil: add an optional parameter to matcher factories for a bad() override...
r25467 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
Siddharth Agarwal
scmutil: add a function to mark that files have been operated on...
r19154 rejected = []
Martin von Zweigbergk
addremove: add back forgotten files (BC)...
r23259 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
Siddharth Agarwal
scmutil: add a function to mark that files have been operated on...
r19154
if repo.ui.verbose:
Martin von Zweigbergk
addremove: add back forgotten files (BC)...
r23259 unknownset = set(unknown + forgotten)
Siddharth Agarwal
scmutil: add a function to mark that files have been operated on...
r19154 toprint = unknownset.copy()
toprint.update(deleted)
for abs in sorted(toprint):
if abs in unknownset:
status = _('adding %s\n') % abs
else:
status = _('removing %s\n') % abs
repo.ui.status(status)
renames = _findrenames(repo, m, added + unknown, removed + deleted,
similarity)
Martin von Zweigbergk
addremove: add back forgotten files (BC)...
r23259 _markchanges(repo, unknown + forgotten, deleted, renames)
Siddharth Agarwal
scmutil: add a function to mark that files have been operated on...
r19154
for f in rejected:
if f in m.files():
return 1
return 0
Siddharth Agarwal
scmutil.addremove: factor out dirstate walk into another function...
r19150 def _interestingfiles(repo, matcher):
'''Walk dirstate with matcher, looking for files that addremove would care
about.
This is different from dirstate.status because it doesn't care about
whether files are modified or clean.'''
Martin von Zweigbergk
addremove: add back forgotten files (BC)...
r23259 added, unknown, deleted, removed, forgotten = [], [], [], [], []
Augie Fackler
pathutil: tease out a new library to break an import cycle from canonpath use
r20033 audit_path = pathutil.pathauditor(repo.root)
Siddharth Agarwal
scmutil.addremove: factor out dirstate walk into another function...
r19150
ctx = repo[None]
dirstate = repo.dirstate
Siddharth Agarwal
addremove: don't do full walks...
r19655 walkresults = dirstate.walk(matcher, sorted(ctx.substate), True, False,
full=False)
Siddharth Agarwal
scmutil.addremove: factor out dirstate walk into another function...
r19150 for abs, st in walkresults.iteritems():
dstate = dirstate[abs]
if dstate == '?' and audit_path.check(abs):
unknown.append(abs)
elif dstate != 'r' and not st:
deleted.append(abs)
Martin von Zweigbergk
addremove: add back forgotten files (BC)...
r23259 elif dstate == 'r' and st:
forgotten.append(abs)
Siddharth Agarwal
scmutil.addremove: factor out dirstate walk into another function...
r19150 # for finding renames
Martin von Zweigbergk
addremove: add back forgotten files (BC)...
r23259 elif dstate == 'r' and not st:
Siddharth Agarwal
scmutil.addremove: factor out dirstate walk into another function...
r19150 removed.append(abs)
elif dstate == 'a':
added.append(abs)
Martin von Zweigbergk
addremove: add back forgotten files (BC)...
r23259 return added, unknown, deleted, removed, forgotten
Siddharth Agarwal
scmutil.addremove: factor out dirstate walk into another function...
r19150
Siddharth Agarwal
scmutil.addremove: factor out code to find renames...
r19152 def _findrenames(repo, matcher, added, removed, similarity):
'''Find renames from removed files to added ones.'''
renames = {}
if similarity > 0:
for old, new, score in similar.findrenames(repo, added, removed,
similarity):
if (repo.ui.verbose or not matcher.exact(old)
or not matcher.exact(new)):
repo.ui.status(_('recording removal of %s as rename to %s '
'(%d%% similar)\n') %
(matcher.rel(old), matcher.rel(new),
score * 100))
renames[new] = old
return renames
Siddharth Agarwal
scmutil.addremove: factor out code to mark added/removed/renames...
r19153 def _markchanges(repo, unknown, deleted, renames):
'''Marks the files in unknown as added, the files in deleted as removed,
and the files in renames as copied.'''
wctx = repo[None]
Bryan O'Sullivan
with: use context manager in _markchanges
r27851 with repo.wlock():
Siddharth Agarwal
scmutil.addremove: factor out code to mark added/removed/renames...
r19153 wctx.forget(deleted)
wctx.add(unknown)
for new, old in renames.iteritems():
wctx.copy(old, new)
Matt Mackall
scmutil: fold in wdutil
r14320 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
"""Update the dirstate to reflect the intent of copying src to dst. For
different reasons it might not end with dst being marked as copied from src.
"""
origsrc = repo.dirstate.copied(src) or src
if dst == origsrc: # copying back a copy?
if repo.dirstate[dst] not in 'mn' and not dryrun:
repo.dirstate.normallookup(dst)
else:
if repo.dirstate[origsrc] == 'a' and origsrc == src:
if not ui.quiet:
ui.warn(_("%s has not been committed yet, so no copy "
"data will be stored for %s.\n")
% (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
if repo.dirstate[dst] in '?r' and not dryrun:
wctx.add([dst])
elif not dryrun:
wctx.copy(origsrc, dst)
Adrian Buehlmann
introduce new function scmutil.readrequires...
r14482
def readrequires(opener, supported):
'''Reads and parses .hg/requires and checks if all entries found
are in the list of supported features.'''
requirements = set(opener.read("requires").splitlines())
Pierre-Yves David
requirements: show all missing features in the error message....
r14746 missings = []
Adrian Buehlmann
introduce new function scmutil.readrequires...
r14482 for r in requirements:
if r not in supported:
Matt Mackall
requires: note apparent corruption
r14484 if not r or not r[0].isalnum():
raise error.RequirementError(_(".hg/requires file is corrupt"))
Pierre-Yves David
requirements: show all missing features in the error message....
r14746 missings.append(r)
missings.sort()
if missings:
Brodie Rao
cleanup: eradicate long lines
r16683 raise error.RequirementError(
Mads Kiilerich
repo: rephrase the "missing requirement" error message...
r20820 _("repository requires features unknown to this Mercurial: %s")
% " ".join(missings),
Matt Mackall
urls: bulk-change primary website URLs
r26421 hint=_("see https://mercurial-scm.org/wiki/MissingRequirement"
Mads Kiilerich
repo: rephrase the "missing requirement" error message...
r20820 " for more information"))
Adrian Buehlmann
introduce new function scmutil.readrequires...
r14482 return requirements
Idan Kamara
scmutil: introduce filecache...
r14928
Drew Gottlieb
requires: move requires file writing func from localrepo to scmutil...
r24934 def writerequires(opener, requirements):
Gregory Szorc
scmutil: use context managers for file handles...
r27706 with opener('requires', 'w') as fp:
for r in sorted(requirements):
fp.write("%s\n" % r)
Drew Gottlieb
requires: move requires file writing func from localrepo to scmutil...
r24934
Siddharth Agarwal
scmutil: rename filecacheentry to filecachesubentry...
r20043 class filecachesubentry(object):
Siddharth Agarwal
scmutil.filecacheentry: make stat argument to constructor mandatory...
r20042 def __init__(self, path, stat):
Idan Kamara
scmutil: introduce filecache...
r14928 self.path = path
Idan Kamara
filecache: allow filecacheentry to be created without stating in __init__...
r18315 self.cachestat = None
self._cacheable = None
Idan Kamara
scmutil: introduce filecache...
r14928
Idan Kamara
filecache: allow filecacheentry to be created without stating in __init__...
r18315 if stat:
Siddharth Agarwal
scmutil: rename filecacheentry to filecachesubentry...
r20043 self.cachestat = filecachesubentry.stat(self.path)
Idan Kamara
filecache: allow filecacheentry to be created without stating in __init__...
r18315
if self.cachestat:
self._cacheable = self.cachestat.cacheable()
else:
# None means we don't know yet
self._cacheable = None
Idan Kamara
scmutil: introduce filecache...
r14928
def refresh(self):
if self.cacheable():
Siddharth Agarwal
scmutil: rename filecacheentry to filecachesubentry...
r20043 self.cachestat = filecachesubentry.stat(self.path)
Idan Kamara
scmutil: introduce filecache...
r14928
def cacheable(self):
if self._cacheable is not None:
return self._cacheable
# we don't know yet, assume it is for now
return True
def changed(self):
# no point in going further if we can't cache it
if not self.cacheable():
return True
Siddharth Agarwal
scmutil: rename filecacheentry to filecachesubentry...
r20043 newstat = filecachesubentry.stat(self.path)
Idan Kamara
scmutil: introduce filecache...
r14928
# we may not know if it's cacheable yet, check again now
if newstat and self._cacheable is None:
self._cacheable = newstat.cacheable()
# check again
if not self._cacheable:
return True
if self.cachestat != newstat:
self.cachestat = newstat
return True
else:
return False
@staticmethod
def stat(path):
try:
return util.cachestat(path)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except OSError as e:
Idan Kamara
scmutil: introduce filecache...
r14928 if e.errno != errno.ENOENT:
raise
Siddharth Agarwal
scmutil: introduce a filecacheentry that can watch multiple paths
r20044 class filecacheentry(object):
def __init__(self, paths, stat=True):
self._entries = []
for path in paths:
self._entries.append(filecachesubentry(path, stat))
def changed(self):
'''true if any entry has changed'''
for entry in self._entries:
if entry.changed():
return True
return False
def refresh(self):
for entry in self._entries:
entry.refresh()
Idan Kamara
scmutil: introduce filecache...
r14928 class filecache(object):
Siddharth Agarwal
scmutil.filecache: support watching over multiple files
r20045 '''A property like decorator that tracks files under .hg/ for updates.
Idan Kamara
scmutil: introduce filecache...
r14928
Records stat info when called in _filecache.
Siddharth Agarwal
scmutil.filecache: support watching over multiple files
r20045 On subsequent calls, compares old stat info with new info, and recreates the
object when any of the files changes, updating the new stat info in
_filecache.
Idan Kamara
scmutil: introduce filecache...
r14928
Mercurial either atomic renames or appends for files under .hg,
so to ensure the cache is reliable we need the filesystem to be able
to tell us if a file has been replaced. If it can't, we fallback to
timeless@mozdev.org
spelling: behaviour -> behavior
r26098 recreating the object on every call (essentially the same behavior as
Siddharth Agarwal
scmutil.filecache: support watching over multiple files
r20045 propertycache).
'''
def __init__(self, *paths):
self.paths = paths
Idan Kamara
filecache: refactor path join logic to a function...
r16198
def join(self, obj, fname):
Siddharth Agarwal
scmutil.filecache: support watching over multiple files
r20045 """Used to compute the runtime path of a cached file.
Idan Kamara
filecache: refactor path join logic to a function...
r16198
Users should subclass filecache and provide their own version of this
function to call the appropriate join function on 'obj' (an instance
of the class that its member function was decorated).
"""
return obj.join(fname)
Idan Kamara
scmutil: introduce filecache...
r14928
def __call__(self, func):
self.func = func
self.name = func.__name__
return self
def __get__(self, obj, type=None):
Martijn Pieters
scmutil: allow access to filecache descriptor on class...
r29373 # if accessed on the class, return the descriptor itself.
if obj is None:
return self
Idan Kamara
scmutil: update cached copy when filecached attribute is assigned (issue3263)...
r16115 # do we need to check if the file changed?
if self.name in obj.__dict__:
Idan Kamara
filecache: create an entry in _filecache when __set__ is called for a missing one...
r18316 assert self.name in obj._filecache, self.name
Idan Kamara
scmutil: update cached copy when filecached attribute is assigned (issue3263)...
r16115 return obj.__dict__[self.name]
Idan Kamara
scmutil: introduce filecache...
r14928 entry = obj._filecache.get(self.name)
if entry:
if entry.changed():
entry.obj = self.func(obj)
else:
Siddharth Agarwal
scmutil.filecache: support watching over multiple files
r20045 paths = [self.join(obj, path) for path in self.paths]
Idan Kamara
scmutil: introduce filecache...
r14928
# We stat -before- creating the object so our cache doesn't lie if
# a writer modified between the time we read and stat
Siddharth Agarwal
scmutil.filecache: support watching over multiple files
r20045 entry = filecacheentry(paths, True)
Idan Kamara
scmutil: introduce filecache...
r14928 entry.obj = self.func(obj)
obj._filecache[self.name] = entry
Idan Kamara
scmutil: update cached copy when filecached attribute is assigned (issue3263)...
r16115 obj.__dict__[self.name] = entry.obj
Idan Kamara
scmutil: introduce filecache...
r14928 return entry.obj
Idan Kamara
scmutil: update cached copy when filecached attribute is assigned (issue3263)...
r16115
def __set__(self, obj, value):
Idan Kamara
filecache: create an entry in _filecache when __set__ is called for a missing one...
r18316 if self.name not in obj._filecache:
# we add an entry for the missing value because X in __dict__
# implies X in _filecache
Siddharth Agarwal
scmutil.filecache: support watching over multiple files
r20045 paths = [self.join(obj, path) for path in self.paths]
ce = filecacheentry(paths, False)
Idan Kamara
filecache: create an entry in _filecache when __set__ is called for a missing one...
r18316 obj._filecache[self.name] = ce
else:
ce = obj._filecache[self.name]
ce.obj = value # update cached copy
Idan Kamara
scmutil: update cached copy when filecached attribute is assigned (issue3263)...
r16115 obj.__dict__[self.name] = value # update copy returned by obj.x
def __delete__(self, obj):
try:
del obj.__dict__[self.name]
except KeyError:
Augie Fackler
scmutil: clean up use of two-argument raise...
r18177 raise AttributeError(self.name)
Siddharth Agarwal
scmutil: add a way for a subprocess to be run with an inheritable lock...
r26490
def _locksub(repo, lock, envvar, cmd, environ=None, *args, **kwargs):
if lock is None:
raise error.LockInheritanceContractViolation(
'lock can only be inherited while held')
if environ is None:
environ = {}
with lock.inherit() as locker:
environ[envvar] = locker
return repo.ui.system(cmd, environ=environ, *args, **kwargs)
Siddharth Agarwal
scmutil: add a way for a repo's wlock to be inherited by a subprocess...
r26491
def wlocksub(repo, cmd, *args, **kwargs):
"""run cmd as a subprocess that allows inheriting repo's wlock
This can only be called while the wlock is held. This takes all the
arguments that ui.system does, and returns the exit code of the
subprocess."""
return _locksub(repo, repo.currentwlock(), 'HG_WLOCK_LOCKER', cmd, *args,
**kwargs)
Pierre-Yves David
scmutil: extract general delta config handling in a function...
r26906
def gdinitconfig(ui):
"""helper function to know if a repo should be created as general delta
Pierre-Yves David
format: introduce 'format.usegeneraldelta`...
r26907 """
# experimental config: format.generaldelta
return (ui.configbool('format', 'generaldelta', False)
Pierre-Yves David
format: create new repository as 'generaldelta' by default...
r27093 or ui.configbool('format', 'usegeneraldelta', True))
Pierre-Yves David
scmutil: extract general delta config handling in a function...
r26906
Pierre-Yves David
format: introduce 'format.usegeneraldelta`...
r26907 def gddeltaconfig(ui):
"""helper function to know if incoming delta should be optimised
"""
Pierre-Yves David
scmutil: extract general delta config handling in a function...
r26906 # experimental config: format.generaldelta
return ui.configbool('format', 'generaldelta', False)
Gregory Szorc
scmutil: support background file closing...
r27895
class delayclosedfile(object):
"""Proxy for a file object whose close is delayed.
Do not instantiate outside of the vfs layer.
"""
def __init__(self, fh, closer):
object.__setattr__(self, '_origfh', fh)
object.__setattr__(self, '_closer', closer)
def __getattr__(self, attr):
return getattr(self._origfh, attr)
def __setattr__(self, attr, value):
return setattr(self._origfh, attr, value)
def __delattr__(self, attr):
return delattr(self._origfh, attr)
def __enter__(self):
return self._origfh.__enter__()
def __exit__(self, exc_type, exc_value, exc_tb):
self._closer.close(self._origfh)
def close(self):
self._closer.close(self._origfh)
class backgroundfilecloser(object):
"""Coordinates background closing of file handles on multiple threads."""
def __init__(self, ui, expectedcount=-1):
self._running = False
self._entered = False
self._threads = []
self._threadexception = None
# Only Windows/NTFS has slow file closing. So only enable by default
# on that platform. But allow to be enabled elsewhere for testing.
defaultenabled = os.name == 'nt'
enabled = ui.configbool('worker', 'backgroundclose', defaultenabled)
if not enabled:
return
# There is overhead to starting and stopping the background threads.
# Don't do background processing unless the file count is large enough
# to justify it.
minfilecount = ui.configint('worker', 'backgroundcloseminfilecount',
2048)
# FUTURE dynamically start background threads after minfilecount closes.
# (We don't currently have any callers that don't know their file count)
if expectedcount > 0 and expectedcount < minfilecount:
return
# Windows defaults to a limit of 512 open files. A buffer of 128
# should give us enough headway.
maxqueue = ui.configint('worker', 'backgroundclosemaxqueue', 384)
threadcount = ui.configint('worker', 'backgroundclosethreadcount', 4)
ui.debug('starting %d threads for background file closing\n' %
threadcount)
timeless
scmutil: use util.queue/util.empty for py3 compat
r28819 self._queue = util.queue(maxsize=maxqueue)
Gregory Szorc
scmutil: support background file closing...
r27895 self._running = True
for i in range(threadcount):
t = threading.Thread(target=self._worker, name='backgroundcloser')
self._threads.append(t)
t.start()
def __enter__(self):
self._entered = True
return self
def __exit__(self, exc_type, exc_value, exc_tb):
self._running = False
# Wait for threads to finish closing so open files don't linger for
# longer than lifetime of context manager.
for t in self._threads:
t.join()
def _worker(self):
"""Main routine for worker thread."""
while True:
try:
fh = self._queue.get(block=True, timeout=0.100)
# Need to catch or the thread will terminate and
# we could orphan file descriptors.
try:
fh.close()
except Exception as e:
# Stash so can re-raise from main thread later.
self._threadexception = e
timeless
scmutil: use util.queue/util.empty for py3 compat
r28819 except util.empty:
Gregory Szorc
scmutil: support background file closing...
r27895 if not self._running:
break
def close(self, fh):
"""Schedule a file for closing."""
if not self._entered:
liscju
i18n: translate abort messages...
r29389 raise error.Abort(_('can only call close() when context manager '
'active'))
Gregory Szorc
scmutil: support background file closing...
r27895
# If a background thread encountered an exception, raise now so we fail
# fast. Otherwise we may potentially go on for minutes until the error
# is acted on.
if self._threadexception:
e = self._threadexception
self._threadexception = None
raise e
# If we're not actively running, close synchronously.
if not self._running:
fh.close()
return
self._queue.put(fh, block=True, timeout=None)