# HG changeset patch # User Jun Wu # Date 2016-02-26 14:59:39 # Node ID b4ceadb2c439e0b04271962bf57b2429ced00755 # Parent d4cced5885f836b91e616c61480f22c6c9829386 chgserver: add utilities to calculate mtimehash mtimehash is designed to detect file changes. These files include: - single file extensions (__init__.py for complex extensions) - mercurial/__version__.py - python (sys.executable) mtimehash only uses stat to check files so it's fast but not 100% accurate. However it should be good enough for our use case. For chgserver, once mtimehash changes, the server is considered outdated immediately and should no longer provide service. diff --git a/hgext/chgserver.py b/hgext/chgserver.py --- a/hgext/chgserver.py +++ b/hgext/chgserver.py @@ -30,10 +30,12 @@ from __future__ import absolute_import import SocketServer import errno +import inspect import os import re import signal import struct +import sys import threading import time import traceback @@ -46,6 +48,7 @@ from mercurial import ( commandserver, dispatch, error, + extensions, osutil, util, ) @@ -97,6 +100,50 @@ def _confighash(ui): envhash = _hashlist(sorted(envitems)) return sectionhash[:6] + envhash[:6] +def _getmtimepaths(ui): + """get a list of paths that should be checked to detect change + + The list will include: + - extensions (will not cover all files for complex extensions) + - mercurial/__version__.py + - python binary + """ + modules = [m for n, m in extensions.extensions(ui)] + try: + from mercurial import __version__ + modules.append(__version__) + except ImportError: + pass + files = [sys.executable] + for m in modules: + try: + files.append(inspect.getabsfile(m)) + except TypeError: + pass + return sorted(set(files)) + +def _mtimehash(paths): + """return a quick hash for detecting file changes + + mtimehash calls stat on given paths and calculate a hash based on size and + mtime of each file. mtimehash does not read file content because reading is + expensive. therefore it's not 100% reliable for detecting content changes. + it's possible to return different hashes for same file contents. + it's also possible to return a same hash for different file contents for + some carefully crafted situation. + + for chgserver, it is designed that once mtimehash changes, the server is + considered outdated immediately and should no longer provide service. + """ + def trystat(path): + try: + st = os.stat(path) + return (st.st_mtime, st.st_size) + except OSError: + # could be ENOENT, EPERM etc. not fatal in any case + pass + return _hashlist(map(trystat, paths))[:12] + # copied from hgext/pager.py:uisetup() def _setuppagercmd(ui, options, cmd): if not ui.formatted():