Mercurial > hg > mercurial-source
changeset 32920:4fadea09feca
merge with stable
author | Augie Fackler <augie@google.com> |
---|---|
date | Thu, 04 May 2017 00:26:55 -0400 |
parents | f238a483a1fd (diff) 40785ccab410 (current diff) |
children | d90ffee93df6 |
files | |
diffstat | 16 files changed, 123 insertions(+), 112 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -452,7 +452,7 @@ whitespace=True) for abs in ctx.walk(m): fctx = ctx[abs] - if not opts.get('text') and util.binary(fctx.data()): + if not opts.get('text') and fctx.isbinary(): fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs)) continue @@ -837,8 +837,6 @@ elif extra or good + bad + skip + reset + extend + bool(command) > 1: raise error.Abort(_('incompatible arguments')) - cmdutil.checkunfinished(repo) - if reset: hbisect.resetstate(repo) return @@ -865,6 +863,7 @@ """common used update sequence""" if noupdate: return + cmdutil.checkunfinished(repo) cmdutil.bailifchanged(repo) return hg.clean(repo, node, show_stats=show_stats)
--- a/mercurial/dispatch.py +++ b/mercurial/dispatch.py @@ -162,9 +162,13 @@ ret = None try: ret = _runcatch(req) - except KeyboardInterrupt: + except KeyboardInterrupt as inst: try: - req.ui.warn(_("interrupted!\n")) + if isinstance(inst, error.SignalInterrupt): + msg = _("killed!\n") + else: + msg = _("interrupted!\n") + req.ui.warn(msg) except error.SignalInterrupt: # maybe pager would quit without consuming all the output, and # SIGPIPE was raised. we cannot print anything in this case. @@ -179,7 +183,7 @@ if req.ui.logblockedtimes: req.ui._blockedtimes['command_duration'] = duration * 1000 req.ui.log('uiblocked', 'ui blocked ms', **req.ui._blockedtimes) - req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n", + req.ui.log("commandfinish", "%s exited %d after %0.2f seconds\n", msg, ret or 0, duration) try: req._runexithandlers()
--- a/mercurial/filelog.py +++ b/mercurial/filelog.py @@ -18,7 +18,7 @@ _mdre = re.compile('\1\n') def parsemeta(text): - """return (metadatadict, keylist, metadatasize)""" + """return (metadatadict, metadatasize)""" # text can be buffer, so we can't use .startswith or .index if text[:2] != '\1\n': return None, None
--- a/mercurial/fileset.py +++ b/mercurial/fileset.py @@ -256,7 +256,7 @@ """ # i18n: "binary" is a keyword getargs(x, 0, 0, _("binary takes no arguments")) - return [f for f in mctx.existing() if util.binary(mctx.ctx[f].data())] + return [f for f in mctx.existing() if mctx.ctx[f].isbinary()] @predicate('exec()', callexisting=True) def exec_(mctx, x):
--- a/mercurial/help/internals/wireprotocol.txt +++ b/mercurial/help/internals/wireprotocol.txt @@ -632,6 +632,9 @@ branches -------- +(Legacy command used for discovery in old clients. Clients with ``getbundle`` +use the ``known`` and ``heads`` commands instead.) + Obtain ancestor changesets of specific nodes back to a branch point. Despite the name, this command has nothing to do with Mercurial named branches.
--- a/mercurial/hgweb/webcommands.py +++ b/mercurial/hgweb/webcommands.py @@ -808,7 +808,7 @@ context = parsecontext(web.config('web', 'comparisoncontext', '5')) def filelines(f): - if util.binary(f.data()): + if f.isbinary(): mt = mimetypes.guess_type(f.path())[0] if not mt: mt = 'application/octet-stream' @@ -886,7 +886,7 @@ yield p def annotate(**map): - if util.binary(fctx.data()): + if fctx.isbinary(): mt = (mimetypes.guess_type(fctx.path())[0] or 'application/octet-stream') lines = [((fctx.filectx(fctx.filerev()), 1), '(binary:%s)' % mt)]
--- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -385,16 +385,6 @@ # generic mapping between names and nodes self.names = namespaces.namespaces() - @property - def wopener(self): - self.ui.deprecwarn("use 'repo.wvfs' instead of 'repo.wopener'", '4.2') - return self.wvfs - - @property - def opener(self): - self.ui.deprecwarn("use 'repo.vfs' instead of 'repo.opener'", '4.2') - return self.vfs - def close(self): self._writecaches() @@ -649,11 +639,6 @@ """ return hook.hook(self.ui, self, name, throw, **args) - def tag(self, names, node, message, local, user, date, editor=False): - self.ui.deprecwarn("use 'tagsmod.tag' instead of 'repo.tag'", '4.2') - tagsmod.tag(self, names, node, message, local, user, date, - editor=editor) - @filteredpropertycache def _tagscache(self): '''Returns a tagscache object that contains various tags related @@ -841,10 +826,6 @@ return 'store' return None - def join(self, f, *insidef): - self.ui.deprecwarn("use 'repo.vfs.join' instead of 'repo.join'", '4.2') - return self.vfs.join(os.path.join(f, *insidef)) - def wjoin(self, f, *insidef): return self.vfs.reljoin(self.root, f, *insidef) @@ -884,15 +865,6 @@ def pathto(self, f, cwd=None): return self.dirstate.pathto(f, cwd) - def wfile(self, f, mode='r'): - self.ui.deprecwarn("use 'repo.wvfs' instead of 'repo.wfile'", '4.2') - return self.wvfs(f, mode) - - def _link(self, f): - self.ui.deprecwarn("use 'repo.wvfs.islink' instead of 'repo._link'", - '4.2') - return self.wvfs.islink(f) - def _loadfilter(self, filter): if filter not in self.filterpats: l = []
--- a/mercurial/match.py +++ b/mercurial/match.py @@ -52,7 +52,7 @@ return fset, other def _expandsubinclude(kindpats, root): - '''Returns the list of subinclude matchers and the kindpats without the + '''Returns the list of subinclude matcher args and the kindpats without the subincludes in it.''' relmatchers = [] other = [] @@ -64,12 +64,12 @@ path = pathutil.join(sourceroot, pat) newroot = pathutil.dirname(path) - relmatcher = match(newroot, '', [], ['include:%s' % path]) + matcherargs = (newroot, '', [], ['include:%s' % path]) prefix = pathutil.canonpath(root, root, newroot) if prefix: prefix += '/' - relmatchers.append((prefix, relmatcher)) + relmatchers.append((prefix, matcherargs)) else: other.append((kind, pat, source)) @@ -584,10 +584,17 @@ subincludes, kindpats = _expandsubinclude(kindpats, root) if subincludes: + submatchers = {} def matchsubinclude(f): - for prefix, mf in subincludes: - if f.startswith(prefix) and mf(f[len(prefix):]): - return True + for prefix, matcherargs in subincludes: + if f.startswith(prefix): + mf = submatchers.get(prefix) + if mf is None: + mf = match(*matcherargs) + submatchers[prefix] = mf + + if mf(f[len(prefix):]): + return True return False matchfuncs.append(matchsubinclude)
--- a/mercurial/posix.py +++ b/mercurial/posix.py @@ -494,7 +494,7 @@ def getuser(): '''return name of current user''' - return getpass.getuser() + return pycompat.fsencode(getpass.getuser()) def username(uid=None): """Return the name of the user with the given uid.
--- a/mercurial/scmutil.py +++ b/mercurial/scmutil.py @@ -26,7 +26,6 @@ revsetlang, similar, util, - vfs as vfsmod, ) if pycompat.osname == 'nt': @@ -186,8 +185,6 @@ ui.warn(_("abort: file censored %s!\n") % inst) except error.RevlogError as inst: ui.warn(_("abort: %s!\n") % inst) - except error.SignalInterrupt: - ui.warn(_("killed!\n")) except error.InterventionRequired as inst: ui.warn("%s\n" % inst) if inst.hint: @@ -335,27 +332,6 @@ key = s.digest() return key -def _deprecated(old, new, func): - msg = ('class at mercurial.scmutil.%s moved to mercurial.vfs.%s' - % (old, new)) - def wrapper(*args, **kwargs): - util.nouideprecwarn(msg, '4.2') - return func(*args, **kwargs) - return wrapper - -# compatibility layer since all 'vfs' code moved to 'mercurial.vfs' -# -# This is hard to instal deprecation warning to this since we do not have -# access to a 'ui' object. -opener = _deprecated('opener', 'vfs', vfsmod.vfs) -vfs = _deprecated('vfs', 'vfs', vfsmod.vfs) -filteropener = _deprecated('filteropener', 'filtervfs', vfsmod.filtervfs) -filtervfs = _deprecated('filtervfs', 'filtervfs', vfsmod.filtervfs) -abstractvfs = _deprecated('abstractvfs', 'abstractvfs', vfsmod.abstractvfs) -readonlyvfs = _deprecated('readonlyvfs', 'readonlyvfs', vfsmod.readonlyvfs) -auditvfs = _deprecated('auditvfs', 'auditvfs', vfsmod.auditvfs) -checkambigatclosing = vfsmod.checkambigatclosing - def walkrepos(path, followsym=False, seen_dirs=None, recurse=False): '''yield every hg repository under path, always recursively. The recurse flag will only control recursion into repo working dirs'''
--- a/mercurial/templatefilters.py +++ b/mercurial/templatefilters.py @@ -16,6 +16,7 @@ encoding, hbisect, node, + pycompat, registrar, templatekw, util, @@ -24,6 +25,9 @@ urlerr = util.urlerr urlreq = util.urlreq +if pycompat.ispy3: + long = int + # filters are callables like: # fn(obj) # with: @@ -226,8 +230,8 @@ elif obj is True: return 'true' elif isinstance(obj, (int, long, float)): - return str(obj) - elif isinstance(obj, str): + return pycompat.bytestr(obj) + elif isinstance(obj, bytes): return '"%s"' % encoding.jsonescape(obj, paranoid=paranoid) elif util.safehasattr(obj, 'keys'): out = ['%s: %s' % (json(k), json(v)) @@ -351,11 +355,11 @@ text and concatenating them. """ thing = templatekw.unwraphybrid(thing) - if util.safehasattr(thing, '__iter__') and not isinstance(thing, str): + if util.safehasattr(thing, '__iter__') and not isinstance(thing, bytes): return "".join([stringify(t) for t in thing if t is not None]) if thing is None: return "" - return str(thing) + return pycompat.bytestr(thing) @templatefilter('stripdir') def stripdir(text):
--- a/mercurial/util.py +++ b/mercurial/util.py @@ -1724,8 +1724,7 @@ iterator over chunks of arbitrary size.""" def __init__(self, in_iter): - """in_iter is the iterator that's iterating over the input chunks. - targetsize is how big a buffer to try to maintain.""" + """in_iter is the iterator that's iterating over the input chunks.""" def splitbig(chunks): for chunk in chunks: if len(chunk) > 2**20:
--- a/mercurial/worker.py +++ b/mercurial/worker.py @@ -134,37 +134,43 @@ killworkers() oldchldhandler = signal.signal(signal.SIGCHLD, sigchldhandler) ui.flush() + parentpid = os.getpid() for pargs in partition(args, workers): - pid = os.fork() - if pid == 0: - signal.signal(signal.SIGINT, oldhandler) - signal.signal(signal.SIGCHLD, oldchldhandler) - - def workerfunc(): - os.close(rfd) - for i, item in func(*(staticargs + (pargs,))): - os.write(wfd, '%d %s\n' % (i, item)) - return 0 + # make sure we use os._exit in all worker code paths. otherwise the + # worker may do some clean-ups which could cause surprises like + # deadlock. see sshpeer.cleanup for example. + # override error handling *before* fork. this is necessary because + # exception (signal) may arrive after fork, before "pid =" assignment + # completes, and other exception handler (dispatch.py) can lead to + # unexpected code path without os._exit. + ret = -1 + try: + pid = os.fork() + if pid == 0: + signal.signal(signal.SIGINT, oldhandler) + signal.signal(signal.SIGCHLD, oldchldhandler) - # make sure we use os._exit in all code paths. otherwise the worker - # may do some clean-ups which could cause surprises like deadlock. - # see sshpeer.cleanup for example. - ret = 0 - try: + def workerfunc(): + os.close(rfd) + for i, item in func(*(staticargs + (pargs,))): + os.write(wfd, '%d %s\n' % (i, item)) + return 0 + + ret = scmutil.callcatch(ui, workerfunc) + except: # parent re-raises, child never returns + if os.getpid() == parentpid: + raise + exctype = sys.exc_info()[0] + force = not issubclass(exctype, KeyboardInterrupt) + ui.traceback(force=force) + finally: + if os.getpid() != parentpid: try: - ret = scmutil.callcatch(ui, workerfunc) - finally: ui.flush() - except KeyboardInterrupt: - os._exit(255) - except: # never return, therefore no re-raises - try: - ui.traceback(force=True) - ui.flush() + except: # never returns, no re-raises + pass finally: - os._exit(255) - else: - os._exit(ret & 255) + os._exit(ret & 255) pids.add(pid) os.close(wfd) fp = os.fdopen(rfd, pycompat.sysstr('rb'), 0)
--- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ # 'python setup.py --help' for more options import sys, platform -if getattr(sys, 'version_info', (0, 0, 0)) < (2, 6, 0, 'final'): +if sys.version_info < (2, 6, 0, 'final'): raise SystemExit("Mercurial requires Python 2.6 or later.") if sys.version_info[0] >= 3:
--- a/tests/test-bisect.t +++ b/tests/test-bisect.t @@ -551,7 +551,14 @@ date: Thu Jan 01 00:00:06 1970 +0000 summary: msg 6 - + $ hg graft -q 15 + warning: conflicts while merging a! (edit, then use 'hg resolve --mark') + abort: unresolved conflicts, can't continue + (use 'hg resolve' and 'hg graft --continue') + [255] + $ hg bisect --reset + $ hg up -C . + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved Check that bisect does not break on obsolete changesets =========================================================
--- a/tests/test-worker.t +++ b/tests/test-worker.t @@ -2,6 +2,7 @@ $ cat > t.py <<EOF > from __future__ import absolute_import, print_function + > import time > from mercurial import ( > cmdutil, > error, @@ -22,6 +23,7 @@ > for arg in args: > ui.status('run\n') > yield 1, arg + > time.sleep(0.1) # easier to trigger killworkers code path > functable = { > 'abort': abort, > 'exc': exc, @@ -74,21 +76,53 @@ Known exception should be caught, but printed if --traceback is enabled - $ hg --config "extensions.t=$abspath" --config 'worker.numcpus=2' \ - > test 100000.0 abort + $ hg --config "extensions.t=$abspath" --config 'worker.numcpus=8' \ + > test 100000.0 abort 2>&1 start abort: known exception [255] - $ hg --config "extensions.t=$abspath" --config 'worker.numcpus=2' \ - > test 100000.0 abort --traceback 2>&1 | grep '^Traceback' - Traceback (most recent call last): - Traceback (most recent call last): + $ hg --config "extensions.t=$abspath" --config 'worker.numcpus=8' \ + > test 100000.0 abort --traceback 2>&1 | egrep '^(SystemExit|Abort)' + Abort: known exception + SystemExit: 255 Traceback must be printed for unknown exceptions - $ hg --config "extensions.t=$abspath" --config 'worker.numcpus=2' \ - > test 100000.0 exc 2>&1 | grep '^Traceback' - Traceback (most recent call last): + $ hg --config "extensions.t=$abspath" --config 'worker.numcpus=8' \ + > test 100000.0 exc 2>&1 | grep '^Exception' + Exception: unknown exception + +Workers should not do cleanups in all cases + + $ cat > $TESTTMP/detectcleanup.py <<EOF + > from __future__ import absolute_import + > import atexit + > import os + > import time + > oldfork = os.fork + > count = 0 + > parentpid = os.getpid() + > def delayedfork(): + > global count + > count += 1 + > pid = oldfork() + > # make it easier to test SIGTERM hitting other workers when they have + > # not set up error handling yet. + > if count > 1 and pid == 0: + > time.sleep(0.1) + > return pid + > os.fork = delayedfork + > def cleanup(): + > if os.getpid() != parentpid: + > os.write(1, 'should never happen\n') + > atexit.register(cleanup) + > EOF + + $ hg --config "extensions.t=$abspath" --config worker.numcpus=8 --config \ + > "extensions.d=$TESTTMP/detectcleanup.py" test 100000 abort + start + abort: known exception + [255] #endif