Mercurial > hg
comparison 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 |
comparison
equal
deleted
inserted
replaced
28432:2377c4ac4eec | 28433:3b67f27bb908 |
---|---|
1 # state.py - fsmonitor persistent state | |
2 # | |
3 # Copyright 2013-2016 Facebook, Inc. | |
4 # | |
5 # This software may be used and distributed according to the terms of the | |
6 # GNU General Public License version 2 or any later version. | |
7 | |
8 from __future__ import absolute_import | |
9 | |
10 import errno | |
11 import os | |
12 import socket | |
13 import struct | |
14 | |
15 from mercurial import pathutil | |
16 from mercurial.i18n import _ | |
17 | |
18 _version = 4 | |
19 _versionformat = ">I" | |
20 | |
21 class state(object): | |
22 def __init__(self, repo): | |
23 self._opener = repo.opener | |
24 self._ui = repo.ui | |
25 self._rootdir = pathutil.normasprefix(repo.root) | |
26 self._lastclock = None | |
27 | |
28 self.mode = self._ui.config('fsmonitor', 'mode', default='on') | |
29 self.walk_on_invalidate = self._ui.configbool( | |
30 'fsmonitor', 'walk_on_invalidate', False) | |
31 self.timeout = float(self._ui.config( | |
32 'fsmonitor', 'timeout', default='2')) | |
33 | |
34 def get(self): | |
35 try: | |
36 file = self._opener('fsmonitor.state', 'rb') | |
37 except IOError as inst: | |
38 if inst.errno != errno.ENOENT: | |
39 raise | |
40 return None, None, None | |
41 | |
42 versionbytes = file.read(4) | |
43 if len(versionbytes) < 4: | |
44 self._ui.log( | |
45 'fsmonitor', 'fsmonitor: state file only has %d bytes, ' | |
46 'nuking state\n' % len(versionbytes)) | |
47 self.invalidate() | |
48 return None, None, None | |
49 try: | |
50 diskversion = struct.unpack(_versionformat, versionbytes)[0] | |
51 if diskversion != _version: | |
52 # different version, nuke state and start over | |
53 self._ui.log( | |
54 'fsmonitor', 'fsmonitor: version switch from %d to ' | |
55 '%d, nuking state\n' % (diskversion, _version)) | |
56 self.invalidate() | |
57 return None, None, None | |
58 | |
59 state = file.read().split('\0') | |
60 # state = hostname\0clock\0ignorehash\0 + list of files, each | |
61 # followed by a \0 | |
62 diskhostname = state[0] | |
63 hostname = socket.gethostname() | |
64 if diskhostname != hostname: | |
65 # file got moved to a different host | |
66 self._ui.log('fsmonitor', 'fsmonitor: stored hostname "%s" ' | |
67 'different from current "%s", nuking state\n' % | |
68 (diskhostname, hostname)) | |
69 self.invalidate() | |
70 return None, None, None | |
71 | |
72 clock = state[1] | |
73 ignorehash = state[2] | |
74 # discard the value after the last \0 | |
75 notefiles = state[3:-1] | |
76 | |
77 finally: | |
78 file.close() | |
79 | |
80 return clock, ignorehash, notefiles | |
81 | |
82 def set(self, clock, ignorehash, notefiles): | |
83 if clock is None: | |
84 self.invalidate() | |
85 return | |
86 | |
87 try: | |
88 file = self._opener('fsmonitor.state', 'wb') | |
89 except (IOError, OSError): | |
90 self._ui.warn(_("warning: unable to write out fsmonitor state\n")) | |
91 return | |
92 | |
93 try: | |
94 file.write(struct.pack(_versionformat, _version)) | |
95 file.write(socket.gethostname() + '\0') | |
96 file.write(clock + '\0') | |
97 file.write(ignorehash + '\0') | |
98 if notefiles: | |
99 file.write('\0'.join(notefiles)) | |
100 file.write('\0') | |
101 finally: | |
102 file.close() | |
103 | |
104 def invalidate(self): | |
105 try: | |
106 os.unlink(os.path.join(self._rootdir, '.hg', 'fsmonitor.state')) | |
107 except OSError as inst: | |
108 if inst.errno != errno.ENOENT: | |
109 raise | |
110 | |
111 def setlastclock(self, clock): | |
112 self._lastclock = clock | |
113 | |
114 def getlastclock(self): | |
115 return self._lastclock |