Commit d570b05b authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

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.
parent c8bb7e17
......@@ -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
......@@ -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
......@@ -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);
}
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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();
......
......@@ -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")));
......
......@@ -83,6 +83,7 @@ public:
QString currentProjectName();
QFileInfo currentProjectRoot();
bool closeEditor(Core::IEditor *editor);
QStringList standardArguments() const;
MercurialSettings *settings();
......
......@@ -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
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment