state.py
147 lines
| 4.6 KiB
| text/x-python
|
PythonLexer
|
r28433 | # 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 | ||||
|
r29205 | from mercurial.i18n import _ | ||
|
r32816 | from mercurial import ( | ||
pathutil, | ||||
util, | ||||
) | ||||
|
r28433 | |||
_version = 4 | ||||
_versionformat = ">I" | ||||
|
r43346 | |||
|
r28433 | class state(object): | ||
def __init__(self, repo): | ||||
|
r31215 | self._vfs = repo.vfs | ||
|
r28433 | self._ui = repo.ui | ||
self._rootdir = pathutil.normasprefix(repo.root) | ||||
self._lastclock = None | ||||
|
r32816 | self._identity = util.filestat(None) | ||
|
r28433 | |||
|
r34464 | self.mode = self._ui.config('fsmonitor', 'mode') | ||
|
r28433 | self.walk_on_invalidate = self._ui.configbool( | ||
|
r43346 | 'fsmonitor', 'walk_on_invalidate' | ||
) | ||||
|
r34464 | self.timeout = float(self._ui.config('fsmonitor', 'timeout')) | ||
|
r28433 | |||
def get(self): | ||||
try: | ||||
|
r31215 | file = self._vfs('fsmonitor.state', 'rb') | ||
|
r28433 | except IOError as inst: | ||
|
r32816 | self._identity = util.filestat(None) | ||
|
r28433 | if inst.errno != errno.ENOENT: | ||
raise | ||||
return None, None, None | ||||
|
r32816 | self._identity = util.filestat.fromfp(file) | ||
|
r28433 | versionbytes = file.read(4) | ||
if len(versionbytes) < 4: | ||||
self._ui.log( | ||||
|
r43346 | 'fsmonitor', | ||
'fsmonitor: state file only has %d bytes, ' | ||||
'nuking state\n' % len(versionbytes), | ||||
) | ||||
|
r28433 | 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( | ||||
|
r43346 | 'fsmonitor', | ||
'fsmonitor: version switch from %d to ' | ||||
'%d, nuking state\n' % (diskversion, _version), | ||||
) | ||||
|
r28433 | self.invalidate() | ||
return None, None, None | ||||
state = file.read().split('\0') | ||||
# state = hostname\0clock\0ignorehash\0 + list of files, each | ||||
# followed by a \0 | ||||
|
r30539 | if len(state) < 3: | ||
self._ui.log( | ||||
|
r43346 | 'fsmonitor', | ||
'fsmonitor: state file truncated (expected ' | ||||
'3 chunks, found %d), nuking state\n', | ||||
len(state), | ||||
) | ||||
|
r30539 | self.invalidate() | ||
return None, None, None | ||||
|
r28433 | diskhostname = state[0] | ||
hostname = socket.gethostname() | ||||
if diskhostname != hostname: | ||||
# file got moved to a different host | ||||
|
r43346 | self._ui.log( | ||
'fsmonitor', | ||||
'fsmonitor: stored hostname "%s" ' | ||||
'different from current "%s", nuking state\n' | ||||
% (diskhostname, hostname), | ||||
) | ||||
|
r28433 | 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 | ||||
|
r32816 | # Read the identity from the file on disk rather than from the open file | ||
# pointer below, because the latter is actually a brand new file. | ||||
identity = util.filestat.frompath(self._vfs.join('fsmonitor.state')) | ||||
if identity != self._identity: | ||||
self._ui.debug('skip updating fsmonitor.state: identity mismatch\n') | ||||
return | ||||
|
r28433 | try: | ||
|
r43346 | file = self._vfs( | ||
'fsmonitor.state', 'wb', atomictemp=True, checkambig=True | ||||
) | ||||
|
r28433 | except (IOError, OSError): | ||
self._ui.warn(_("warning: unable to write out fsmonitor state\n")) | ||||
return | ||||
|
r30539 | with file: | ||
|
r28433 | 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') | ||||
def invalidate(self): | ||||
try: | ||||
os.unlink(os.path.join(self._rootdir, '.hg', 'fsmonitor.state')) | ||||
except OSError as inst: | ||||
if inst.errno != errno.ENOENT: | ||||
raise | ||||
|
r32816 | self._identity = util.filestat(None) | ||
|
r28433 | |||
def setlastclock(self, clock): | ||||
self._lastclock = clock | ||||
def getlastclock(self): | ||||
return self._lastclock | ||||