Sun, 28 Aug 2011 18:53:13 +0200
Started implementing an interface to the Mercurial command server (as of Mercurial >= 1.9).
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/VcsPlugins/vcsMercurial/HgClient.py Sun Aug 28 18:53:13 2011 +0200 @@ -0,0 +1,260 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2011 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing an interface to the Mercurial command server. +""" + +import struct +import io + +from PyQt4.QtCore import QProcess, QProcessEnvironment, QObject, QByteArray, \ + QCoreApplication + +import Preferences + + +class HgClient(QObject): + """ + Class implementing the Mercurial command server interface. + """ + InputFormat = ">I" + OutputFormat = ">cI" + OutputFormatSize = struct.calcsize(OutputFormat) + ReturnFormat = ">i" + + def __init__(self, repoPath, encoding, parent=None): + """ + Constructor + + @param repoPath root directory of the repository (string) + @param encoding encoding to be used by the command server (string) + @param parent reference to the parent object (QObject) + """ + super().__init__(parent) + + self.__server = QProcess() + self.__started = False + self.__version = None + self.__encoding = Preferences.getSystem("IOEncoding") + + # connect signals + self.__server.finished.connect(self.__serverFinished) + + # generate command line and environment + self.__serverArgs = [] + self.__serverArgs.append("serve") + self.__serverArgs.append("--cmdserver") + self.__serverArgs.append("pipe") + self.__serverArgs.append("--config") + self.__serverArgs.append("ui.interactive=True") + if repoPath: + self.__serverArgs.append("--repository") + self.__serverArgs.append(repoPath) + + if encoding: + env = QProcessEnvironment.systemEnvironment() + env.insert("HGENCODING", encoding) + self.__server.setProcessEnvironment(env) + self.__encoding = encoding + + def startServer(self): + """ + Public method to start the command server. + + @return tuple of flag indicating a successful start (boolean) and + an error message (string) in case of failure + """ + self.__server.start('hg', self.__serverArgs) + serverStarted = self.__server.waitForStarted() + if not serverStarted: + return False, self.trUtf8( + 'The process {0} could not be started. ' + 'Ensure, that it is in the search path.' + ).format('hg') + + self.__server.setReadChannel(QProcess.StandardOutput) + ok, error = self.__readHello() + self.__started = ok + return ok, error + + def stopServer(self): + """ + Public method to stop the command server. + """ + self.__server.closeWriteChannel() + self.__server.waitForFinished() + + def restartServer(self): + """ + Public method to restart the command server. + + @return tuple of flag indicating a successful start (boolean) and + an error message (string) in case of failure + """ + self.stopServer() + return self.startServer() + + def __readHello(self): + """ + Private method to read the hello message sent by the command server. + + @return tuple of flag indicating success (boolean) and an error message + in case of failure (string) + """ + ch, msg = self.__readChannel() + if not ch: + return False, self.trUtf8("Did not receive the 'hello' message.") + elif ch != "o": + return False, self.trUtf8("Received data on unexpected channel.") + + msg = msg.split("\n") + + if not msg[0].startswith("capabilities: "): + return False, self.trUtf8("Bad 'hello' message, expected 'capabilities: '" + " but got '{0}'.").format(msg[0]) + self.__capabilities = msg[0][len('capabilities: '):] + if not self.__capabilities: + return False, self.trUtf8("'capabilities' message did not contain" + " any capability.") + + self.__capabilities = set(self.__capabilities.split()) + if "runcommand" not in self.__capabilities: + return False, "'capabilities' did not contain 'runcommand'." + + if not msg[1].startswith("encoding: "): + return False, self.trUtf8("Bad 'hello' message, expected 'encoding: '" + " but got '{0}'.").format(msg[1]) + encoding = msg[1][len('encoding: '):] + if not encoding: + return False, self.trUtf8("'encoding' message did not contain" + " any encoding.") + self.__encoding = encoding + + return True, "" + + def __serverFinished(self, exitCode, exitStatus): + """ + Private slot connected to the finished signal. + + @param exitCode exit code of the process (integer) + @param exitStatus exit status of the process (QProcess.ExitStatus) + """ + self.__started = False + + def __readChannel(self): + """ + Private method to read data from the command server. + + @return tuple of channel designator and channel data (string, integer or string) + """ + if self.__server.bytesAvailable() > 0 or \ + self.__server.waitForReadyRead(10000): + data = bytes(self.__server.read(HgClient.OutputFormatSize)) + if not data: + return "", "" + + channel, length = struct.unpack(HgClient.OutputFormat, data) + channel = channel.decode(self.__encoding) + if channel in "IL": + return channel, length + else: + return (channel, + str(self.__server.read(length), self.__encoding, "replace")) + else: + return "", "" + + def __writeDataBlock(self, data): + """ + Private slot to write some data to the command server. + + @param data data to be sent (string) + """ + if not isinstance(data, bytes): + data = data.encode(self.__encoding) + self.__server.write(QByteArray(struct.pack(HgClient.InputFormat, len(data)))) + self.__server.write(QByteArray(data)) + self.__server.waitForBytesWritten() + + def __runcommand(self, args, inputChannels, outputChannels): + """ + Private method to run a command in the server (low level). + + @param args list of arguments for the command (list of string) + @param inputChannels dictionary of input channels. The dictionary must + have the keys 'I' and 'L' and each entry must be a function receiving + the number of bytes to write. + @param outputChannels dictionary of output channels. The dictionary must + have the keys 'o' and 'e' and each entry must be a function receiving + the data. + """ + if not self.__started: + return -1 + + self.__server.write(QByteArray(b'runcommand\n')) + self.__writeDataBlock('\0'.join(args)) + + while True: + QCoreApplication.processEvents() + if self.__server.bytesAvailable() == 0: + continue + channel, data = self.__readChannel() + + # input channels + if channel in inputChannels: + self.__writeDataBlock(inputChannels[channel](data)) + + # output channels + elif channel in outputChannels: + outputChannels[channel](data) + + # result channel, command is finished + elif channel == "r": + return struct.unpack(HgClient.ReturnFormat, + data.encode(self.__encoding))[0] + + # unexpected but required channel + elif channel.isupper(): + raise RuntimeError( + "Unexpected but required channel '{0}'.".format(channel)) + + # optional channels + else: + pass + + def runcommand(self, args, prompt=None, input=None): + """ + Public method to execute a command via the command server. + + @param args list of arguments for the command (list of string) + @param prompt function to reply to prompts by the server. It + receives the max number of bytes to return and the contents + of the output channel received so far. + @param input function to reply to bulk data requests by the server. + It receives the max number of bytes to return. + @return output and errors of the command server (string) + """ + output = io.StringIO() + error = io.StringIO() + outputChannels = { + "o": output.write, + "e": error.write + } + + inputChannels = {} + if prompt is not None: + def func(size): + reply = prompt(size, output.getvalue()) + return reply + inputChannels["L"] = func + if input is not None: + inputChannels["I"] = input + + self.__runcommand(args, inputChannels, outputChannels) + out = output.getvalue() + err = error.getvalue() + + return out, err +
--- a/Plugins/VcsPlugins/vcsMercurial/HgDialog.py Sat Aug 27 11:50:54 2011 +0200 +++ b/Plugins/VcsPlugins/vcsMercurial/HgDialog.py Sun Aug 28 18:53:13 2011 +0200 @@ -27,11 +27,12 @@ shows the output of the process. The dialog is modal, which causes a synchronized execution of the process. """ - def __init__(self, text, parent=None): + def __init__(self, text, hg=None, parent=None): """ Constructor @param text text to be shown by the label (string) + @param hg reference to the Mercurial interface object (Hg) @param parent parent widget (QWidget) """ super().__init__(parent) @@ -43,6 +44,7 @@ self.proc = None self.username = '' self.password = '' + self.__hgClient = hg.getClient() self.outputGroup.setTitle(text) @@ -114,33 +116,47 @@ else: self.__updateCommand = False - self.proc = QProcess() - if showArgs: self.resultbox.append(' '.join(args)) self.resultbox.append('') - self.proc.finished.connect(self.__procFinished) - self.proc.readyReadStandardOutput.connect(self.__readStdout) - self.proc.readyReadStandardError.connect(self.__readStderr) - - if workingDir: - self.proc.setWorkingDirectory(workingDir) - self.proc.start('hg', args) - procStarted = self.proc.waitForStarted() - if not procStarted: - self.buttonBox.setFocus() - self.inputGroup.setEnabled(False) - E5MessageBox.critical(self, - self.trUtf8('Process Generation Error'), - self.trUtf8( - 'The process {0} could not be started. ' - 'Ensure, that it is in the search path.' - ).format('hg')) + if self.__hgClient is None: + self.proc = QProcess() + + self.proc.finished.connect(self.__procFinished) + self.proc.readyReadStandardOutput.connect(self.__readStdout) + self.proc.readyReadStandardError.connect(self.__readStderr) + + if workingDir: + self.proc.setWorkingDirectory(workingDir) + self.proc.start('hg', args) + procStarted = self.proc.waitForStarted() + if not procStarted: + self.buttonBox.setFocus() + self.inputGroup.setEnabled(False) + E5MessageBox.critical(self, + self.trUtf8('Process Generation Error'), + self.trUtf8( + 'The process {0} could not be started. ' + 'Ensure, that it is in the search path.' + ).format('hg')) + else: + self.inputGroup.setEnabled(True) + self.inputGroup.show() + return procStarted else: - self.inputGroup.setEnabled(True) - self.inputGroup.show() - return procStarted + out, err = self.__hgClient.runcommand(args) + + if out: + self.__showOutput(out) + if err: + self.__showError(err) + + self.normal = True + + self.__finish() + + return True def normalExit(self): """ @@ -170,15 +186,23 @@ s = str(self.proc.readAllStandardOutput(), Preferences.getSystem("IOEncoding"), 'replace') - self.resultbox.insertPlainText(s) - self.resultbox.ensureCursorVisible() - - # check for a changed project file - if self.__updateCommand: - for line in s.splitlines(): - if '.e4p' in line: - self.__hasAddOrDelete = True - break + self.__showOutput(s) + + def __showOutput(self, out): + """ + Private slot to show some output. + + @param out output to be shown (string) + """ + self.resultbox.insertPlainText(out) + self.resultbox.ensureCursorVisible() + + # check for a changed project file + if self.__updateCommand: + for line in out.splitlines(): + if '.e4p' in line: + self.__hasAddOrDelete = True + break QCoreApplication.processEvents() @@ -190,12 +214,20 @@ error pane. """ if self.proc is not None: - self.errorGroup.show() s = str(self.proc.readAllStandardError(), Preferences.getSystem("IOEncoding"), 'replace') - self.errors.insertPlainText(s) - self.errors.ensureCursorVisible() + self.__showError(s) + + def __showError(self, out): + """ + Private slot to show some error. + + @param out error to be shown (string) + """ + self.errorGroup.show() + self.errors.insertPlainText(out) + self.errors.ensureCursorVisible() QCoreApplication.processEvents()
--- a/Plugins/VcsPlugins/vcsMercurial/hg.py Sat Aug 27 11:50:54 2011 +0200 +++ b/Plugins/VcsPlugins/vcsMercurial/hg.py Sun Aug 28 18:53:13 2011 +0200 @@ -45,6 +45,7 @@ from .HgBackoutDialog import HgBackoutDialog from .HgServeDialog import HgServeDialog from .HgUtilities import getConfigPath +from .HgClient import HgClient from .BookmarksExtension.bookmarks import Bookmarks from .QueuesExtension.queues import Queues @@ -142,6 +143,8 @@ self.__iniWatcher.fileChanged.connect(self.__iniFileChanged) self.__iniWatcher.addPath(getConfigPath()) + self.__client = None + # instantiate the extensions self.__extensions = { "bookmarks": Bookmarks(self), @@ -189,6 +192,17 @@ # shut down the extensions for extension in self.__extensions.values(): extension.shutdown() + + # shut down the client + self.__client and self.__client.stopServer() + + def getClient(self): + """ + Public method to get a reference to the command server interface. + + @return reference to the client (HgClient) + """ + return self.__client def vcsExists(self): """ @@ -272,6 +286,7 @@ args = [] args.append('init') args.append(projectDir) + # init is not possible with the command server dia = HgDialog(self.trUtf8('Creating Mercurial repository')) res = dia.startProcess(args) if res: @@ -287,7 +302,8 @@ args.append('--addremove') args.append('--message') args.append(msg) - dia = HgDialog(self.trUtf8('Initial commit to Mercurial repository')) + dia = HgDialog(self.trUtf8('Initial commit to Mercurial repository'), + self) res = dia.startProcess(args, projectDir) if res: dia.exec_() @@ -326,9 +342,14 @@ args.append(projectDir) if noDialog: - return self.startSynchronizedProcess(QProcess(), 'hg', args) + if self.__client is None: + return self.startSynchronizedProcess(QProcess(), 'hg', args) + else: + out, err = self.__client.runcommand(args) + return err == "" else: - dia = HgDialog(self.trUtf8('Cloning project from a Mercurial repository')) + dia = HgDialog(self.trUtf8('Cloning project from a Mercurial repository'), + self) res = dia.startProcess(args) if res: dia.exec_() @@ -424,7 +445,8 @@ if noDialog: self.startSynchronizedProcess(QProcess(), "hg", args, dname) else: - dia = HgDialog(self.trUtf8('Commiting changes to Mercurial repository')) + dia = HgDialog(self.trUtf8('Committing changes to Mercurial repository'), + self) res = dia.startProcess(args, dname) if res: dia.exec_() @@ -469,10 +491,14 @@ return False if noDialog: - self.startSynchronizedProcess(QProcess(), "hg", args, repodir) + if self.__client is None: + self.startSynchronizedProcess(QProcess(), 'hg', args, repodir) + else: + out, err = self.__client.runcommand(args) res = False else: - dia = HgDialog(self.trUtf8('Synchronizing with the Mercurial repository')) + dia = HgDialog(self.trUtf8('Synchronizing with the Mercurial repository'), + self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -518,10 +544,13 @@ args.append(name) if noDialog: - self.startSynchronizedProcess(QProcess(), "hg", args, repodir) + if self.__client is None: + self.startSynchronizedProcess(QProcess(), 'hg', args, repodir) + else: + out, err = self.__client.runcommand(args) else: dia = HgDialog( - self.trUtf8('Adding files/directories to the Mercurial repository')) + self.trUtf8('Adding files/directories to the Mercurial repository'), self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -578,10 +607,15 @@ return False if noDialog: - res = self.startSynchronizedProcess(QProcess(), "hg", args, repodir) + if self.__client is None: + res = self.startSynchronizedProcess(QProcess(), 'hg', args, repodir) + else: + out, err = self.__client.runcommand(args) + res = err == "" else: dia = HgDialog( - self.trUtf8('Removing files/directories from the Mercurial repository')) + self.trUtf8('Removing files/directories from the Mercurial repository'), + self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -637,8 +671,13 @@ if noDialog: res = self.startSynchronizedProcess(QProcess(), "hg", args, repodir) + if self.__client is None: + res = self.startSynchronizedProcess(QProcess(), 'hg', args, repodir) + else: + out, err = self.__client.runcommand(args) + res = err == "" else: - dia = HgDialog(self.trUtf8('Renaming {0}').format(name)) + dia = HgDialog(self.trUtf8('Renaming {0}').format(name), self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -743,7 +782,7 @@ args.append("Removed tag <{0}>.".format(tag)) args.append(tag) - dia = HgDialog(self.trUtf8('Taging in the Mercurial repository')) + dia = HgDialog(self.trUtf8('Taging in the Mercurial repository'), self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -773,7 +812,7 @@ if repodir == os.sep: return - dia = HgDialog(self.trUtf8('Reverting changes')) + dia = HgDialog(self.trUtf8('Reverting changes'), self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -821,7 +860,7 @@ args.append("--rev") args.append(rev) - dia = HgDialog(self.trUtf8('Merging').format(name)) + dia = HgDialog(self.trUtf8('Merging').format(name), self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -883,32 +922,40 @@ if repodir == os.sep: return 0 - ioEncoding = Preferences.getSystem("IOEncoding") - process = QProcess() args = [] args.append('status') args.append('--all') args.append('--noninteractive') - process.setWorkingDirectory(repodir) - process.start('hg', args) - procStarted = process.waitForStarted() - if procStarted: - finished = process.waitForFinished(30000) - if finished and process.exitCode() == 0: - output = \ - str(process.readAllStandardOutput(), ioEncoding, 'replace') - for line in output.splitlines(): - flag, path = line.split(" ", 1) - absname = os.path.join(repodir, os.path.normcase(path)) - if flag not in "?I": - if fname == '.': - if absname.startswith(dname + os.path.sep): - return self.canBeCommitted - if absname == dname: - return self.canBeCommitted - else: - if absname == name: - return self.canBeCommitted + + output = "" + if self.__client is None: + process = QProcess() + process.setWorkingDirectory(repodir) + process.start('hg', args) + procStarted = process.waitForStarted() + if procStarted: + finished = process.waitForFinished(30000) + if finished and process.exitCode() == 0: + output = \ + str(process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + 'replace') + else: + output, error = self.__client.runcommand(args) + + if output: + for line in output.splitlines(): + flag, path = line.split(" ", 1) + absname = os.path.join(repodir, os.path.normcase(path)) + if flag not in "?I": + if fname == '.': + if absname.startswith(dname + os.path.sep): + return self.canBeCommitted + if absname == dname: + return self.canBeCommitted + else: + if absname == name: + return self.canBeCommitted return self.canBeAdded @@ -944,44 +991,52 @@ if repodir == os.sep: return names - ioEncoding = Preferences.getSystem("IOEncoding") - process = QProcess() args = [] args.append('status') args.append('--all') args.append('--noninteractive') - process.setWorkingDirectory(dname) - process.start('hg', args) - procStarted = process.waitForStarted() - if procStarted: - finished = process.waitForFinished(30000) - if finished and process.exitCode() == 0: - dirs = [x for x in names.keys() if os.path.isdir(x)] - output = \ - str(process.readAllStandardOutput(), ioEncoding, 'replace') - for line in output.splitlines(): - flag, path = line.split(" ", 1) - name = os.path.normcase(os.path.join(repodir, path)) - dirName = os.path.dirname(name) - if name.startswith(dname): - if flag not in "?I": - if name in names: - names[name] = self.canBeCommitted - if dirName in names: - names[dirName] = self.canBeCommitted - if dirs: - for d in dirs: - if name.startswith(d): - names[d] = self.canBeCommitted - dirs.remove(d) - break + + output = "" + if self.__client is None: + process = QProcess() + process.setWorkingDirectory(dname) + process.start('hg', args) + procStarted = process.waitForStarted() + if procStarted: + finished = process.waitForFinished(30000) + if finished and process.exitCode() == 0: + output = \ + str(process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + 'replace') + else: + output, error = self.__client.runcommand(args) + + if output: + dirs = [x for x in names.keys() if os.path.isdir(x)] + for line in output.splitlines(): + flag, path = line.split(" ", 1) + name = os.path.normcase(os.path.join(repodir, path)) + dirName = os.path.dirname(name) + if name.startswith(dname): if flag not in "?I": - self.statusCache[name] = self.canBeCommitted - self.statusCache[dirName] = self.canBeCommitted - else: - self.statusCache[name] = self.canBeAdded - if dirName not in self.statusCache: - self.statusCache[dirName] = self.canBeAdded + if name in names: + names[name] = self.canBeCommitted + if dirName in names: + names[dirName] = self.canBeCommitted + if dirs: + for d in dirs: + if name.startswith(d): + names[d] = self.canBeCommitted + dirs.remove(d) + break + if flag not in "?I": + self.statusCache[name] = self.canBeCommitted + self.statusCache[dirName] = self.canBeCommitted + else: + self.statusCache[name] = self.canBeAdded + if dirName not in self.statusCache: + self.statusCache[dirName] = self.canBeAdded return names @@ -1044,7 +1099,7 @@ if repodir == os.sep: return - dia = HgDialog(self.trUtf8('Mercurial command')) + dia = HgDialog(self.trUtf8('Mercurial command'), self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -1077,65 +1132,78 @@ """ info = [] - process = QProcess() args = [] args.append('parents') args.append('--template') args.append('{rev}:{node|short}@@@{tags}@@@{author|xmlescape}@@@' '{date|isodate}@@@{branches}@@@{bookmarks}\n') - process.setWorkingDirectory(ppath) - process.start('hg', args) - procStarted = process.waitForStarted() - if procStarted: - finished = process.waitForFinished(30000) - if finished and process.exitCode() == 0: - output = str(process.readAllStandardOutput(), - Preferences.getSystem("IOEncoding"), 'replace') - index = 0 - for line in output.splitlines(): - index += 1 - changeset, tags, author, date, branches, bookmarks = line.split("@@@") - cdate, ctime = date.split()[:2] - info.append("""<p><table>""") + + output = "" + if self.__client is None: + process = QProcess() + process.setWorkingDirectory(ppath) + process.start('hg', args) + procStarted = process.waitForStarted() + if procStarted: + finished = process.waitForFinished(30000) + if finished and process.exitCode() == 0: + output = str(process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), 'replace') + else: + output, error = self.__client.runcommand(args) + + if output: + index = 0 + for line in output.splitlines(): + index += 1 + changeset, tags, author, date, branches, bookmarks = line.split("@@@") + cdate, ctime = date.split()[:2] + info.append("""<p><table>""") + info.append(QApplication.translate("mercurial", + """<tr><td><b>Parent #{0}</b></td><td></td></tr>\n""" + """<tr><td><b>Changeset</b></td><td>{1}</td></tr>""")\ + .format(index, changeset)) + if tags: info.append(QApplication.translate("mercurial", - """<tr><td><b>Parent #{0}</b></td><td></td></tr>\n""" - """<tr><td><b>Changeset</b></td><td>{1}</td></tr>""")\ - .format(index, changeset)) - if tags: - info.append(QApplication.translate("mercurial", - """<tr><td><b>Tags</b></td><td>{0}</td></tr>""")\ - .format('<br/>'.join(tags.split()))) - if bookmarks: - info.append(QApplication.translate("mercurial", - """<tr><td><b>Bookmarks</b></td><td>{0}</td></tr>""")\ - .format('<br/>'.join(bookmarks.split()))) - if branches: - info.append(QApplication.translate("mercurial", - """<tr><td><b>Branches</b></td><td>{0}</td></tr>""")\ - .format('<br/>'.join(branches.split()))) + """<tr><td><b>Tags</b></td><td>{0}</td></tr>""")\ + .format('<br/>'.join(tags.split()))) + if bookmarks: + info.append(QApplication.translate("mercurial", + """<tr><td><b>Bookmarks</b></td><td>{0}</td></tr>""")\ + .format('<br/>'.join(bookmarks.split()))) + if branches: info.append(QApplication.translate("mercurial", - """<tr><td><b>Last author</b></td><td>{0}</td></tr>\n""" - """<tr><td><b>Committed date</b></td><td>{1}</td></tr>\n""" - """<tr><td><b>Committed time</b></td><td>{2}</td></tr>""")\ - .format(author, cdate, ctime)) - info.append("""</table></p>""") + """<tr><td><b>Branches</b></td><td>{0}</td></tr>""")\ + .format('<br/>'.join(branches.split()))) + info.append(QApplication.translate("mercurial", + """<tr><td><b>Last author</b></td><td>{0}</td></tr>\n""" + """<tr><td><b>Committed date</b></td><td>{1}</td></tr>\n""" + """<tr><td><b>Committed time</b></td><td>{2}</td></tr>""")\ + .format(author, cdate, ctime)) + info.append("""</table></p>""") url = "" args = [] args.append('showconfig') args.append('paths.default') - process.setWorkingDirectory(ppath) - process.start('hg', args) - procStarted = process.waitForStarted() - if procStarted: - finished = process.waitForFinished(30000) - if finished and process.exitCode() == 0: - output = str(process.readAllStandardOutput(), - Preferences.getSystem("IOEncoding"), 'replace') - if output: - url = output.splitlines()[0].strip() - else: - url = "" + + output = "" + if self.__client is None: + process.setWorkingDirectory(ppath) + process.start('hg', args) + procStarted = process.waitForStarted() + if procStarted: + finished = process.waitForFinished(30000) + if finished and process.exitCode() == 0: + output = str(process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), 'replace') + else: + output, error = self.__client.runcommand(args) + + if output: + url = output.splitlines()[0].strip() + else: + url = "" return QApplication.translate('mercurial', """<h3>Repository information</h3>\n""" @@ -1222,7 +1290,7 @@ return False dia = HgDialog(self.trUtf8('Copying {0}') - .format(name)) + .format(name), self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -1242,30 +1310,38 @@ @param repodir directory name of the repository (string) @return list of tags (list of string) """ - ioEncoding = Preferences.getSystem("IOEncoding") - process = QProcess() args = [] args.append('tags') args.append('--verbose') - process.setWorkingDirectory(repodir) - process.start('hg', args) - procStarted = process.waitForStarted() - if procStarted: - finished = process.waitForFinished(30000) - if finished and process.exitCode() == 0: - self.tagsList = [] - output = \ - str(process.readAllStandardOutput(), ioEncoding, 'replace') - for line in output.splitlines(): - l = line.strip().split() - if l[-1][0] in "1234567890": - # last element is a rev:changeset - del l[-1] - else: - del l[-2:] - name = " ".join(l) - if name not in ["tip", "default"]: - self.tagsList.append(name) + + output = "" + if self.__client is None: + process = QProcess() + process.setWorkingDirectory(repodir) + process.start('hg', args) + procStarted = process.waitForStarted() + if procStarted: + finished = process.waitForFinished(30000) + if finished and process.exitCode() == 0: + output = \ + str(process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + 'replace') + else: + output, error = self.__client.runcommand(args) + + if output: + self.tagsList = [] + for line in output.splitlines(): + l = line.strip().split() + if l[-1][0] in "1234567890": + # last element is a rev:changeset + del l[-1] + else: + del l[-2:] + name = " ".join(l) + if name not in ["tip", "default"]: + self.tagsList.append(name) return self.tagsList[:] @@ -1276,30 +1352,38 @@ @param repodir directory name of the repository (string) @return list of branches (list of string) """ - ioEncoding = Preferences.getSystem("IOEncoding") - process = QProcess() args = [] args.append('branches') args.append('--closed') - process.setWorkingDirectory(repodir) - process.start('hg', args) - procStarted = process.waitForStarted() - if procStarted: - finished = process.waitForFinished(30000) - if finished and process.exitCode() == 0: - self.branchesList = [] - output = \ - str(process.readAllStandardOutput(), ioEncoding, 'replace') - for line in output.splitlines(): - l = line.strip().split() - if l[-1][0] in "1234567890": - # last element is a rev:changeset - del l[-1] - else: - del l[-2:] - name = " ".join(l) - if name not in ["tip", "default"]: - self.branchesList.append(name) + + output = "" + if self.__client is None: + process = QProcess() + process.setWorkingDirectory(repodir) + process.start('hg', args) + procStarted = process.waitForStarted() + if procStarted: + finished = process.waitForFinished(30000) + if finished and process.exitCode() == 0: + output = \ + str(process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), + 'replace') + else: + output, error = self.__client.runcommand(args) + + if output: + self.branchesList = [] + for line in output.splitlines(): + l = line.strip().split() + if l[-1][0] in "1234567890": + # last element is a rev:changeset + del l[-1] + else: + del l[-2:] + name = " ".join(l) + if name not in ["tip", "default"]: + self.branchesList.append(name) return self.branchesList[:] @@ -1474,7 +1558,7 @@ if repodir == os.sep: return - dia = HgDialog(self.trUtf8('Pulling from a remote Mercurial repository')) + dia = HgDialog(self.trUtf8('Pulling from a remote Mercurial repository'), self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -1506,7 +1590,7 @@ if repodir == os.sep: return - dia = HgDialog(self.trUtf8('Pushing to a remote Mercurial repository')) + dia = HgDialog(self.trUtf8('Pushing to a remote Mercurial repository'), self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -1525,75 +1609,80 @@ info = [] - # find the root of the repo - repodir = self.splitPath(ppath)[0] - while not os.path.isdir(os.path.join(repodir, self.adminDir)): - repodir = os.path.dirname(repodir) - if repodir == os.sep: - return - - process = QProcess() args = [] args.append(mode) args.append('--template') args.append('{rev}:{node|short}@@@{tags}@@@{author|xmlescape}@@@' '{date|isodate}@@@{branches}@@@{parents}@@@{bookmarks}\n') - process.setWorkingDirectory(repodir) - process.start('hg', args) - procStarted = process.waitForStarted() - if procStarted: - finished = process.waitForFinished(30000) - if finished and process.exitCode() == 0: - output = str(process.readAllStandardOutput(), - Preferences.getSystem("IOEncoding"), 'replace') - index = 0 - for line in output.splitlines(): - index += 1 - changeset, tags, author, date, branches, parents, bookmarks = \ - line.split("@@@") - cdate, ctime = date.split()[:2] - info.append("""<p><table>""") - if mode == "heads": - info.append(QApplication.translate("mercurial", - """<tr><td><b>Head #{0}</b></td><td></td></tr>\n""" - .format(index, changeset))) - elif mode == "parents": - info.append(QApplication.translate("mercurial", - """<tr><td><b>Parent #{0}</b></td><td></td></tr>\n""" - .format(index, changeset))) - elif mode == "tip": - info.append(QApplication.translate("mercurial", - """<tr><td><b>Tip</b></td><td></td></tr>\n""")) + output = "" + if self.__client is None: + # find the root of the repo + repodir = self.splitPath(ppath)[0] + while not os.path.isdir(os.path.join(repodir, self.adminDir)): + repodir = os.path.dirname(repodir) + if repodir == os.sep: + return + + process = QProcess() + process.setWorkingDirectory(repodir) + process.start('hg', args) + procStarted = process.waitForStarted() + if procStarted: + finished = process.waitForFinished(30000) + if finished and process.exitCode() == 0: + output = str(process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), 'replace') + else: + output, error = self.__client.runcommand(args) + + if output: + index = 0 + for line in output.splitlines(): + index += 1 + changeset, tags, author, date, branches, parents, bookmarks = \ + line.split("@@@") + cdate, ctime = date.split()[:2] + info.append("""<p><table>""") + if mode == "heads": info.append(QApplication.translate("mercurial", - """<tr><td><b>Changeset</b></td><td>{0}</td></tr>""")\ - .format(changeset)) - if tags: - info.append(QApplication.translate("mercurial", - """<tr><td><b>Tags</b></td><td>{0}</td></tr>""")\ - .format('<br/>'.join(tags.split()))) - if bookmarks: - info.append(QApplication.translate("mercurial", - """<tr><td><b>Bookmarks</b></td><td>{0}</td></tr>""")\ - .format('<br/>'.join(bookmarks.split()))) - if branches: - info.append(QApplication.translate("mercurial", - """<tr><td><b>Branches</b></td><td>{0}</td></tr>""")\ - .format('<br/>'.join(branches.split()))) - if parents: - info.append(QApplication.translate("mercurial", - """<tr><td><b>Parents</b></td><td>{0}</td></tr>""")\ - .format('<br/>'.join(parents.split()))) + """<tr><td><b>Head #{0}</b></td><td></td></tr>\n""" + .format(index, changeset))) + elif mode == "parents": + info.append(QApplication.translate("mercurial", + """<tr><td><b>Parent #{0}</b></td><td></td></tr>\n""" + .format(index, changeset))) + elif mode == "tip": + info.append(QApplication.translate("mercurial", + """<tr><td><b>Tip</b></td><td></td></tr>\n""")) + info.append(QApplication.translate("mercurial", + """<tr><td><b>Changeset</b></td><td>{0}</td></tr>""")\ + .format(changeset)) + if tags: + info.append(QApplication.translate("mercurial", + """<tr><td><b>Tags</b></td><td>{0}</td></tr>""")\ + .format('<br/>'.join(tags.split()))) + if bookmarks: info.append(QApplication.translate("mercurial", - """<tr><td><b>Last author</b></td><td>{0}</td></tr>\n""" - """<tr><td><b>Committed date</b></td><td>{1}</td></tr>\n""" - """<tr><td><b>Committed time</b></td><td>{2}</td></tr>\n""" - """</table></p>""")\ - .format(author, cdate, ctime)) - - dlg = VcsRepositoryInfoDialog(None, "\n".join(info)) - dlg.exec_() - + """<tr><td><b>Bookmarks</b></td><td>{0}</td></tr>""")\ + .format('<br/>'.join(bookmarks.split()))) + if branches: + info.append(QApplication.translate("mercurial", + """<tr><td><b>Branches</b></td><td>{0}</td></tr>""")\ + .format('<br/>'.join(branches.split()))) + if parents: + info.append(QApplication.translate("mercurial", + """<tr><td><b>Parents</b></td><td>{0}</td></tr>""")\ + .format('<br/>'.join(parents.split()))) + info.append(QApplication.translate("mercurial", + """<tr><td><b>Last author</b></td><td>{0}</td></tr>\n""" + """<tr><td><b>Committed date</b></td><td>{1}</td></tr>\n""" + """<tr><td><b>Committed time</b></td><td>{2}</td></tr>\n""" + """</table></p>""")\ + .format(author, cdate, ctime)) + + dlg = VcsRepositoryInfoDialog(None, "\n".join(info)) + dlg.exec_() def hgResolve(self, name): """ @@ -1620,8 +1709,7 @@ if repodir == os.sep: return False - dia = HgDialog( - self.trUtf8('Resolving files/directories')) + dia = HgDialog(self.trUtf8('Resolving files/directories'), self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -1653,7 +1741,8 @@ args.append('branch') args.append(name.strip().replace(" ", "_")) - dia = HgDialog(self.trUtf8('Creating branch in the Mercurial repository')) + dia = HgDialog(self.trUtf8('Creating branch in the Mercurial repository'), + self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -1676,7 +1765,7 @@ args = [] args.append("branch") - dia = HgDialog(self.trUtf8('Showing current branch')) + dia = HgDialog(self.trUtf8('Showing current branch'), self) res = dia.startProcess(args, repodir, False) if res: dia.exec_() @@ -1733,7 +1822,8 @@ args = [] args.append('verify') - dia = HgDialog(self.trUtf8('Verifying the integrity of the Mercurial repository')) + dia = HgDialog(self.trUtf8('Verifying the integrity of the Mercurial repository'), + self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -1757,7 +1847,7 @@ args.append('showconfig') args.append("--untrusted") - dia = HgDialog(self.trUtf8('Showing the combined configuration settings')) + dia = HgDialog(self.trUtf8('Showing the combined configuration settings'), self) res = dia.startProcess(args, repodir, False) if res: dia.exec_() @@ -1780,7 +1870,7 @@ args = [] args.append('paths') - dia = HgDialog(self.trUtf8('Showing aliases for remote repositories')) + dia = HgDialog(self.trUtf8('Showing aliases for remote repositories'), self) res = dia.startProcess(args, repodir, False) if res: dia.exec_() @@ -1803,7 +1893,7 @@ args = [] args.append('recover') - dia = HgDialog(self.trUtf8('Recovering from interrupted transaction')) + dia = HgDialog(self.trUtf8('Recovering from interrupted transaction'), self) res = dia.startProcess(args, repodir, False) if res: dia.exec_() @@ -1826,7 +1916,7 @@ args = [] args.append('identify') - dia = HgDialog(self.trUtf8('Identifying project directory')) + dia = HgDialog(self.trUtf8('Identifying project directory'), self) res = dia.startProcess(args, repodir, False) if res: dia.exec_() @@ -1947,7 +2037,7 @@ args.append(compression) args.append(fname) - dia = HgDialog(self.trUtf8('Create changegroup')) + dia = HgDialog(self.trUtf8('Create changegroup'), self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -2009,7 +2099,7 @@ args.append('identify') args.append(file) - dia = HgDialog(self.trUtf8('Identifying changegroup file')) + dia = HgDialog(self.trUtf8('Identifying changegroup file'), self) res = dia.startProcess(args, repodir, False) if res: dia.exec_() @@ -2046,7 +2136,7 @@ args.append("--update") args.extend(files) - dia = HgDialog(self.trUtf8('Apply changegroups')) + dia = HgDialog(self.trUtf8('Apply changegroups'), self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -2094,7 +2184,7 @@ if rev: args.append(rev) - dia = HgDialog(self.trUtf8('Mercurial Bisect ({0})').format(subcommand)) + dia = HgDialog(self.trUtf8('Mercurial Bisect ({0})').format(subcommand), self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -2127,7 +2217,7 @@ return dia = HgDialog( - self.trUtf8('Removing files from the Mercurial repository only')) + self.trUtf8('Removing files from the Mercurial repository only'), self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -2182,7 +2272,7 @@ args.append(message) args.append(rev) - dia = HgDialog(self.trUtf8('Backing out changeset')) + dia = HgDialog(self.trUtf8('Backing out changeset'), self) res = dia.startProcess(args, repodir) if res: dia.exec_() @@ -2207,7 +2297,7 @@ self.trUtf8("""Are you sure you want to rollback the last transaction?"""), icon=E5MessageBox.Warning) if res: - dia = HgDialog(self.trUtf8('Rollback last transaction')) + dia = HgDialog(self.trUtf8('Rollback last transaction'), self) res = dia.startProcess(["rollback"], repodir) if res: dia.exec_() @@ -2241,6 +2331,15 @@ @param path name of the changed file (string) """ self.__getExtensionsInfo() + + if self.__client: + ok, err = self.__client.restartServer() + if not ok: + E5MessageBox.warning(None, + self.trUtf8("Mercurial Command Server"), + self.trUtf8("""<p>The Mercurial Command Server could not be""" + """ restarted.</p><p>Reason: {0}</p>""").format(err)) + self.__client = None def __monitorRepoIniFile(self, name): """ @@ -2269,20 +2368,26 @@ activeExtensions = sorted(self.__activeExtensions) self.__activeExtensions = [] - process = QProcess() args = [] args.append('showconfig') args.append('extensions') - process.start('hg', args) - procStarted = process.waitForStarted() - if procStarted: - finished = process.waitForFinished(30000) - if finished and process.exitCode() == 0: - output = str(process.readAllStandardOutput(), - Preferences.getSystem("IOEncoding"), 'replace') - for line in output.splitlines(): - extensionName = line.split("=", 1)[0].strip().split(".")[-1].strip() - self.__activeExtensions.append(extensionName) + + if self.__client is None: + process = QProcess() + process.start('hg', args) + procStarted = process.waitForStarted() + if procStarted: + finished = process.waitForFinished(30000) + if finished and process.exitCode() == 0: + output = str(process.readAllStandardOutput(), + Preferences.getSystem("IOEncoding"), 'replace') + else: + output, error = self.__client.runcommand(args) + + if output: + for line in output.splitlines(): + extensionName = line.split("=", 1)[0].strip().split(".")[-1].strip() + self.__activeExtensions.append(extensionName) if self.versionStr >= "1.8": if "bookmarks" not in self.__activeExtensions: @@ -2334,7 +2439,19 @@ """ self.__projectHelper = self.__plugin.getProjectHelper() self.__projectHelper.setObjects(self, project) - self.__monitorRepoIniFile(project.ppath) + self.__monitorRepoIniFile(project.getProjectPath()) + + if self.versionStr >= "1.9": + client = HgClient(project.getProjectPath(), "utf-8", self) + ok, err = client.startServer() + if ok: + self.__client = client + else: + E5MessageBox.warning(None, + self.trUtf8("Mercurial Command Server"), + self.trUtf8("""<p>The Mercurial Command Server could not be""" + """ started.</p><p>Reason: {0}</p>""").format(err)) + return self.__projectHelper ############################################################################
--- a/eric5.e4p Sat Aug 27 11:50:54 2011 +0200 +++ b/eric5.e4p Sun Aug 28 18:53:13 2011 +0200 @@ -927,6 +927,7 @@ <Source>Utilities/crypto/py3PBKDF2.py</Source> <Source>Preferences/ConfigurationPages/SecurityPage.py</Source> <Source>UtilitiesPython2/PySideImporter.py</Source> + <Source>Plugins/VcsPlugins/vcsMercurial/HgClient.py</Source> </Sources> <Forms> <Form>PyUnit/UnittestDialog.ui</Form>