Mercurial > public > mercurial-scm > hg
diff hgext/fsmonitor/state.py @ 28433:3b67f27bb908
fsmonitor: new experimental extension
Extension to plug into a Watchman daemon, speeding up hg status calls by
relying on OS events to tell us what files have changed.
Originally developed at https://bitbucket.org/facebook/hgwatchman
author | Martijn Pieters <mjpieters@fb.com> |
---|---|
date | Thu, 03 Mar 2016 14:29:19 +0000 |
parents | |
children | a0939666b836 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext/fsmonitor/state.py Thu Mar 03 14:29:19 2016 +0000 @@ -0,0 +1,115 @@ +# state.py - fsmonitor persistent state +# +# Copyright 2013-2016 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 errno +import os +import socket +import struct + +from mercurial import pathutil +from mercurial.i18n import _ + +_version = 4 +_versionformat = ">I" + +class state(object): + def __init__(self, repo): + self._opener = repo.opener + self._ui = repo.ui + self._rootdir = pathutil.normasprefix(repo.root) + self._lastclock = None + + self.mode = self._ui.config('fsmonitor', 'mode', default='on') + self.walk_on_invalidate = self._ui.configbool( + 'fsmonitor', 'walk_on_invalidate', False) + self.timeout = float(self._ui.config( + 'fsmonitor', 'timeout', default='2')) + + def get(self): + try: + file = self._opener('fsmonitor.state', 'rb') + except IOError as inst: + if inst.errno != errno.ENOENT: + raise + return None, None, None + + versionbytes = file.read(4) + if len(versionbytes) < 4: + self._ui.log( + 'fsmonitor', 'fsmonitor: state file only has %d bytes, ' + 'nuking state\n' % len(versionbytes)) + self.invalidate() + return None, None, None + try: + diskversion = struct.unpack(_versionformat, versionbytes)[0] + if diskversion != _version: + # different version, nuke state and start over + self._ui.log( + 'fsmonitor', 'fsmonitor: version switch from %d to ' + '%d, nuking state\n' % (diskversion, _version)) + self.invalidate() + return None, None, None + + state = file.read().split('\0') + # state = hostname\0clock\0ignorehash\0 + list of files, each + # followed by a \0 + diskhostname = state[0] + hostname = socket.gethostname() + if diskhostname != hostname: + # file got moved to a different host + self._ui.log('fsmonitor', 'fsmonitor: stored hostname "%s" ' + 'different from current "%s", nuking state\n' % + (diskhostname, hostname)) + self.invalidate() + return None, None, None + + clock = state[1] + ignorehash = state[2] + # discard the value after the last \0 + notefiles = state[3:-1] + + finally: + file.close() + + return clock, ignorehash, notefiles + + def set(self, clock, ignorehash, notefiles): + if clock is None: + self.invalidate() + return + + try: + file = self._opener('fsmonitor.state', 'wb') + except (IOError, OSError): + self._ui.warn(_("warning: unable to write out fsmonitor state\n")) + return + + try: + file.write(struct.pack(_versionformat, _version)) + file.write(socket.gethostname() + '\0') + file.write(clock + '\0') + file.write(ignorehash + '\0') + if notefiles: + file.write('\0'.join(notefiles)) + file.write('\0') + finally: + file.close() + + def invalidate(self): + try: + os.unlink(os.path.join(self._rootdir, '.hg', 'fsmonitor.state')) + except OSError as inst: + if inst.errno != errno.ENOENT: + raise + + def setlastclock(self, clock): + self._lastclock = clock + + def getlastclock(self): + return self._lastclock