1# wctxcleaner.py - check and clean dirty working directory 2# 3# Copyright 2011 Steve Borho <steve@borho.org> 4# 5# This software may be used and distributed according to the terms of the 6# GNU General Public License version 2, incorporated herein by reference. 7 8from __future__ import absolute_import 9 10from .qtcore import ( 11 QObject, 12 QThread, 13 pyqtSignal, 14 pyqtSlot, 15) 16from .qtgui import ( 17 QMessageBox, 18 QWidget, 19) 20 21from mercurial import ( 22 cmdutil, 23 error, 24 hg, 25) 26 27from ..util.i18n import _ 28from . import ( 29 cmdcore, 30 cmdui, 31 qtlib, 32 thgrepo, 33) 34 35def _checkchanged(repo): 36 try: 37 cmdutil.bailifchanged(repo) 38 return False 39 except error.Abort: 40 return True 41 42 43class CheckThread(QThread): 44 def __init__(self, repo, parent): 45 QThread.__init__(self, parent) 46 self.repo = hg.repository(repo.ui, repo.root) 47 self.results = (False, 1) 48 self.canceled = False 49 50 def run(self): 51 self.repo.invalidate() 52 self.repo.invalidatedirstate() 53 unresolved = False 54 for root, path, status in thgrepo.recursiveMergeStatus(self.repo): 55 if self.canceled: 56 return 57 if status == b'u': 58 unresolved = True 59 break 60 wctx = self.repo[None] 61 try: 62 dirty = _checkchanged(self.repo) or unresolved 63 self.results = (dirty, len(wctx.parents())) 64 except EnvironmentError: 65 self.results = (True, len(wctx.parents())) 66 67 def cancel(self): 68 self.canceled = True 69 70 71class WctxCleaner(QObject): 72 73 checkStarted = pyqtSignal() 74 checkFinished = pyqtSignal(bool, int) # clean, parents 75 76 def __init__(self, repoagent, parent=None): 77 super(WctxCleaner, self).__init__(parent) 78 assert parent is None or isinstance(parent, QWidget), parent 79 self._repoagent = repoagent 80 self._cmdsession = cmdcore.nullCmdSession() 81 self._checkth = CheckThread(repoagent.rawRepo(), self) 82 self._checkth.started.connect(self.checkStarted) 83 self._checkth.finished.connect(self._onCheckFinished) 84 self._clean = False 85 86 @pyqtSlot() 87 def check(self): 88 """Check states of working directory asynchronously""" 89 if self._checkth.isRunning(): 90 return 91 self._checkth.start() 92 93 def cancelCheck(self): 94 self._checkth.cancel() 95 self._checkth.wait() 96 97 def isChecking(self): 98 return self._checkth.isRunning() 99 100 def isCheckCanceled(self): 101 return self._checkth.canceled 102 103 def isClean(self): 104 return self._clean 105 106 @pyqtSlot() 107 def _onCheckFinished(self): 108 dirty, parents = self._checkth.results 109 self._clean = not dirty 110 self.checkFinished.emit(not dirty, parents) 111 112 @pyqtSlot(str) 113 def runCleaner(self, cmd): 114 """Clean working directory by the specified action""" 115 cmd = str(cmd) 116 if cmd == 'commit': 117 self.launchCommitDialog() 118 elif cmd == 'shelve': 119 self.launchShelveDialog() 120 elif cmd.startswith('discard'): 121 confirm = cmd != 'discard:noconfirm' 122 self.discardChanges(confirm) 123 else: 124 raise ValueError('unknown command: %s' % cmd) 125 126 def launchCommitDialog(self): 127 from tortoisehg.hgqt import commit 128 dlg = commit.CommitDialog(self._repoagent, [], {}, self.parent()) 129 dlg.finished.connect(dlg.deleteLater) 130 dlg.exec_() 131 self.check() 132 133 def launchShelveDialog(self): 134 from tortoisehg.hgqt import shelve 135 dlg = shelve.ShelveDialog(self._repoagent, self.parent()) 136 dlg.finished.connect(dlg.deleteLater) 137 dlg.exec_() 138 self.check() 139 140 def discardChanges(self, confirm=True): 141 if confirm: 142 labels = [(QMessageBox.Yes, _('&Discard')), 143 (QMessageBox.No, _('Cancel'))] 144 if not qtlib.QuestionMsgBox(_('Confirm Discard'), 145 _('Discard outstanding changes to working directory?'), 146 labels=labels, parent=self.parent()): 147 return 148 149 cmdline = ['update', '--clean', '--rev', '.'] 150 self._cmdsession = sess = self._repoagent.runCommand(cmdline, self) 151 sess.commandFinished.connect(self._onCommandFinished) 152 153 @pyqtSlot(int) 154 def _onCommandFinished(self, ret): 155 if ret == 0: 156 self.check() 157 else: 158 cmdui.errorMessageBox(self._cmdsession, self.parent()) 159