def linkActivated(self, cmd): if cmd == 'resolve': dlg = resolve.ResolveDialog(self.repo, self) dlg.exec_() self.checkResolve() elif cmd == 'commit': dlg = commit.CommitDialog(self.repo, [], {}, self) dlg.finished.connect(dlg.deleteLater) dlg.exec_() self.destcsinfo.update(self.repo['.']) self.checkStatus() elif cmd == 'discard': labels = [(QMessageBox.Yes, _('&Discard')), (QMessageBox.No, _('Cancel'))] if not qtlib.QuestionMsgBox(_('Confirm Discard'), _('Discard outstanding changes to working directory?'), labels=labels, parent=self): return def finished(ret): self.repo.decrementBusyCount() if ret == 0: self.checkStatus() cmdline = ['update', '--clean', '--repository', self.repo.root, '--rev', '.'] self.runner = cmdui.Runner(True, self) self.runner.commandFinished.connect(finished) self.repo.incrementBusyCount() self.runner.run(cmdline)
def _showexceptiondialog(self): from tortoisehg.hgqt.bugreport import BugReport, ExceptionMsgBox opts = {} opts['cmd'] = ' '.join(sys.argv[1:]) opts['error'] = ''.join(''.join(traceback.format_exception(*args)) for args in self.errors) etype, evalue = self.errors[0][:2] if (len(set(e[0] for e in self.errors)) == 1 and etype in self._recoverableexc): opts['values'] = evalue errstr = self._recoverableexc[etype] if etype is error.Abort and evalue.hint: errstr = u''.join( [errstr, u'<br><b>', _('hint:'), u'</b> %(arg1)s']) opts['values'] = [str(evalue), evalue.hint] dlg = ExceptionMsgBox(hglib.tounicode(str(evalue)), hglib.tounicode(errstr), opts, parent=self._mainapp.activeWindow()) elif etype is KeyboardInterrupt: if qtlib.QuestionMsgBox( hglib.tounicode(_('Keyboard interrupt')), hglib.tounicode(_('Close this application?'))): QApplication.quit() else: self.errors = [] return else: dlg = BugReport(opts, parent=self._mainapp.activeWindow()) dlg.exec_()
def reject(self): if os.path.exists(self.repo.join('rebasestate')): main = _('Exiting with an unfinished rebase is not recommended.') text = _('Consider aborting the rebase first.') labels = ((QMessageBox.Yes, _('&Exit')), (QMessageBox.No, _('Cancel'))) if not qtlib.QuestionMsgBox( _('Confirm Exit'), main, text, labels=labels, parent=self): return super(RebaseDialog, self).reject()
def init(self): dest = self.getPath() if dest == '': qtlib.ErrorMsgBox(_('Error executing init'), _('Destination path is empty'), _('Please enter the directory path')) self.dest_edit.setFocus() return False dest = os.path.normpath(dest) self.dest_edit.setText(hglib.tounicode(dest)) udest = self.dest_edit.text() if not os.path.exists(dest): p = dest l = 0 while not os.path.exists(p): l += 1 p, t = os.path.split(p) if not t: break # already root path if l > 1: res = qtlib.QuestionMsgBox( _('Init'), _('Are you sure about adding the new repository ' '%d extra levels deep?') % l, _('Path exists up to:\n%s\nand you asked for:\n%s') % (p, udest), defaultbutton=QMessageBox.No) if not res: self.dest_edit.setFocus() return try: # create the folder, just like Hg would os.makedirs(dest) except: qtlib.ErrorMsgBox(_('Error executing init'), _('Cannot create folder %s') % udest) return False _ui = ui.ui() # dotencode is the new default repo format in Mercurial 1.7 if self.make_pre_1_7_chk.isChecked(): _ui.setconfig('format', 'dotencode', 'False') try: # create the new repo hg.repository(_ui, dest, create=1) except error.RepoError, inst: qtlib.ErrorMsgBox(_('Error executing init'), _('Unable to create new repository'), hglib.tounicode(str(inst))) return False
def rename(self): """execute the rename""" # check inputs src = self.get_src() dest = self.get_dest() if not os.path.exists(src): qtlib.WarningMsgBox(self.msgTitle, _('Source does not exists.')) return fullsrc = os.path.abspath(src) if not fullsrc.startswith(self.repo.root): qtlib.ErrorMsgBox(self.errTitle, _('The source must be within the repository tree.')) return fulldest = os.path.abspath(dest) if not fulldest.startswith(self.repo.root): qtlib.ErrorMsgBox(self.errTitle, _('The destination must be within the repository tree.')) return if src == dest: qtlib.ErrorMsgBox(self.errTitle, _('Please give a destination that differs from the source')) return if (os.path.isfile(dest) and not self.isCaseFoldingOnWin()): res = qtlib.QuestionMsgBox(self.msgTitle, '<p>%s</p><p>%s</p>' % (_('Destination file already exists.'), _('Are you sure you want to overwrite it ?')), defaultbutton=QMessageBox.No) if not res: return cmdline = self.compose_command(src, dest) self.show_command(cmdline) if self.isCaseFoldingOnWin(): # We do the rename ourselves if it's a pure casefolding # action on Windows. Because there is no way to make Hg # do 'hg mv foo Foo' correctly there. if self.copy_chk.isChecked(): qtlib.ErrorMsgBox(self.errTitle, _('Cannot do a pure casefolding copy on Windows')) return else: try: targetdir = os.path.dirname(fulldest) if not os.path.isdir(targetdir): os.makedirs(targetdir) os.rename(fullsrc, fulldest) except (OSError, IOError), inst: if self.copy_chk.isChecked(): txt = _('The following error was caught while copying:') else: txt = _('The following error was caught while renaming:') qtlib.ErrorMsgBox(self.errTitle, txt, hglib.tounicode(str(inst))) return
def canExit(self): if len(self.repo.parents()) == 2: main = _('Do you want to exit?') text = _('To finish merging, you need to commit ' 'the working directory.') labels = ((QMessageBox.Yes, _('&Exit')), (QMessageBox.No, _('Cancel'))) if not qtlib.QuestionMsgBox( _('Confirm Exit'), main, text, labels=labels, parent=self): return False return True
def deleteShelfB(self): shelf = self.currentPatchB() ushelf = hglib.tounicode(os.path.basename(shelf)) if not qtlib.QuestionMsgBox(_('Are you sure?'), _('Delete shelf file %s?') % ushelf): return try: os.unlink(shelf) self.showMessage(_('Shelf deleted')) except EnvironmentError, e: self.showMessage(hglib.tounicode(str(e)))
def clearShelfB(self): shelf = self.currentPatchB() ushelf = hglib.tounicode(os.path.basename(shelf)) if not qtlib.QuestionMsgBox(_('Are you sure?'), _('Clear contents of shelf file %s?') % ushelf): return try: f = open(shelf, "w") f.close() self.showMessage(_('Shelf cleared')) except EnvironmentError, e: self.showMessage(hglib.tounicode(str(e)))
def reject(self): s = QSettings() s.setValue('resolve/geom', self.saveGeometry()) if self.utree.model().rowCount() > 0: main = _('Exit without finishing resolve?') text = _('Unresolved conflicts remain. Are you sure?') labels = ((QMessageBox.Yes, _('E&xit')), (QMessageBox.No, _('Cancel'))) if not qtlib.QuestionMsgBox(_('Confirm Exit'), main, text, labels=labels, parent=self): return super(ResolveDialog, self).reject()
def qqueueActivate(self): uq = self.ql.item(self.ql.currentRow()).text() q = hglib.fromunicode(uq) if q == self.repo.thgactivemqname: return if qtlib.QuestionMsgBox( _('Confirm patch queue switch'), _("Do you really want to activate patch queue '%s' ?") % uq, parent=self, defaultbutton=QMessageBox.No): opts = [q] self.qqueueCommand(opts)
def reject(self): if self.th and self.th.isRunning(): return if os.path.exists(self._graftstatefile): main = _('Exiting with an unfinished graft is not recommended.') text = _('Consider aborting the graft first.') labels = ((QMessageBox.Yes, _('&Exit')), (QMessageBox.No, _('Cancel'))) if not qtlib.QuestionMsgBox(_('Confirm Exit'), main, text, labels=labels, parent=self): return super(GraftDialog, self).reject()
def archive(self): # verify input type = self.get_selected_archive_type()['type'] dest = unicode(self.dest_edit.text()) if os.path.exists(dest): if type == 'files': if os.path.isfile(dest): qtlib.WarningMsgBox(_('Duplicate Name'), _('The destination "%s" already exists as ' 'a file!') % dest) return False elif os.listdir(dest): if not qtlib.QuestionMsgBox(_('Confirm Overwrite'), _('The directory "%s" is not empty!\n\n' 'Do you want to overwrite it?') % dest, parent=self): return False else: if os.path.isfile(dest): if not qtlib.QuestionMsgBox(_('Confirm Overwrite'), _('The file "%s" already exists!\n\n' 'Do you want to overwrite it?') % dest, parent=self): return False else: qtlib.WarningMsgBox(_('Duplicate Name'), _('The destination "%s" already exists as ' 'a folder!') % dest) return False # prepare command line cmdline = self.compose_command(hglib.fromunicode(dest), type) if self.files_in_rev_chk.isChecked(): self.savedcwd = os.getcwd() os.chdir(self.repo.root) # start archiving self.cmd.run(cmdline)
def qqueueDelete(self): uq = self.ql.item(self.ql.currentRow()).text() q = hglib.fromunicode(uq) if q == 'patches': return if qtlib.QuestionMsgBox( _('Confirm patch queue delete'), _("Do you really want to delete patch queue '%s' ?") % uq, parent=self, defaultbutton=QMessageBox.No): opts = ['--delete', q] self.needsRefresh = True self.qqueueCommand(opts)
def clearShelfA(self): if self.comboa.currentIndex() == 0: if not qtlib.QuestionMsgBox(_('Are you sure?'), _('Revert all working copy changes?')): return try: self.repo.ui.quiet = True commands.revert(self.repo.ui, self.repo, all=True) self.repo.ui.quiet = False except (EnvironmentError, error.Abort), e: self.showMessage(hglib.tounicode(str(e))) self.refreshCombos() return
def canExit(self): if len(self.repo.parents()) == 2: main = _('Do you want to exit?') text = _('To finish merging, you must commit ' 'the working directory.\n\n' 'To cancel the merge you can update to one ' 'of the merge parent revisions.') labels = ((QMessageBox.Yes, _('&Exit')), (QMessageBox.No, _('Cancel'))) if not qtlib.QuestionMsgBox( _('Confirm Exit'), main, text, labels=labels, parent=self): return False return True
def checkGuardsOrComments(): cont = True for p in self.repo.mq.fullseries: if '#' in p: cont = qtlib.QuestionMsgBox( 'Confirm qreorder', _('<p>ATTENTION!<br>' 'Guard or comment found.<br>' 'Reordering patches will destroy them.<br>' '<br>Continue?</p>'), parent=self, defaultbutton=QMessageBox.No) break return cont
def checkForRejects(repo, rawoutput, parent=None): """Parse output of qpush/qpop to resolve hunk failure manually""" rejre = re.compile('saving rejects to file (.*).rej') for m in rejre.finditer(rawoutput): wfile = m.groups()[0] if not os.path.exists(repo.wjoin(wfile)): continue ufile = hglib.tounicode(wfile) if qtlib.QuestionMsgBox(_('Manually resolve rejected chunks?'), _('%s had rejected chunks, edit patched ' 'file together with rejects?') % ufile, parent=parent): dlg = rejects.RejectsDialog(repo.wjoin(wfile), parent) dlg.exec_()
def strip(self): # Note that we have discussed that --hidden should only be passed to # mercurial commands when hidden revisions are shown. # However in the case of strip we can always pass it --hidden safely, # since strip will always strip all the descendants of a revision. # Thus in this case --hidden will just let us choose a hidden revision # as the base revision to strip. cmdline = [ 'strip', '--repository', self.repo.root, '--verbose', '--hidden' ] rev = hglib.fromunicode(self.rev_combo.currentText()) if not rev: return cmdline.append(rev) if self.discard_chk.isChecked(): cmdline.append('--force') else: try: node = self.repo[rev] except (error.LookupError, error.RepoLookupError, error.RepoError): return def isclean(): """return whether WD is changed""" wc = self.repo[None] return not (wc.modified() or wc.added() or wc.removed()) if not isclean(): main = _("Detected uncommitted local changes.") text = _("Do you want to discard them and continue?") labels = ((QMessageBox.Yes, _('&Yes (--force)')), (QMessageBox.No, _('&No'))) if qtlib.QuestionMsgBox(_('Confirm Strip'), main, text, labels=labels, parent=self): cmdline.append('--force') else: return # backup options if self.nobackup_chk.isChecked(): cmdline.append('--nobackup') # start the strip self.repo.incrementBusyCount() self.cmd.run(cmdline)
def qqueuePurge(self): uq = self.ql.item(self.ql.currentRow()).text() q = hglib.fromunicode(uq) if q == 'patches': return if qtlib.QuestionMsgBox( _('Confirm patch queue purge'), _("<p>This will also erase the patchfiles on disk!</p>" "<p>Do you really want to purge patch queue '%s' ?</p>") % uq, parent=self, defaultbutton=QMessageBox.No): opts = ['--purge', q] self.needsRefresh = True self.qqueueCommand(opts)
def validatePage(self): 'validate that we can continue with the merge' if self.field('discard').toBool(): labels = [(QMessageBox.Yes, _('&Discard')), (QMessageBox.No, _('Cancel'))] if not qtlib.QuestionMsgBox( _('Confirm Discard Changes'), _('The changes from revision %s and all unmerged parents ' 'will be discarded.\n\n' 'Are you sure this is what you want to do?') % (self.otherCsInfo.get_data('revid')), labels=labels, parent=self): return False return super(SummaryPage, self).validatePage()
def checkForRejects(self, ret): if ret is 0: self.refreshStatus() return rejre = re.compile('saving rejects to file (.*).rej') for m in rejre.finditer(self.cmd.core.rawoutput()): wfile = m.groups()[0] if not os.path.exists(self.repo.wjoin(wfile)): continue ufile = hglib.tounicode(wfile) if qtlib.QuestionMsgBox(_('Manually resolve rejected chunks?'), _('%s had rejected chunks, edit patched ' 'file together with rejects?') % ufile, parent=self): dlg = rejects.RejectsDialog(self.repo.wjoin(wfile), self) dlg.exec_() self.refreshStatus()
def onLinkActivated(self, cmd): cmd = hglib.fromunicode(cmd) repo = self.repo if cmd == 'commit': from tortoisehg.hgqt import commit dlg = commit.CommitDialog(repo, [], {}, self) dlg.finished.connect(dlg.deleteLater) dlg.exec_() self.refresh() elif cmd == 'shelve': from tortoisehg.hgqt import shelve dlg = shelve.ShelveDialog(repo, self.wizard()) dlg.finished.connect(dlg.deleteLater) dlg.exec_() self.refresh() elif cmd.startswith('discard'): if cmd != 'discard:noconfirm': labels = [(QMessageBox.Yes, _('&Discard')), (QMessageBox.No, _('Cancel'))] if not qtlib.QuestionMsgBox( _('Confirm Discard'), _('Discard outstanding changes to working directory?'), labels=labels, parent=self): return def finished(ret): repo.decrementBusyCount() self.refresh() cmdline = [ 'update', '--clean', '--repository', repo.root, '--rev', '.' ] self.runner = cmdui.Runner(True, self) self.runner.commandFinished.connect(finished) repo.incrementBusyCount() self.runner.run(cmdline) elif cmd == 'view': dlg = status.StatusDialog(repo, [], {}, self) dlg.exec_() self.refresh() elif cmd == 'skip': self.wizard().next() else: raise 'unknown command: %s' % cmd
def eng_toggled(checked): if self.isComplete(): oldmsg = self.msgEntry.text() if self.wizard().backoutmergeparentrev: msgset = i18n.keepgettext()._( 'Backed out merge changeset: ') else: msgset = i18n.keepgettext()._('Backed out changeset: ') msg = checked and msgset['id'] or msgset['str'] if oldmsg and oldmsg != msg: if not qtlib.QuestionMsgBox( _('Confirm Discard Message'), _('Discard current backout message?'), parent=self): self.engChk.blockSignals(True) self.engChk.setChecked(not checked) self.engChk.blockSignals(False) return self.msgEntry.setText(msg + str(self.repo[self.wizard().backoutrev])) self.msgEntry.moveCursorToEnd()
def removeSelected(self): 'remove selected repository' s = self.selitem item = s.internalPointer() if 'remove' not in item.menulist(): # check capability return if not item.okToDelete(): labels = [(QMessageBox.Yes, _('&Delete')), (QMessageBox.No, _('Cancel'))] if not qtlib.QuestionMsgBox( _('Confirm Delete'), _("Delete Group '%s' and all its entries?") % item.name, labels=labels, parent=self): return m = self.model() row = s.row() parent = s.parent() m.removeRows(row, 1, parent) self.selectionChanged(None, None) self.updateSettingsFile.emit()
def rename(self): """execute the rename""" # check inputs src = self.get_src() dest = self.get_dest() if not os.path.exists(src): qtlib.WarningMsgBox(self.msgTitle, _('Source does not exists.')) return fullsrc = os.path.abspath(src) if not fullsrc.startswith(self.repo.root): qtlib.ErrorMsgBox(self.errTitle, _('The source must be within the repository tree.')) return fulldest = os.path.abspath(dest) if not fulldest.startswith(self.repo.root): qtlib.ErrorMsgBox(self.errTitle, _('The destination must be within the repository tree.')) return if src == dest: qtlib.ErrorMsgBox(self.errTitle, _('Please give a destination that differs from the source')) return if (os.path.isfile(dest) and not self.isCaseFoldingOnWin()): res = qtlib.QuestionMsgBox(self.msgTitle, '<p>%s</p><p>%s</p>' % (_('Destination file already exists.'), _('Are you sure you want to overwrite it ?')), defaultbutton=QMessageBox.No) if not res: return if self.isCaseFoldingOnWin() and self.copy_chk.isChecked(): qtlib.ErrorMsgBox(self.errTitle, _('Cannot do a pure casefolding copy on Windows')) return cmdline = self.compose_command(src, dest) self.show_command(cmdline) self.cmd.run(cmdline)
def newHook(self): td = HookConfigDialog(self) res = td.exec_() if res: hooktype, command, hookname = td.value() # Does the new hook already exist? hooks = self.value() if hooktype in hooks: existingcommand = hooks[hooktype].get(hookname, None) if existingcommand is not None: if existingcommand == command: # The command already exists "as is"! return if not qtlib.QuestionMsgBox( _('Replace existing hook?'), _('There is an existing %s.%s hook.\n\n' 'Do you want to replace it?') % (hooktype, hookname), parent=self): return # Delete existing matching hooks in reverse order # (otherwise the row numbers will be wrong after the first # deletion) for r in reversed( self.findHooks(hooktype=hooktype, hookname=hookname)): self.deleteHook(r) self.hooktable.setSortingEnabled(False) row = self.hooktable.rowCount() self.hooktable.insertRow(row) for c, text in enumerate((hooktype, hookname, command)): self.hooktable.setItem(row, c, QTableWidgetItem(text)) # Make the hook column not editable (a dialog is used to edit it) itemhook = self.hooktable.item(row, 0) itemhook.setFlags(itemhook.flags() & ~Qt.ItemIsEditable) self.hooktable.setSortingEnabled(True) self.hooktable.resizeColumnsToContents() self.updatebuttons()
def update(self): self.saveSettings() cmdline = ['update', '--repository', self.repo.root] if self.verbose_chk.isChecked(): cmdline += ['--verbose'] cmdline += [ '--config', 'ui.merge=internal:' + (self.autoresolve_chk.isChecked() and 'merge' or 'fail') ] rev = hglib.fromunicode(self.rev_combo.currentText()) activatebookmarkmode = self.repo.ui.config('tortoisehg', 'activatebookmarks', 'prompt') if activatebookmarkmode != 'never': bookmarks = self.repo[rev].bookmarks() if bookmarks and rev not in bookmarks: # The revision that we are updating into has bookmarks, # but the user did not refer to the revision by one of them # (probably used a revision number or hash) # Ask the user if it wants to update to one of these bookmarks # instead selectedbookmark = None if len(bookmarks) == 1: if activatebookmarkmode == 'auto': activatebookmark = True else: activatebookmark = qtlib.QuestionMsgBox( _('Activate bookmark?'), _('The selected revision (%s) has a bookmark on it ' 'called "<i>%s</i>".<p>Do you want to activate it?' '<br></b><i>You can disable this prompt by configuring ' 'Settings/Workbench/Activate Bookmarks</i>') \ % (hglib.tounicode(rev), bookmarks[0])) if activatebookmark: selectedbookmark = bookmarks[0] else: # Even in auto mode, when there is more than one bookmark # we must ask the user which one must be activated selectedbookmark = qtlib.ChoicePrompt( _('Activate bookmark?'), _('The selected revision (<i>%s</i>) has <i>%d</i> ' 'bookmarks on it.<p>Select the bookmark that you want ' 'to activate and click <i>OK</i>.<p>Click <i>Cancel</i> ' 'if you don\'t want to activate any of them.<p>' '<p><i>You can disable this prompt by configuring ' 'Settings/Workbench/Activate Bookmarks</i><p>') \ % (hglib.tounicode(rev), len(bookmarks)), self, bookmarks, self.repo._bookmarkcurrent).run() if selectedbookmark: rev = selectedbookmark elif self.repo[rev] == self.repo[self.repo._bookmarkcurrent]: deactivatebookmark = qtlib.QuestionMsgBox( _('Deactivate current bookmark?'), _('Do you really want to deactivate the <i>%s</i> ' 'bookmark?') % self.repo._bookmarkcurrent) if deactivatebookmark: cmdline = ['bookmark', '--repository', self.repo.root] if self.verbose_chk.isChecked(): cmdline += ['--verbose'] cmdline += ['-i', self.repo._bookmarkcurrent] self.repo.incrementBusyCount() self.cmd.run(cmdline) return cmdline.append('--rev') cmdline.append(rev) if self.discard_chk.isChecked(): cmdline.append('--clean') else: cur = self.repo['.'] try: node = self.repo[rev] except (error.LookupError, error.RepoLookupError, error.RepoError): return def isclean(): '''whether WD is changed''' try: wc = self.repo[None] if wc.modified() or wc.added() or wc.removed(): return False for s in wc.substate: if wc.sub(s).dirty(): return False except EnvironmentError: return False return True def ismergedchange(): '''whether the local changes are merged (have 2 parents)''' wc = self.repo[None] return len(wc.parents()) == 2 def iscrossbranch(p1, p2): '''whether p1 -> p2 crosses branch''' pa = p1.ancestor(p2) return p1.branch() != p2.branch() or (p1 != pa and p2 != pa) def islocalmerge(p1, p2, clean=None): if clean is None: clean = isclean() pa = p1.ancestor(p2) return not clean and (p1 == pa or p2 == pa) def confirmupdate(clean=None): if clean is None: clean = isclean() msg = _('Detected uncommitted local changes in working tree.\n' 'Please select to continue:\n') data = { 'discard': (_('&Discard'), _('Discard - discard local changes, no backup')), 'shelve': (_('&Shelve'), _('Shelve - move local changes to a patch')), 'merge': (_('&Merge'), _('Merge - allow to merge with local changes')), } opts = ['discard'] if not ismergedchange(): opts.append('shelve') if islocalmerge(cur, node, clean): opts.append('merge') dlg = QMessageBox(QMessageBox.Question, _('Confirm Update'), '', QMessageBox.Cancel, self) buttonnames = {} for name in opts: label, desc = data[name] msg += '\n' msg += desc btn = dlg.addButton(label, QMessageBox.ActionRole) buttonnames[btn] = name dlg.setDefaultButton(QMessageBox.Cancel) dlg.setText(msg) dlg.exec_() clicked = buttonnames.get(dlg.clickedButton()) return clicked # If merge-by-default, we want to merge whenever possible, # without prompting user (similar to command-line behavior) defaultmerge = self.merge_chk.isChecked() clean = isclean() if clean: cmdline.append('--check') elif not (defaultmerge and islocalmerge(cur, node, clean)): clicked = confirmupdate(clean) if clicked == 'discard': cmdline.append('--clean') elif clicked == 'shelve': from tortoisehg.hgqt import shelve dlg = shelve.ShelveDialog(self._repoagent, self) dlg.finished.connect(dlg.deleteLater) dlg.exec_() return elif clicked == 'merge': pass # no args else: return # start updating self.repo.incrementBusyCount() self.cmd.run(cmdline)
class ChunksWidget(QWidget): linkActivated = pyqtSignal(QString) showMessage = pyqtSignal(QString) chunksSelected = pyqtSignal(bool) fileSelected = pyqtSignal(bool) fileModelEmpty = pyqtSignal(bool) fileModified = pyqtSignal() contextmenu = None def __init__(self, repo, parent, multiselectable): QWidget.__init__(self, parent) self.repo = repo self.multiselectable = multiselectable self.currentFile = None layout = QVBoxLayout(self) layout.setSpacing(0) layout.setMargin(0) layout.setContentsMargins(2, 2, 2, 2) self.setLayout(layout) self.splitter = QSplitter(self) self.splitter.setOrientation(Qt.Vertical) self.splitter.setChildrenCollapsible(False) self.layout().addWidget(self.splitter) self.filelist = filelistview.HgFileListView(repo, self, multiselectable) self.filelistmodel = filelistmodel.HgFileListModel(self) self.filelist.setModel(self.filelistmodel) self.filelist.setContextMenuPolicy(Qt.CustomContextMenu) self.filelist.customContextMenuRequested.connect(self.menuRequest) self.filelist.doubleClicked.connect(self.vdiff) self.fileListFrame = QFrame(self.splitter) self.fileListFrame.setFrameShape(QFrame.NoFrame) vbox = QVBoxLayout() vbox.setSpacing(0) vbox.setMargin(0) vbox.addWidget(self.filelist) self.fileListFrame.setLayout(vbox) self.diffbrowse = DiffBrowser(self.splitter) self.diffbrowse.setFont(qtlib.getfont('fontdiff').font()) self.diffbrowse.showMessage.connect(self.showMessage) self.diffbrowse.linkActivated.connect(self.linkActivated) self.diffbrowse.chunksSelected.connect(self.chunksSelected) self.filelist.fileSelected.connect(self.displayFile) self.filelist.clearDisplay.connect(self.diffbrowse.clearDisplay) self.splitter.setStretchFactor(0, 0) self.splitter.setStretchFactor(1, 3) self.timerevent = self.startTimer(500) self._actions = {} for name, desc, icon, key, tip, cb in [ ('diff', _('Visual Diff'), 'visualdiff', 'Ctrl+D', _('View file changes in external diff tool'), self.vdiff), ('edit', _('Edit Local'), 'edit-file', 'Shift+Ctrl+L', _('Edit current file in working copy'), self.editCurrentFile), ('revert', _('Revert to Revision'), 'hg-revert', 'Shift+Ctrl+R', _('Revert file(s) to contents at this revision'), self.revertfile), ]: act = QAction(desc, self) if icon: act.setIcon(qtlib.geticon(icon)) if key: act.setShortcut(key) if tip: act.setStatusTip(tip) if cb: act.triggered.connect(cb) self._actions[name] = act self.addAction(act) @pyqtSlot(QPoint) def menuRequest(self, point): actionlist = ['diff', 'edit', 'revert'] if not self.contextmenu: menu = QMenu(self) for act in actionlist: menu.addAction(self._actions[act]) self.contextmenu = menu self.contextmenu.exec_(self.filelist.viewport().mapToGlobal(point)) def vdiff(self): filenames = self.getSelectedFiles() if len(filenames) == 0: return opts = {'change': self.ctx.rev()} dlg = visdiff.visualdiff(self.repo.ui, self.repo, filenames, opts) if dlg: dlg.exec_() def revertfile(self): filenames = self.getSelectedFiles() if len(filenames) == 0: return rev = self.ctx.rev() if rev is None: rev = self.ctx.p1().rev() dlg = revert.RevertDialog(self.repo, filenames, rev, self) dlg.exec_() dlg.deleteLater() def timerEvent(self, event): 'Periodic poll of currently displayed patch or working file' if not hasattr(self, 'filelist'): return ctx = self.ctx if ctx is None: return if isinstance(ctx, patchctx): path = ctx._path mtime = ctx._mtime elif self.currentFile: path = self.repo.wjoin(self.currentFile) mtime = self.mtime else: return try: if os.path.exists(path): newmtime = os.path.getmtime(path) if mtime != newmtime: self.mtime = newmtime self.refresh() except EnvironmentError: pass def runPatcher(self, fp, wfile, updatestate): ui = self.repo.ui.copy() class warncapt(ui.__class__): def warn(self, msg, *args, **opts): self.write(msg) ui.__class__ = warncapt ok = True repo = self.repo ui.pushbuffer() try: eolmode = ui.config('patch', 'eol', 'strict') if eolmode.lower() not in patch.eolmodes: eolmode = 'strict' else: eolmode = eolmode.lower() # 'updatestate' flag has no effect since hg 1.9 try: ret = patch.internalpatch(ui, repo, fp, 1, files=None, eolmode=eolmode, similarity=0) except ValueError: ret = -1 if ret < 0: ok = False self.showMessage.emit(_('Patch failed to apply')) except (patch.PatchError, EnvironmentError), err: ok = False self.showMessage.emit(hglib.tounicode(str(err))) rejfilere = re.compile(r'\b%s\.rej\b' % re.escape(wfile)) for line in ui.popbuffer().splitlines(): if rejfilere.search(line): if qtlib.QuestionMsgBox(_('Manually resolve rejected chunks?'), hglib.tounicode(line) + u'<br><br>' + _('Edit patched file and rejects?'), parent=self): from tortoisehg.hgqt import rejects dlg = rejects.RejectsDialog(repo.wjoin(wfile), self) if dlg.exec_() == QDialog.Accepted: ok = True break return ok
def deleteSelectedChunks(self): 'delete currently selected chunks' repo = self.repo chunks = self.diffbrowse.curchunks dchunks = [c for c in chunks[1:] if c.selected] if not dchunks: self.showMessage.emit(_('No deletable chunks')) return ctx = self.ctx kchunks = [c for c in chunks[1:] if not c.selected] revertall = False if not kchunks: if isinstance(ctx, patchctx): revertmsg = _('Completely remove file from patch?') else: revertmsg = _('Revert all file changes?') revertall = qtlib.QuestionMsgBox(_('No chunks remain'), revertmsg) if isinstance(ctx, patchctx): repo.thgbackup(ctx._path) fp = util.atomictempfile(ctx._path, 'wb') buf = cStringIO.StringIO() try: if ctx._ph.comments: buf.write('\n'.join(ctx._ph.comments)) buf.write('\n\n') needsnewline = False for wfile in ctx._fileorder: if wfile == self.currentFile: if revertall: continue chunks[0].write(buf) for chunk in kchunks: chunk.write(buf) else: if buf.tell() and buf.getvalue()[-1] != '\n': buf.write('\n') for chunk in ctx._files[wfile]: chunk.write(buf) fp.write(buf.getvalue()) fp.close() finally: del fp ctx.invalidate() self.fileModified.emit() else: path = repo.wjoin(self.currentFile) if not os.path.exists(path): self.showMessage.emit(_('file has been deleted, refresh')) return if self.mtime != os.path.getmtime(path): self.showMessage.emit(_('file has been modified, refresh')) return repo.thgbackup(path) if revertall: commands.revert(repo.ui, repo, path, no_backup=True) else: wlock = repo.wlock() try: # atomictemp can preserve file permission wf = repo.wopener(self.currentFile, 'wb', atomictemp=True) wf.write(self.diffbrowse.origcontents) wf.close() fp = cStringIO.StringIO() chunks[0].write(fp) for c in kchunks: c.write(fp) fp.seek(0) self.runPatcher(fp, self.currentFile, False) finally: wlock.release() self.fileModified.emit()
if self.comboa.currentIndex() == 0: if not qtlib.QuestionMsgBox(_('Are you sure?'), _('Revert all working copy changes?')): return try: self.repo.ui.quiet = True commands.revert(self.repo.ui, self.repo, all=True) self.repo.ui.quiet = False except (EnvironmentError, error.Abort), e: self.showMessage(hglib.tounicode(str(e))) self.refreshCombos() return shelf = self.currentPatchA() ushelf = hglib.tounicode(os.path.basename(shelf)) if not qtlib.QuestionMsgBox( _('Are you sure?'), _('Clear contents of shelf file %s?') % ushelf): return try: f = open(shelf, "w") f.close() self.showMessage(_('Shelf cleared')) except EnvironmentError, e: self.showMessage(hglib.tounicode(str(e))) self.refreshCombos() @pyqtSlot() def deleteShelfB(self): shelf = self.currentPatchB() ushelf = hglib.tounicode(os.path.basename(shelf)) if not qtlib.QuestionMsgBox(_('Are you sure?'),