durin42 created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers.
REVISION SUMMARY The blocking of SIGINT is not done by default, but my hope is that we will one day. This was inspired by Facebook's "nointerrupt" extension, which is a bit more heavy-handed than this (whole commands are treated as unsafe to interrupt). A future patch will enable this for varying bits of Mercurial that are performing unsafe operations. .. api:: New context manager ``ui.unsafeoperation()`` to mark portions of a command as potentially unsafe places to interrupt Mercurial with Control-C or similar. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D3716 AFFECTED FILES mercurial/configitems.py mercurial/ui.py CHANGE DETAILS diff --git a/mercurial/ui.py b/mercurial/ui.py --- a/mercurial/ui.py +++ b/mercurial/ui.py @@ -224,6 +224,7 @@ self._colormode = None self._terminfoparams = {} self._styles = {} + self._oldsiginthandler = None if src: self.fout = src.fout @@ -334,6 +335,39 @@ self._blockedtimes[key + '_blocked'] += \ (util.timer() - starttime) * 1000 + @contextlib.contextmanager + def unsafeoperation(self): + """Mark an operation as unsafe. + + Most operations on a repository are safe to interrupt, but a + few are risky (for example repair.strip). This context manager + lets you advise Mercurial that something risky is happening so + that control-C etc can be blocked if desired. + """ + enabled = self.configbool('experimental', 'nointerrupt') + inter = self.interactive() or not self.configbool( + 'experimental', 'nointerrupt-interactiveonly') + if not (enabled and inter and self._oldsiginthandler is None): + # if nointerrupt support is turned off, the process isn't + # interactive, or we're already in an unsafeoperation + # block, do nothing. + yield + return + + def disablesiginthandler(*args): + self.warn(self.config('experimental', 'nointerrupt-message') + '\n') + signal.signal(signal.SIGINT, self._oldsiginthandler) + self._oldsiginthandler = None + + try: + self._oldsiginthandler = signal.getsignal(signal.SIGINT) + signal.signal(signal.SIGINT, disablesiginthandler) + yield + finally: + if self._oldsiginthandler is not None: + signal.signal(signal.SIGINT, self._oldsiginthandler) + self._oldsiginthandler = None + def formatter(self, topic, opts): return formatter.formatter(self, self, topic, opts) diff --git a/mercurial/configitems.py b/mercurial/configitems.py --- a/mercurial/configitems.py +++ b/mercurial/configitems.py @@ -560,6 +560,17 @@ coreconfigitem('experimental', 'mergedriver', default=None, ) +coreconfigitem('experimental', 'nointerrupt', default=False) +_nointmsg = """ +========================== +Interrupting Mercurial may leave your repo in a bad state. +If you really want to interrupt your current command, press +CTRL-C again. +========================== +""".strip() +coreconfigitem('experimental', 'nointerrupt-message', default=_nointmsg) +coreconfigitem('experimental', 'nointerrupt-interactiveonly', default=True) + coreconfigitem('experimental', 'obsmarkers-exchange-debug', default=False, ) To: durin42, #hg-reviewers Cc: mercurial-devel _______________________________________________ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel