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