141 |
141 |
142 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for |
142 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for |
143 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should |
143 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should |
144 # be specifying the version(s) of Mercurial they are tested with, or |
144 # be specifying the version(s) of Mercurial they are tested with, or |
145 # leave the attribute unspecified. |
145 # leave the attribute unspecified. |
146 testedwith = 'ships-with-hg-core' |
146 testedwith = b'ships-with-hg-core' |
147 |
147 |
148 configtable = {} |
148 configtable = {} |
149 configitem = registrar.configitem(configtable) |
149 configitem = registrar.configitem(configtable) |
150 |
150 |
151 configitem( |
151 configitem( |
152 'fsmonitor', 'mode', default='on', |
152 b'fsmonitor', b'mode', default=b'on', |
153 ) |
153 ) |
154 configitem( |
154 configitem( |
155 'fsmonitor', 'walk_on_invalidate', default=False, |
155 b'fsmonitor', b'walk_on_invalidate', default=False, |
156 ) |
156 ) |
157 configitem( |
157 configitem( |
158 'fsmonitor', 'timeout', default='2', |
158 b'fsmonitor', b'timeout', default=b'2', |
159 ) |
159 ) |
160 configitem( |
160 configitem( |
161 'fsmonitor', 'blacklistusers', default=list, |
161 b'fsmonitor', b'blacklistusers', default=list, |
162 ) |
162 ) |
163 configitem( |
163 configitem( |
164 'fsmonitor', 'watchman_exe', default='watchman', |
164 b'fsmonitor', b'watchman_exe', default=b'watchman', |
165 ) |
165 ) |
166 configitem( |
166 configitem( |
167 'fsmonitor', 'verbose', default=True, experimental=True, |
167 b'fsmonitor', b'verbose', default=True, experimental=True, |
168 ) |
168 ) |
169 configitem( |
169 configitem( |
170 'experimental', 'fsmonitor.transaction_notify', default=False, |
170 b'experimental', b'fsmonitor.transaction_notify', default=False, |
171 ) |
171 ) |
172 |
172 |
173 # This extension is incompatible with the following blacklisted extensions |
173 # This extension is incompatible with the following blacklisted extensions |
174 # and will disable itself when encountering one of these: |
174 # and will disable itself when encountering one of these: |
175 _blacklist = ['largefiles', 'eol'] |
175 _blacklist = [b'largefiles', b'eol'] |
176 |
176 |
177 |
177 |
178 def debuginstall(ui, fm): |
178 def debuginstall(ui, fm): |
179 fm.write( |
179 fm.write( |
180 "fsmonitor-watchman", |
180 b"fsmonitor-watchman", |
181 _("fsmonitor checking for watchman binary... (%s)\n"), |
181 _(b"fsmonitor checking for watchman binary... (%s)\n"), |
182 ui.configpath("fsmonitor", "watchman_exe"), |
182 ui.configpath(b"fsmonitor", b"watchman_exe"), |
183 ) |
183 ) |
184 root = tempfile.mkdtemp() |
184 root = tempfile.mkdtemp() |
185 c = watchmanclient.client(ui, root) |
185 c = watchmanclient.client(ui, root) |
186 err = None |
186 err = None |
187 try: |
187 try: |
188 v = c.command("version") |
188 v = c.command(b"version") |
189 fm.write( |
189 fm.write( |
190 "fsmonitor-watchman-version", |
190 b"fsmonitor-watchman-version", |
191 _(" watchman binary version %s\n"), |
191 _(b" watchman binary version %s\n"), |
192 v["version"], |
192 v[b"version"], |
193 ) |
193 ) |
194 except watchmanclient.Unavailable as e: |
194 except watchmanclient.Unavailable as e: |
195 err = str(e) |
195 err = str(e) |
196 fm.condwrite( |
196 fm.condwrite( |
197 err, |
197 err, |
198 "fsmonitor-watchman-error", |
198 b"fsmonitor-watchman-error", |
199 _(" watchman binary missing or broken: %s\n"), |
199 _(b" watchman binary missing or broken: %s\n"), |
200 err, |
200 err, |
201 ) |
201 ) |
202 return 1 if err else 0 |
202 return 1 if err else 0 |
203 |
203 |
204 |
204 |
205 def _handleunavailable(ui, state, ex): |
205 def _handleunavailable(ui, state, ex): |
206 """Exception handler for Watchman interaction exceptions""" |
206 """Exception handler for Watchman interaction exceptions""" |
207 if isinstance(ex, watchmanclient.Unavailable): |
207 if isinstance(ex, watchmanclient.Unavailable): |
208 # experimental config: fsmonitor.verbose |
208 # experimental config: fsmonitor.verbose |
209 if ex.warn and ui.configbool('fsmonitor', 'verbose'): |
209 if ex.warn and ui.configbool(b'fsmonitor', b'verbose'): |
210 if 'illegal_fstypes' not in str(ex): |
210 if b'illegal_fstypes' not in str(ex): |
211 ui.warn(str(ex) + '\n') |
211 ui.warn(str(ex) + b'\n') |
212 if ex.invalidate: |
212 if ex.invalidate: |
213 state.invalidate() |
213 state.invalidate() |
214 # experimental config: fsmonitor.verbose |
214 # experimental config: fsmonitor.verbose |
215 if ui.configbool('fsmonitor', 'verbose'): |
215 if ui.configbool(b'fsmonitor', b'verbose'): |
216 ui.log('fsmonitor', 'Watchman unavailable: %s\n', ex.msg) |
216 ui.log(b'fsmonitor', b'Watchman unavailable: %s\n', ex.msg) |
217 else: |
217 else: |
218 ui.log('fsmonitor', 'Watchman exception: %s\n', ex) |
218 ui.log(b'fsmonitor', b'Watchman exception: %s\n', ex) |
219 |
219 |
220 |
220 |
221 def _hashignore(ignore): |
221 def _hashignore(ignore): |
222 """Calculate hash for ignore patterns and filenames |
222 """Calculate hash for ignore patterns and filenames |
223 |
223 |
261 Whenever full is False, ignored is False, and the Watchman client is |
261 Whenever full is False, ignored is False, and the Watchman client is |
262 available, use Watchman combined with saved state to possibly return only a |
262 available, use Watchman combined with saved state to possibly return only a |
263 subset of files.''' |
263 subset of files.''' |
264 |
264 |
265 def bail(reason): |
265 def bail(reason): |
266 self._ui.debug('fsmonitor: fallback to core status, %s\n' % reason) |
266 self._ui.debug(b'fsmonitor: fallback to core status, %s\n' % reason) |
267 return orig(match, subrepos, unknown, ignored, full=True) |
267 return orig(match, subrepos, unknown, ignored, full=True) |
268 |
268 |
269 if full: |
269 if full: |
270 return bail('full rewalk requested') |
270 return bail(b'full rewalk requested') |
271 if ignored: |
271 if ignored: |
272 return bail('listing ignored files') |
272 return bail(b'listing ignored files') |
273 if not self._watchmanclient.available(): |
273 if not self._watchmanclient.available(): |
274 return bail('client unavailable') |
274 return bail(b'client unavailable') |
275 state = self._fsmonitorstate |
275 state = self._fsmonitorstate |
276 clock, ignorehash, notefiles = state.get() |
276 clock, ignorehash, notefiles = state.get() |
277 if not clock: |
277 if not clock: |
278 if state.walk_on_invalidate: |
278 if state.walk_on_invalidate: |
279 return bail('no clock') |
279 return bail(b'no clock') |
280 # Initial NULL clock value, see |
280 # Initial NULL clock value, see |
281 # https://facebook.github.io/watchman/docs/clockspec.html |
281 # https://facebook.github.io/watchman/docs/clockspec.html |
282 clock = 'c:0:0' |
282 clock = b'c:0:0' |
283 notefiles = [] |
283 notefiles = [] |
284 |
284 |
285 ignore = self._ignore |
285 ignore = self._ignore |
286 dirignore = self._dirignore |
286 dirignore = self._dirignore |
287 if unknown: |
287 if unknown: |
288 if _hashignore(ignore) != ignorehash and clock != 'c:0:0': |
288 if _hashignore(ignore) != ignorehash and clock != b'c:0:0': |
289 # ignore list changed -- can't rely on Watchman state any more |
289 # ignore list changed -- can't rely on Watchman state any more |
290 if state.walk_on_invalidate: |
290 if state.walk_on_invalidate: |
291 return bail('ignore rules changed') |
291 return bail(b'ignore rules changed') |
292 notefiles = [] |
292 notefiles = [] |
293 clock = 'c:0:0' |
293 clock = b'c:0:0' |
294 else: |
294 else: |
295 # always ignore |
295 # always ignore |
296 ignore = util.always |
296 ignore = util.always |
297 dirignore = util.always |
297 dirignore = util.always |
298 |
298 |
299 matchfn = match.matchfn |
299 matchfn = match.matchfn |
300 matchalways = match.always() |
300 matchalways = match.always() |
301 dmap = self._map |
301 dmap = self._map |
302 if util.safehasattr(dmap, '_map'): |
302 if util.safehasattr(dmap, b'_map'): |
303 # for better performance, directly access the inner dirstate map if the |
303 # for better performance, directly access the inner dirstate map if the |
304 # standard dirstate implementation is in use. |
304 # standard dirstate implementation is in use. |
305 dmap = dmap._map |
305 dmap = dmap._map |
306 nonnormalset = self._map.nonnormalset |
306 nonnormalset = self._map.nonnormalset |
307 |
307 |
337 work = [d for d in work if not dirignore(d[0])] |
337 work = [d for d in work if not dirignore(d[0])] |
338 |
338 |
339 if not work and (exact or skipstep3): |
339 if not work and (exact or skipstep3): |
340 for s in subrepos: |
340 for s in subrepos: |
341 del results[s] |
341 del results[s] |
342 del results['.hg'] |
342 del results[b'.hg'] |
343 return results |
343 return results |
344 |
344 |
345 # step 2: query Watchman |
345 # step 2: query Watchman |
346 try: |
346 try: |
347 # Use the user-configured timeout for the query. |
347 # Use the user-configured timeout for the query. |
348 # Add a little slack over the top of the user query to allow for |
348 # Add a little slack over the top of the user query to allow for |
349 # overheads while transferring the data |
349 # overheads while transferring the data |
350 self._watchmanclient.settimeout(state.timeout + 0.1) |
350 self._watchmanclient.settimeout(state.timeout + 0.1) |
351 result = self._watchmanclient.command( |
351 result = self._watchmanclient.command( |
352 'query', |
352 b'query', |
353 { |
353 { |
354 'fields': ['mode', 'mtime', 'size', 'exists', 'name'], |
354 b'fields': [b'mode', b'mtime', b'size', b'exists', b'name'], |
355 'since': clock, |
355 b'since': clock, |
356 'expression': [ |
356 b'expression': [ |
357 'not', |
357 b'not', |
358 ['anyof', ['dirname', '.hg'], ['name', '.hg', 'wholename']], |
358 [ |
|
359 b'anyof', |
|
360 [b'dirname', b'.hg'], |
|
361 [b'name', b'.hg', b'wholename'], |
|
362 ], |
359 ], |
363 ], |
360 'sync_timeout': int(state.timeout * 1000), |
364 b'sync_timeout': int(state.timeout * 1000), |
361 'empty_on_fresh_instance': state.walk_on_invalidate, |
365 b'empty_on_fresh_instance': state.walk_on_invalidate, |
362 }, |
366 }, |
363 ) |
367 ) |
364 except Exception as ex: |
368 except Exception as ex: |
365 _handleunavailable(self._ui, state, ex) |
369 _handleunavailable(self._ui, state, ex) |
366 self._watchmanclient.clearconnection() |
370 self._watchmanclient.clearconnection() |
367 return bail('exception during run') |
371 return bail(b'exception during run') |
368 else: |
372 else: |
369 # We need to propagate the last observed clock up so that we |
373 # We need to propagate the last observed clock up so that we |
370 # can use it for our next query |
374 # can use it for our next query |
371 state.setlastclock(result['clock']) |
375 state.setlastclock(result[b'clock']) |
372 if result['is_fresh_instance']: |
376 if result[b'is_fresh_instance']: |
373 if state.walk_on_invalidate: |
377 if state.walk_on_invalidate: |
374 state.invalidate() |
378 state.invalidate() |
375 return bail('fresh instance') |
379 return bail(b'fresh instance') |
376 fresh_instance = True |
380 fresh_instance = True |
377 # Ignore any prior noteable files from the state info |
381 # Ignore any prior noteable files from the state info |
378 notefiles = [] |
382 notefiles = [] |
379 |
383 |
380 # for file paths which require normalization and we encounter a case |
384 # for file paths which require normalization and we encounter a case |
381 # collision, we store our own foldmap |
385 # collision, we store our own foldmap |
382 if normalize: |
386 if normalize: |
383 foldmap = dict((normcase(k), k) for k in results) |
387 foldmap = dict((normcase(k), k) for k in results) |
384 |
388 |
385 switch_slashes = pycompat.ossep == '\\' |
389 switch_slashes = pycompat.ossep == b'\\' |
386 # The order of the results is, strictly speaking, undefined. |
390 # The order of the results is, strictly speaking, undefined. |
387 # For case changes on a case insensitive filesystem we may receive |
391 # For case changes on a case insensitive filesystem we may receive |
388 # two entries, one with exists=True and another with exists=False. |
392 # two entries, one with exists=True and another with exists=False. |
389 # The exists=True entries in the same response should be interpreted |
393 # The exists=True entries in the same response should be interpreted |
390 # as being happens-after the exists=False entries due to the way that |
394 # as being happens-after the exists=False entries due to the way that |
391 # Watchman tracks files. We use this property to reconcile deletes |
395 # Watchman tracks files. We use this property to reconcile deletes |
392 # for name case changes. |
396 # for name case changes. |
393 for entry in result['files']: |
397 for entry in result[b'files']: |
394 fname = entry['name'] |
398 fname = entry[b'name'] |
395 if _fixencoding: |
399 if _fixencoding: |
396 fname = _watchmantofsencoding(fname) |
400 fname = _watchmantofsencoding(fname) |
397 if switch_slashes: |
401 if switch_slashes: |
398 fname = fname.replace('\\', '/') |
402 fname = fname.replace(b'\\', b'/') |
399 if normalize: |
403 if normalize: |
400 normed = normcase(fname) |
404 normed = normcase(fname) |
401 fname = normalize(fname, True, True) |
405 fname = normalize(fname, True, True) |
402 foldmap[normed] = fname |
406 foldmap[normed] = fname |
403 fmode = entry['mode'] |
407 fmode = entry[b'mode'] |
404 fexists = entry['exists'] |
408 fexists = entry[b'exists'] |
405 kind = getkind(fmode) |
409 kind = getkind(fmode) |
406 |
410 |
407 if '/.hg/' in fname or fname.endswith('/.hg'): |
411 if b'/.hg/' in fname or fname.endswith(b'/.hg'): |
408 return bail('nested-repo-detected') |
412 return bail(b'nested-repo-detected') |
409 |
413 |
410 if not fexists: |
414 if not fexists: |
411 # if marked as deleted and we don't already have a change |
415 # if marked as deleted and we don't already have a change |
412 # record, mark it as deleted. If we already have an entry |
416 # record, mark it as deleted. If we already have an entry |
413 # for fname then it was either part of walkexplicit or was |
417 # for fname then it was either part of walkexplicit or was |
507 listclean = clean |
511 listclean = clean |
508 listunknown = unknown |
512 listunknown = unknown |
509 |
513 |
510 def _cmpsets(l1, l2): |
514 def _cmpsets(l1, l2): |
511 try: |
515 try: |
512 if 'FSMONITOR_LOG_FILE' in encoding.environ: |
516 if b'FSMONITOR_LOG_FILE' in encoding.environ: |
513 fn = encoding.environ['FSMONITOR_LOG_FILE'] |
517 fn = encoding.environ[b'FSMONITOR_LOG_FILE'] |
514 f = open(fn, 'wb') |
518 f = open(fn, b'wb') |
515 else: |
519 else: |
516 fn = 'fsmonitorfail.log' |
520 fn = b'fsmonitorfail.log' |
517 f = self.vfs.open(fn, 'wb') |
521 f = self.vfs.open(fn, b'wb') |
518 except (IOError, OSError): |
522 except (IOError, OSError): |
519 self.ui.warn(_('warning: unable to write to %s\n') % fn) |
523 self.ui.warn(_(b'warning: unable to write to %s\n') % fn) |
520 return |
524 return |
521 |
525 |
522 try: |
526 try: |
523 for i, (s1, s2) in enumerate(zip(l1, l2)): |
527 for i, (s1, s2) in enumerate(zip(l1, l2)): |
524 if set(s1) != set(s2): |
528 if set(s1) != set(s2): |
525 f.write('sets at position %d are unequal\n' % i) |
529 f.write(b'sets at position %d are unequal\n' % i) |
526 f.write('watchman returned: %s\n' % s1) |
530 f.write(b'watchman returned: %s\n' % s1) |
527 f.write('stat returned: %s\n' % s2) |
531 f.write(b'stat returned: %s\n' % s2) |
528 finally: |
532 finally: |
529 f.close() |
533 f.close() |
530 |
534 |
531 if isinstance(node1, context.changectx): |
535 if isinstance(node1, context.changectx): |
532 ctx1 = node1 |
536 ctx1 = node1 |
690 |
694 |
691 |
695 |
692 def wrapdirstate(orig, self): |
696 def wrapdirstate(orig, self): |
693 ds = orig(self) |
697 ds = orig(self) |
694 # only override the dirstate when Watchman is available for the repo |
698 # only override the dirstate when Watchman is available for the repo |
695 if util.safehasattr(self, '_fsmonitorstate'): |
699 if util.safehasattr(self, b'_fsmonitorstate'): |
696 makedirstate(self, ds) |
700 makedirstate(self, ds) |
697 return ds |
701 return ds |
698 |
702 |
699 |
703 |
700 def extsetup(ui): |
704 def extsetup(ui): |
701 extensions.wrapfilecache( |
705 extensions.wrapfilecache( |
702 localrepo.localrepository, 'dirstate', wrapdirstate |
706 localrepo.localrepository, b'dirstate', wrapdirstate |
703 ) |
707 ) |
704 if pycompat.isdarwin: |
708 if pycompat.isdarwin: |
705 # An assist for avoiding the dangling-symlink fsevents bug |
709 # An assist for avoiding the dangling-symlink fsevents bug |
706 extensions.wrapfunction(os, 'symlink', wrapsymlink) |
710 extensions.wrapfunction(os, b'symlink', wrapsymlink) |
707 |
711 |
708 extensions.wrapfunction(merge, 'update', wrapupdate) |
712 extensions.wrapfunction(merge, b'update', wrapupdate) |
709 |
713 |
710 |
714 |
711 def wrapsymlink(orig, source, link_name): |
715 def wrapsymlink(orig, source, link_name): |
712 ''' if we create a dangling symlink, also touch the parent dir |
716 ''' if we create a dangling symlink, also touch the parent dir |
713 to encourage fsevents notifications to work more correctly ''' |
717 to encourage fsevents notifications to work more correctly ''' |
754 # Make sure we have a wlock prior to sending notifications to watchman. |
758 # Make sure we have a wlock prior to sending notifications to watchman. |
755 # We don't want to race with other actors. In the update case, |
759 # We don't want to race with other actors. In the update case, |
756 # merge.update is going to take the wlock almost immediately. We are |
760 # merge.update is going to take the wlock almost immediately. We are |
757 # effectively extending the lock around several short sanity checks. |
761 # effectively extending the lock around several short sanity checks. |
758 if self.oldnode is None: |
762 if self.oldnode is None: |
759 self.oldnode = self.repo['.'].node() |
763 self.oldnode = self.repo[b'.'].node() |
760 |
764 |
761 if self.repo.currentwlock() is None: |
765 if self.repo.currentwlock() is None: |
762 if util.safehasattr(self.repo, 'wlocknostateupdate'): |
766 if util.safehasattr(self.repo, b'wlocknostateupdate'): |
763 self._lock = self.repo.wlocknostateupdate() |
767 self._lock = self.repo.wlocknostateupdate() |
764 else: |
768 else: |
765 self._lock = self.repo.wlock() |
769 self._lock = self.repo.wlock() |
766 self.need_leave = self._state('state-enter', hex(self.oldnode)) |
770 self.need_leave = self._state(b'state-enter', hex(self.oldnode)) |
767 return self |
771 return self |
768 |
772 |
769 def __exit__(self, type_, value, tb): |
773 def __exit__(self, type_, value, tb): |
770 abort = True if type_ else False |
774 abort = True if type_ else False |
771 self.exit(abort=abort) |
775 self.exit(abort=abort) |
772 |
776 |
773 def exit(self, abort=False): |
777 def exit(self, abort=False): |
774 try: |
778 try: |
775 if self.need_leave: |
779 if self.need_leave: |
776 status = 'failed' if abort else 'ok' |
780 status = b'failed' if abort else b'ok' |
777 if self.newnode is None: |
781 if self.newnode is None: |
778 self.newnode = self.repo['.'].node() |
782 self.newnode = self.repo[b'.'].node() |
779 if self.distance is None: |
783 if self.distance is None: |
780 self.distance = calcdistance( |
784 self.distance = calcdistance( |
781 self.repo, self.oldnode, self.newnode |
785 self.repo, self.oldnode, self.newnode |
782 ) |
786 ) |
783 self._state('state-leave', hex(self.newnode), status=status) |
787 self._state(b'state-leave', hex(self.newnode), status=status) |
784 finally: |
788 finally: |
785 self.need_leave = False |
789 self.need_leave = False |
786 if self._lock: |
790 if self._lock: |
787 self._lock.release() |
791 self._lock.release() |
788 |
792 |
789 def _state(self, cmd, commithash, status='ok'): |
793 def _state(self, cmd, commithash, status=b'ok'): |
790 if not util.safehasattr(self.repo, '_watchmanclient'): |
794 if not util.safehasattr(self.repo, b'_watchmanclient'): |
791 return False |
795 return False |
792 try: |
796 try: |
793 self.repo._watchmanclient.command( |
797 self.repo._watchmanclient.command( |
794 cmd, |
798 cmd, |
795 { |
799 { |
796 'name': self.name, |
800 b'name': self.name, |
797 'metadata': { |
801 b'metadata': { |
798 # the target revision |
802 # the target revision |
799 'rev': commithash, |
803 b'rev': commithash, |
800 # approximate number of commits between current and target |
804 # approximate number of commits between current and target |
801 'distance': self.distance if self.distance else 0, |
805 b'distance': self.distance if self.distance else 0, |
802 # success/failure (only really meaningful for state-leave) |
806 # success/failure (only really meaningful for state-leave) |
803 'status': status, |
807 b'status': status, |
804 # whether the working copy parent is changing |
808 # whether the working copy parent is changing |
805 'partial': self.partial, |
809 b'partial': self.partial, |
806 }, |
810 }, |
807 }, |
811 }, |
808 ) |
812 ) |
809 return True |
813 return True |
810 except Exception as e: |
814 except Exception as e: |
811 # Swallow any errors; fire and forget |
815 # Swallow any errors; fire and forget |
812 self.repo.ui.log( |
816 self.repo.ui.log( |
813 'watchman', 'Exception %s while running %s\n', e, cmd |
817 b'watchman', b'Exception %s while running %s\n', e, cmd |
814 ) |
818 ) |
815 return False |
819 return False |
816 |
820 |
817 |
821 |
818 # Estimate the distance between two nodes |
822 # Estimate the distance between two nodes |
885 exts = extensions.enabled() |
889 exts = extensions.enabled() |
886 for ext in _blacklist: |
890 for ext in _blacklist: |
887 if ext in exts: |
891 if ext in exts: |
888 ui.warn( |
892 ui.warn( |
889 _( |
893 _( |
890 'The fsmonitor extension is incompatible with the %s ' |
894 b'The fsmonitor extension is incompatible with the %s ' |
891 'extension and has been disabled.\n' |
895 b'extension and has been disabled.\n' |
892 ) |
896 ) |
893 % ext |
897 % ext |
894 ) |
898 ) |
895 return |
899 return |
896 |
900 |
897 if repo.local(): |
901 if repo.local(): |
898 # We don't work with subrepos either. |
902 # We don't work with subrepos either. |
899 # |
903 # |
900 # if repo[None].substate can cause a dirstate parse, which is too |
904 # if repo[None].substate can cause a dirstate parse, which is too |
901 # slow. Instead, look for a file called hgsubstate, |
905 # slow. Instead, look for a file called hgsubstate, |
902 if repo.wvfs.exists('.hgsubstate') or repo.wvfs.exists('.hgsub'): |
906 if repo.wvfs.exists(b'.hgsubstate') or repo.wvfs.exists(b'.hgsub'): |
903 return |
907 return |
904 |
908 |
905 if repo_has_depth_one_nested_repo(repo): |
909 if repo_has_depth_one_nested_repo(repo): |
906 return |
910 return |
907 |
911 |
908 fsmonitorstate = state.state(repo) |
912 fsmonitorstate = state.state(repo) |
909 if fsmonitorstate.mode == 'off': |
913 if fsmonitorstate.mode == b'off': |
910 return |
914 return |
911 |
915 |
912 try: |
916 try: |
913 client = watchmanclient.client(repo.ui, repo._root) |
917 client = watchmanclient.client(repo.ui, repo._root) |
914 except Exception as ex: |
918 except Exception as ex: |