From d570b05beccec87187aa357ad96525e24c655fb9 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint <Friedemann.Kleint@nokia.com> Date: Thu, 5 Nov 2009 12:45:02 +0100 Subject: [PATCH] Mercurial: Work on Windows using Tortoise Hg, polishing. Do not use "cmd /c hg" on Windows as this will fail randomly depending on arguments with blanks, such as "-U 8". Output log messages about synchronous commands, format author correctly, label menus correctly. --- src/libs/utils/winutils.cpp | 48 ++++++++++++++++++- src/libs/utils/winutils.h | 5 ++ src/plugins/mercurial/mercurialclient.cpp | 48 ++++++++++--------- src/plugins/mercurial/mercurialclient.h | 4 +- .../mercurial/mercurialcommitwidget.cpp | 13 ++++- src/plugins/mercurial/mercurialjobrunner.cpp | 27 ++++++++--- src/plugins/mercurial/mercurialjobrunner.h | 7 ++- src/plugins/mercurial/mercurialplugin.cpp | 21 ++++---- src/plugins/mercurial/mercurialplugin.h | 1 + src/plugins/mercurial/mercurialsettings.cpp | 7 --- 10 files changed, 130 insertions(+), 51 deletions(-) diff --git a/src/libs/utils/winutils.cpp b/src/libs/utils/winutils.cpp index 4e7d31c3fcf..ecb5b389f57 100644 --- a/src/libs/utils/winutils.cpp +++ b/src/libs/utils/winutils.cpp @@ -55,6 +55,17 @@ QTCREATOR_UTILS_EXPORT QString winErrorMessage(unsigned long error) return rc; } + +static inline QString msgCannotLoad(const char *lib, const QString &why) +{ + return QString::fromLatin1("Unable load %1: %2").arg(QLatin1String(lib), why); +} + +static inline QString msgCannotResolve(const char *lib) +{ + return QString::fromLatin1("Unable to resolve all required symbols in %1").arg(QLatin1String(lib)); +} + QTCREATOR_UTILS_EXPORT QString winGetDLLVersion(WinDLLVersionType t, const QString &name, QString *errorMessage) @@ -67,7 +78,7 @@ QTCREATOR_UTILS_EXPORT QString winGetDLLVersion(WinDLLVersionType t, const char *versionDLLC = "version.dll"; QLibrary versionLib(QLatin1String(versionDLLC), 0); if (!versionLib.load()) { - *errorMessage = QString::fromLatin1("Unable load %1: %2").arg(QLatin1String(versionDLLC), versionLib.errorString()); + *errorMessage = msgCannotLoad(versionDLLC, versionLib.errorString()); return QString(); } // MinGW requires old-style casts @@ -75,7 +86,7 @@ QTCREATOR_UTILS_EXPORT QString winGetDLLVersion(WinDLLVersionType t, GetFileVersionInfoWProtoType getFileVersionInfoW = (GetFileVersionInfoWProtoType)(versionLib.resolve("GetFileVersionInfoW")); VerQueryValueWProtoType verQueryValueW = (VerQueryValueWProtoType)(versionLib.resolve("VerQueryValueW")); if (!getFileVersionInfoSizeW || !getFileVersionInfoW || !verQueryValueW) { - *errorMessage = QString::fromLatin1("Unable to resolve all required symbols in %1").arg(QLatin1String(versionDLLC)); + *errorMessage = msgCannotResolve(versionDLLC); return QString(); } @@ -111,4 +122,37 @@ QTCREATOR_UTILS_EXPORT QString winGetDLLVersion(WinDLLVersionType t, return rc; } +QTCREATOR_UTILS_EXPORT QString getShortPathName(const QString &name, QString *errorMessage) +{ + typedef DWORD (APIENTRY *GetShortPathNameProtoType)(LPCTSTR, LPTSTR, DWORD); + + if (name.isEmpty()) + return name; + + const char *kernel32DLLC = "kernel32.dll"; + + QLibrary kernel32Lib(kernel32DLLC, 0); + if (!kernel32Lib.isLoaded() && !kernel32Lib.load()) { + *errorMessage = msgCannotLoad(kernel32DLLC, kernel32Lib.errorString()); + return QString(); + } + + // MinGW requires old-style casts + GetShortPathNameProtoType getShortPathNameW = (GetShortPathNameProtoType)(kernel32Lib.resolve("GetShortPathNameW")); + if (!getShortPathNameW) { + *errorMessage = msgCannotResolve(kernel32DLLC); + return QString(); + } + // Determine length, then convert. + const LPCTSTR nameC = reinterpret_cast<LPCTSTR>(name.utf16()); // MinGW + const DWORD length = (*getShortPathNameW)(nameC, NULL, 0); + if (length == 0) + return name; + TCHAR *buffer = new TCHAR[length]; + (*getShortPathNameW)(nameC, buffer, length); + const QString rc = QString::fromUtf16(reinterpret_cast<const ushort *>(buffer), length); + delete [] buffer; + return rc; +} + } // namespace Utils diff --git a/src/libs/utils/winutils.h b/src/libs/utils/winutils.h index 8a73961ce3e..12dea87fc87 100644 --- a/src/libs/utils/winutils.h +++ b/src/libs/utils/winutils.h @@ -47,5 +47,10 @@ enum WinDLLVersionType { WinDLLFileVersion, WinDLLProductVersion }; QTCREATOR_UTILS_EXPORT QString winGetDLLVersion(WinDLLVersionType t, const QString &name, QString *errorMessage); + +// Return the short (8.3) file name +QTCREATOR_UTILS_EXPORT QString getShortPathName(const QString &name, + QString *errorMessage); + } // namespace Utils #endif // WINUTILS_H diff --git a/src/plugins/mercurial/mercurialclient.cpp b/src/plugins/mercurial/mercurialclient.cpp index e344eb61f8d..bf7d7b67d96 100644 --- a/src/plugins/mercurial/mercurialclient.cpp +++ b/src/plugins/mercurial/mercurialclient.cpp @@ -38,6 +38,7 @@ #include <utils/qtcassert.h> #include <vcsbase/vcsbaseeditor.h> +#include <vcsbase/vcsbaseoutputwindow.h> #include <QtCore/QStringList> #include <QtCore/QSharedPointer> @@ -80,7 +81,7 @@ bool MercurialClient::add(const QString &filename) QStringList args; args << QLatin1String("add") << file.absoluteFilePath(); - return hgProcessSync(file, args); + return executeHgSynchronously(file, args); } bool MercurialClient::remove(const QString &filename) @@ -89,7 +90,7 @@ bool MercurialClient::remove(const QString &filename) QStringList args; args << QLatin1String("remove") << file.absoluteFilePath(); - return hgProcessSync(file, args); + return executeHgSynchronously(file, args); } bool MercurialClient::manifestSync(const QString &filename) @@ -98,7 +99,7 @@ bool MercurialClient::manifestSync(const QString &filename) QStringList args(QLatin1String("manifest")); QByteArray output; - hgProcessSync(file, args, &output); + executeHgSynchronously(file, args, &output); const QStringList files = QString::fromLocal8Bit(output).split(QLatin1Char('\n')); @@ -111,25 +112,31 @@ bool MercurialClient::manifestSync(const QString &filename) return false; } -bool MercurialClient::hgProcessSync(const QFileInfo &file, const QStringList &args, - QByteArray *output) const +bool MercurialClient::executeHgSynchronously(const QFileInfo &file, const QStringList &args, + QByteArray *output) const { QProcess hgProcess; hgProcess.setWorkingDirectory(file.isDir() ? file.absoluteFilePath() : file.absolutePath()); - MercurialSettings *settings = MercurialPlugin::instance()->settings(); - QStringList arguments = settings->standardArguments(); - arguments << args; + const MercurialSettings *settings = MercurialPlugin::instance()->settings(); + const QString binary = settings->binary(); + const QStringList arguments = MercurialPlugin::instance()->standardArguments() + args; - hgProcess.start(settings->binary(), arguments); + VCSBase::VCSBaseOutputWindow *outputWindow = VCSBase::VCSBaseOutputWindow::instance(); + outputWindow->appendCommand(MercurialJobRunner::msgExecute(binary, arguments)); - if (!hgProcess.waitForStarted()) + hgProcess.start(binary, arguments); + + if (!hgProcess.waitForStarted()) { + outputWindow->appendError(MercurialJobRunner::msgStartFailed(binary, hgProcess.errorString())); return false; + } hgProcess.closeWriteChannel(); if (!hgProcess.waitForFinished(settings->timeout())) { hgProcess.terminate(); + outputWindow->appendError(MercurialJobRunner::msgTimeout(settings->timeout())); return false; } @@ -145,7 +152,7 @@ bool MercurialClient::hgProcessSync(const QFileInfo &file, const QStringList &ar QString MercurialClient::branchQuerySync(const QFileInfo &repositoryRoot) { QByteArray output; - if (hgProcessSync(repositoryRoot, QStringList(QLatin1String("branch")), &output)) + if (executeHgSynchronously(repositoryRoot, QStringList(QLatin1String("branch")), &output)) return QTextCodec::codecForLocale()->toUnicode(output).trimmed(); return QLatin1String("Unknown Branch"); @@ -237,8 +244,7 @@ void MercurialClient::revert(const QFileInfo &fileOrDir, const QString &revision void MercurialClient::status(const QFileInfo &fileOrDir) { - QStringList args; - args << QLatin1String("status"); + QStringList args(QLatin1String("status")); if (!fileOrDir.isDir()) args.append(fileOrDir.absoluteFilePath()); @@ -301,8 +307,7 @@ void MercurialClient::import(const QFileInfo &repositoryRoot, const QStringList void MercurialClient::pull(const QFileInfo &repositoryRoot, const QString &repository) { - QStringList args; - args << QLatin1String("pull"); + QStringList args(QLatin1String("pull")); if (!repository.isEmpty()) args.append(repository); @@ -312,8 +317,7 @@ void MercurialClient::pull(const QFileInfo &repositoryRoot, const QString &repos void MercurialClient::push(const QFileInfo &repositoryRoot, const QString &repository) { - QStringList args; - args << QLatin1String("push"); + QStringList args(QLatin1String("push")); if (!repository.isEmpty()) args.append(repository); @@ -384,12 +388,12 @@ void MercurialClient::update(const QFileInfo &repositoryRoot, const QString &rev } void MercurialClient::commit(const QFileInfo &repositoryRoot, const QStringList &files, - const QString &commiterInfo, const QString &commitMessageFile) + const QString &committerInfo, const QString &commitMessageFile) { - QStringList args; - - args << QLatin1String("commit") << QLatin1String("-u") << commiterInfo - << QLatin1String("-l") << commitMessageFile << files; + QStringList args(QLatin1String("commit")); + if (!committerInfo.isEmpty()) + args << QLatin1String("-u") << committerInfo; + args << QLatin1String("-l") << commitMessageFile << files; QSharedPointer<HgTask> job(new HgTask(repositoryRoot.absoluteFilePath(), args, false)); jobManager->enqueueJob(job); } diff --git a/src/plugins/mercurial/mercurialclient.h b/src/plugins/mercurial/mercurialclient.h index c7adb10f48c..a6894e5ba1b 100644 --- a/src/plugins/mercurial/mercurialclient.h +++ b/src/plugins/mercurial/mercurialclient.h @@ -88,8 +88,8 @@ private slots: void statusParser(const QByteArray &data); private: - bool hgProcessSync(const QFileInfo &file, const QStringList &args, - QByteArray *output=0) const; + bool executeHgSynchronously(const QFileInfo &file, const QStringList &args, + QByteArray *output=0) const; MercurialJobRunner *jobManager; Core::ICore *core; diff --git a/src/plugins/mercurial/mercurialcommitwidget.cpp b/src/plugins/mercurial/mercurialcommitwidget.cpp index 7f176bdf2df..d3e6c7d0a19 100644 --- a/src/plugins/mercurial/mercurialcommitwidget.cpp +++ b/src/plugins/mercurial/mercurialcommitwidget.cpp @@ -132,8 +132,17 @@ void MercurialCommitWidget::setFields(const QString &repositoryRoot, const QStri QString MercurialCommitWidget::committer() { - QString user = mercurialCommitPanelUi.authorLineEdit->text() + QLatin1String(" <") + - mercurialCommitPanelUi.emailLineEdit->text() + QLatin1Char('>'); + const QString author = mercurialCommitPanelUi.authorLineEdit->text(); + const QString email = mercurialCommitPanelUi.emailLineEdit->text(); + if (author.isEmpty()) + return QString(); + + QString user = author; + if (!email.isEmpty()) { + user += QLatin1String(" <"); + user += email; + user += QLatin1Char('>'); + } return user; } diff --git a/src/plugins/mercurial/mercurialjobrunner.cpp b/src/plugins/mercurial/mercurialjobrunner.cpp index 4b7611c8f1c..eda9f806d79 100644 --- a/src/plugins/mercurial/mercurialjobrunner.cpp +++ b/src/plugins/mercurial/mercurialjobrunner.cpp @@ -132,6 +132,21 @@ void MercurialJobRunner::run() } } +QString MercurialJobRunner::msgExecute(const QString &binary, const QStringList &args) +{ + return tr("Executing: %1 %2\n").arg(binary, args.join(QString(QLatin1Char(' ')))); +} + +QString MercurialJobRunner::msgStartFailed(const QString &binary, const QString &why) +{ + return tr("Unable to start mercurial process '%1': %2").arg(binary, why); +} + +QString MercurialJobRunner::msgTimeout(int timeoutMS) +{ + return tr("Timed out after %1ms waiting for mercurial process to finish.").arg(timeoutMS); +} + void MercurialJobRunner::task(const QSharedPointer<HgTask> &job) { HgTask *taskData = job.data(); @@ -155,8 +170,8 @@ void MercurialJobRunner::task(const QSharedPointer<HgTask> &job) Qt::QueuedConnection); } - const QString starting = tr("Executing: %1 %2\n").arg(binary, taskData->args().join(QString(QLatin1Char(' ')))); - emit commandStarted(starting); + const QStringList args = standardArguments + taskData->args(); + emit commandStarted(msgExecute(binary, args)); //infom the user of what we are going to try and perform if (Constants::debug) @@ -165,13 +180,11 @@ void MercurialJobRunner::task(const QSharedPointer<HgTask> &job) QProcess hgProcess; hgProcess.setWorkingDirectory(taskData->repositoryRoot()); - QStringList args = standardArguments; - args << taskData->args(); hgProcess.start(binary, args); - if (!hgProcess.waitForStarted()) { - emit error(tr("Unable to start mercurial process '%1': %2").arg(binary, hgProcess.errorString())); + if (!hgProcess.waitForStarted()) { + emit error(msgStartFailed(binary, hgProcess.errorString())); return; } @@ -179,7 +192,7 @@ void MercurialJobRunner::task(const QSharedPointer<HgTask> &job) if (!hgProcess.waitForFinished(timeout)) { hgProcess.terminate(); - emit error(tr("Timed out waiting for mercurial process to finish.")); + emit error(msgTimeout(timeout)); return; } diff --git a/src/plugins/mercurial/mercurialjobrunner.h b/src/plugins/mercurial/mercurialjobrunner.h index 7407c088c4f..8f477abd883 100644 --- a/src/plugins/mercurial/mercurialjobrunner.h +++ b/src/plugins/mercurial/mercurialjobrunner.h @@ -70,7 +70,8 @@ private: VCSBase::VCSBaseEditor *editor; }; - +/* A job queue running in a separate thread, executing commands + * and emitting status/log signals. */ class MercurialJobRunner : public QThread { Q_OBJECT @@ -80,6 +81,10 @@ public: void enqueueJob(const QSharedPointer<HgTask> &job); void restart(); + static QString msgExecute(const QString &binary, const QStringList &args); + static QString msgStartFailed(const QString &binary, const QString &why); + static QString msgTimeout(int timeoutMS); + protected: void run(); diff --git a/src/plugins/mercurial/mercurialplugin.cpp b/src/plugins/mercurial/mercurialplugin.cpp index 339a264380c..c3a020a95d9 100644 --- a/src/plugins/mercurial/mercurialplugin.cpp +++ b/src/plugins/mercurial/mercurialplugin.cpp @@ -199,6 +199,11 @@ MercurialSettings *MercurialPlugin::settings() return mercurialSettings; } +QStringList MercurialPlugin::standardArguments() const +{ + return mercurialSettings->standardArguments(); +} + void MercurialPlugin::createMenu() { QList<int> context = QList<int>()<< core->uniqueIDManager()->uniqueIdentifier(QLatin1String(Core::Constants::C_GLOBAL)); @@ -316,7 +321,7 @@ void MercurialPlugin::createDirectoryActions(const QList<int> &context) connect(action, SIGNAL(triggered()), this, SLOT(logRepository())); mercurialContainer->addAction(command); - action = new QAction(tr("Revert"), this); + action = new QAction(tr("Revert..."), this); actionList.append(action); command = actionManager->registerAction(action, QLatin1String(Constants::REVERTMULTI), context); connect(action, SIGNAL(triggered()), this, SLOT(revertMulti())); @@ -354,43 +359,43 @@ void MercurialPlugin::statusMulti() void MercurialPlugin::createRepositoryActions(const QList<int> &context) { - QAction *action = new QAction(tr("Pull"), this); + QAction *action = new QAction(tr("Pull..."), this); actionList.append(action); Core::Command *command = actionManager->registerAction(action, QLatin1String(Constants::PULL), context); connect(action, SIGNAL(triggered()), this, SLOT(pull())); mercurialContainer->addAction(command); - action = new QAction(tr("Push"), this); + action = new QAction(tr("Push..."), this); actionList.append(action); command = actionManager->registerAction(action, QLatin1String(Constants::PUSH), context); connect(action, SIGNAL(triggered()), this, SLOT(push())); mercurialContainer->addAction(command); - action = new QAction(tr("Update"), this); + action = new QAction(tr("Update..."), this); actionList.append(action); command = actionManager->registerAction(action, QLatin1String(Constants::UPDATE), context); connect(action, SIGNAL(triggered()), this, SLOT(update())); mercurialContainer->addAction(command); - action = new QAction(tr("Import"), this); + action = new QAction(tr("Import..."), this); actionList.append(action); command = actionManager->registerAction(action, QLatin1String(Constants::IMPORT), context); connect(action, SIGNAL(triggered()), this, SLOT(import())); mercurialContainer->addAction(command); - action = new QAction(tr("Incoming"), this); + action = new QAction(tr("Incoming..."), this); actionList.append(action); command = actionManager->registerAction(action, QLatin1String(Constants::INCOMING), context); connect(action, SIGNAL(triggered()), this, SLOT(incoming())); mercurialContainer->addAction(command); - action = new QAction(tr("Outgoing"), this); + action = new QAction(tr("Outgoing..."), this); actionList.append(action); command = actionManager->registerAction(action, QLatin1String(Constants::OUTGOING), context); connect(action, SIGNAL(triggered()), this, SLOT(outgoing())); mercurialContainer->addAction(command); - action = new QAction(tr("Commit"), this); + action = new QAction(tr("Commit..."), this); actionList.append(action); command = actionManager->registerAction(action, QLatin1String(Constants::COMMIT), context); command->setDefaultKeySequence(QKeySequence(tr("Alt+H,Alt+C"))); diff --git a/src/plugins/mercurial/mercurialplugin.h b/src/plugins/mercurial/mercurialplugin.h index 331ef3142db..3cd397c6bcc 100644 --- a/src/plugins/mercurial/mercurialplugin.h +++ b/src/plugins/mercurial/mercurialplugin.h @@ -83,6 +83,7 @@ public: QString currentProjectName(); QFileInfo currentProjectRoot(); bool closeEditor(Core::IEditor *editor); + QStringList standardArguments() const; MercurialSettings *settings(); diff --git a/src/plugins/mercurial/mercurialsettings.cpp b/src/plugins/mercurial/mercurialsettings.cpp index 790da7777d1..55a3ae31fa3 100644 --- a/src/plugins/mercurial/mercurialsettings.cpp +++ b/src/plugins/mercurial/mercurialsettings.cpp @@ -31,7 +31,6 @@ #include "constants.h" #include <coreplugin/icore.h> - #include <QtCore/QSettings> @@ -134,11 +133,5 @@ void MercurialSettings::readSettings() void MercurialSettings::setBinAndArgs() { standardArgs.clear(); - -#ifdef Q_OS_WIN - bin = QLatin1String("cmd.exe"); - standardArgs << "/c" << app; -#else bin = app; -#endif } -- GitLab