vcsbaseclient.cpp 24.8 KB
Newer Older
cerf's avatar
cerf committed
1 2
/**************************************************************************
**
3
** Copyright (c) 2014 Brian McGillion and Hugues Delorme
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
cerf's avatar
cerf committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
cerf's avatar
cerf committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
Eike Ziller's avatar
Eike Ziller committed
13 14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
cerf's avatar
cerf committed
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24 25 26
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
cerf's avatar
cerf committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
cerf's avatar
cerf committed
30 31

#include "vcsbaseclient.h"
hjk's avatar
hjk committed
32
#include "vcscommand.h"
cerf's avatar
cerf committed
33
#include "vcsbaseclientsettings.h"
34
#include "vcsbaseeditorparameterwidget.h"
cerf's avatar
cerf committed
35 36

#include <coreplugin/icore.h>
37
#include <coreplugin/vcsmanager.h>
cerf's avatar
cerf committed
38 39 40 41

#include <utils/qtcassert.h>
#include <utils/synchronousprocess.h>
#include <vcsbase/vcsbaseeditor.h>
42
#include <vcsbase/vcsoutputwindow.h>
cerf's avatar
cerf committed
43 44
#include <vcsbase/vcsbaseplugin.h>

45 46 47 48 49
#include <QStringList>
#include <QDir>
#include <QProcess>
#include <QSignalMapper>
#include <QTextCodec>
hjk's avatar
hjk committed
50
#include <QDebug>
51 52
#include <QFileInfo>
#include <QByteArray>
53 54
#include <QVariant>
#include <QProcessEnvironment>
cerf's avatar
cerf committed
55

56
/*!
hjk's avatar
hjk committed
57
    \class VcsBase::VcsBaseClient
58

59 60
    \brief The VcsBaseClient class is the base class for Mercurial and Bazaar
    'clients'.
61 62 63

    Provides base functionality for common commands (diff, log, etc).

hjk's avatar
hjk committed
64
    \sa VcsBase::VcsJobRunner
65 66
*/

cerf's avatar
cerf committed
67 68
Q_DECLARE_METATYPE(QVariant)

69
static Core::IEditor *locateEditor(const char *property, const QString &entry)
cerf's avatar
cerf committed
70
{
hjk's avatar
hjk committed
71
    foreach (Core::IDocument *document, Core::DocumentModel::openedDocuments())
72
        if (document->property(property).toString() == entry)
hjk's avatar
hjk committed
73
            return Core::DocumentModel::editorsForDocument(document).first();
cerf's avatar
cerf committed
74 75 76
    return 0;
}

hjk's avatar
hjk committed
77
namespace VcsBase {
cerf's avatar
cerf committed
78

hjk's avatar
hjk committed
79
class VcsBaseClientPrivate
Friedemann Kleint's avatar
Friedemann Kleint committed
80 81
{
public:
hjk's avatar
hjk committed
82
    VcsBaseClientPrivate(VcsBaseClient *client, VcsBaseClientSettings *settings);
83

84
    void statusParser(const QString &text);
85 86
    void annotateRevision(const QString &workingDirectory, const QString &file,
                          QString change, int lineNumber);
87
    void saveSettings();
Friedemann Kleint's avatar
Friedemann Kleint committed
88

hjk's avatar
hjk committed
89
    void bindCommandToEditor(VcsCommand *cmd, VcsBaseEditorWidget *editor);
90
    void commandFinishedGotoLine(QWidget *editorObject);
91

hjk's avatar
hjk committed
92
    VcsBaseClientSettings *m_clientSettings;
93
    QSignalMapper *m_cmdFinishedMapper;
94 95

private:
hjk's avatar
hjk committed
96
    VcsBaseClient *m_client;
Friedemann Kleint's avatar
Friedemann Kleint committed
97 98
};

hjk's avatar
hjk committed
99
VcsBaseClientPrivate::VcsBaseClientPrivate(VcsBaseClient *client, VcsBaseClientSettings *settings) :
100 101 102
    m_clientSettings(settings),
    m_cmdFinishedMapper(new QSignalMapper(client)),
    m_client(client)
103 104 105
{
}

106
void VcsBaseClientPrivate::statusParser(const QString &text)
107
{
hjk's avatar
hjk committed
108
    QList<VcsBaseClient::StatusItem> lineInfoList;
109

110
    QStringList rawStatusList = text.split(QLatin1Char('\n'));
111 112

    foreach (const QString &string, rawStatusList) {
hjk's avatar
hjk committed
113
        const VcsBaseClient::StatusItem lineInfo = m_client->parseStatusLine(string);
114 115
        if (!lineInfo.flags.isEmpty() && !lineInfo.file.isEmpty())
            lineInfoList.append(lineInfo);
116 117
    }

118
    emit m_client->parsedStatus(lineInfoList);
119 120
}

121 122
void VcsBaseClientPrivate::annotateRevision(const QString &workingDirectory,  const QString &file,
                                            QString change, int lineNumber)
123 124 125 126 127 128
{
    // This might be invoked with a verbose revision description
    // "SHA1 author subject" from the annotation context menu. Strip the rest.
    const int blankPos = change.indexOf(QLatin1Char(' '));
    if (blankPos != -1)
        change.truncate(blankPos);
129
    m_client->annotate(workingDirectory, file, change, lineNumber);
130 131
}

hjk's avatar
hjk committed
132
void VcsBaseClientPrivate::saveSettings()
Friedemann Kleint's avatar
Friedemann Kleint committed
133
{
hjk's avatar
hjk committed
134
    m_clientSettings->writeSettings(Core::ICore::settings());
Friedemann Kleint's avatar
Friedemann Kleint committed
135 136
}

hjk's avatar
hjk committed
137
void VcsBaseClientPrivate::bindCommandToEditor(VcsCommand *cmd, VcsBaseEditorWidget *editor)
138
{
139
    editor->setCommand(cmd);
140 141 142 143
    QObject::connect(cmd, SIGNAL(finished(bool,int,QVariant)), m_cmdFinishedMapper, SLOT(map()));
    m_cmdFinishedMapper->setMapping(cmd, editor);
}

144
void VcsBaseClientPrivate::commandFinishedGotoLine(QWidget *editorObject)
145
{
hjk's avatar
hjk committed
146 147
    VcsBaseEditorWidget *editor = qobject_cast<VcsBaseEditorWidget *>(editorObject);
    VcsCommand *cmd = qobject_cast<VcsCommand *>(m_cmdFinishedMapper->mapping(editor));
148
    if (editor && cmd) {
149 150 151
        if (!cmd->lastExecutionSuccess()) {
            editor->reportCommandFinished(false, cmd->lastExecutionExitCode(), cmd->cookie());
        } else if (cmd->cookie().type() == QVariant::Int) {
152 153 154 155 156
            const int line = cmd->cookie().toInt();
            if (line >= 0)
                editor->gotoLine(line);
        }
        m_cmdFinishedMapper->removeMappings(cmd);
157 158 159
    }
}

hjk's avatar
hjk committed
160
VcsBaseClient::StatusItem::StatusItem(const QString &s, const QString &f) :
161 162 163 164
    flags(s), file(f)
{
}

hjk's avatar
hjk committed
165 166
VcsBaseClient::VcsBaseClient(VcsBaseClientSettings *settings) :
    d(new VcsBaseClientPrivate(this, settings))
cerf's avatar
cerf committed
167 168
{
    qRegisterMetaType<QVariant>();
hjk's avatar
hjk committed
169
    connect(Core::ICore::instance(), SIGNAL(saveSettingsRequested()), this, SLOT(saveSettings()));
170
    connect(d->m_cmdFinishedMapper, SIGNAL(mapped(QWidget*)), this, SLOT(commandFinishedGotoLine(QWidget*)));
cerf's avatar
cerf committed
171 172
}

hjk's avatar
hjk committed
173
VcsBaseClient::~VcsBaseClient()
cerf's avatar
cerf committed
174
{
hjk's avatar
hjk committed
175
    delete d;
cerf's avatar
cerf committed
176 177
}

hjk's avatar
hjk committed
178
bool VcsBaseClient::synchronousCreateRepository(const QString &workingDirectory,
179
                                                const QStringList &extraOptions)
cerf's avatar
cerf committed
180
{
181 182
    QStringList args(vcsCommandString(CreateRepositoryCommand));
    args << extraOptions;
cerf's avatar
cerf committed
183 184 185
    QByteArray outputData;
    if (!vcsFullySynchronousExec(workingDirectory, args, &outputData))
        return false;
186
    VcsOutputWindow::append(
187
                Utils::SynchronousProcess::normalizeNewlines(QString::fromLocal8Bit(outputData)));
188 189 190

    resetCachedVcsInfo(workingDirectory);

cerf's avatar
cerf committed
191 192 193
    return true;
}

hjk's avatar
hjk committed
194
bool VcsBaseClient::synchronousClone(const QString &workingDir,
cerf's avatar
cerf committed
195 196
                                     const QString &srcLocation,
                                     const QString &dstLocation,
197
                                     const QStringList &extraOptions)
cerf's avatar
cerf committed
198 199 200
{
    QStringList args;
    args << vcsCommandString(CloneCommand)
201
         << extraOptions << srcLocation << dstLocation;
cerf's avatar
cerf committed
202
    QByteArray stdOut;
203 204 205
    const bool cloneOk = vcsFullySynchronousExec(workingDir, args, &stdOut);
    resetCachedVcsInfo(workingDir);
    return cloneOk;
cerf's avatar
cerf committed
206 207
}

hjk's avatar
hjk committed
208
bool VcsBaseClient::synchronousAdd(const QString &workingDir, const QString &filename,
209
                                   const QStringList &extraOptions)
cerf's avatar
cerf committed
210 211
{
    QStringList args;
212
    args << vcsCommandString(AddCommand) << extraOptions << filename;
cerf's avatar
cerf committed
213 214 215 216
    QByteArray stdOut;
    return vcsFullySynchronousExec(workingDir, args, &stdOut);
}

hjk's avatar
hjk committed
217
bool VcsBaseClient::synchronousRemove(const QString &workingDir, const QString &filename,
218
                                      const QStringList &extraOptions)
cerf's avatar
cerf committed
219 220
{
    QStringList args;
221
    args << vcsCommandString(RemoveCommand) << extraOptions << filename;
cerf's avatar
cerf committed
222 223 224 225
    QByteArray stdOut;
    return vcsFullySynchronousExec(workingDir, args, &stdOut);
}

hjk's avatar
hjk committed
226
bool VcsBaseClient::synchronousMove(const QString &workingDir,
227 228
                                    const QString &from, const QString &to,
                                    const QStringList &extraOptions)
cerf's avatar
cerf committed
229 230
{
    QStringList args;
231
    args << vcsCommandString(MoveCommand) << extraOptions << from << to;
cerf's avatar
cerf committed
232 233 234 235
    QByteArray stdOut;
    return vcsFullySynchronousExec(workingDir, args, &stdOut);
}

hjk's avatar
hjk committed
236
bool VcsBaseClient::synchronousPull(const QString &workingDir,
cerf's avatar
cerf committed
237
                                    const QString &srcLocation,
238
                                    const QStringList &extraOptions)
cerf's avatar
cerf committed
239 240
{
    QStringList args;
241
    args << vcsCommandString(PullCommand) << extraOptions << srcLocation;
cerf's avatar
cerf committed
242 243
    // Disable UNIX terminals to suppress SSH prompting
    const unsigned flags =
hjk's avatar
hjk committed
244 245 246
            VcsBasePlugin::SshPasswordPrompt
            | VcsBasePlugin::ShowStdOutInLogWindow
            | VcsBasePlugin::ShowSuccessMessage;
cerf's avatar
cerf committed
247 248 249 250 251 252 253
    const Utils::SynchronousProcessResponse resp = vcsSynchronousExec(workingDir, args, flags);
    const bool ok = resp.result == Utils::SynchronousProcessResponse::Finished;
    if (ok)
        emit changed(QVariant(workingDir));
    return ok;
}

hjk's avatar
hjk committed
254
bool VcsBaseClient::synchronousPush(const QString &workingDir,
cerf's avatar
cerf committed
255
                                    const QString &dstLocation,
256
                                    const QStringList &extraOptions)
cerf's avatar
cerf committed
257 258
{
    QStringList args;
259
    args << vcsCommandString(PushCommand) << extraOptions << dstLocation;
cerf's avatar
cerf committed
260 261
    // Disable UNIX terminals to suppress SSH prompting
    const unsigned flags =
hjk's avatar
hjk committed
262 263 264
            VcsBasePlugin::SshPasswordPrompt
            | VcsBasePlugin::ShowStdOutInLogWindow
            | VcsBasePlugin::ShowSuccessMessage;
cerf's avatar
cerf committed
265 266 267 268
    const Utils::SynchronousProcessResponse resp = vcsSynchronousExec(workingDir, args, flags);
    return resp.result == Utils::SynchronousProcessResponse::Finished;
}

hjk's avatar
hjk committed
269
bool VcsBaseClient::vcsFullySynchronousExec(const QString &workingDir,
cerf's avatar
cerf committed
270
                                            const QStringList &args,
271
                                            QByteArray *output) const
cerf's avatar
cerf committed
272 273 274 275
{
    QProcess vcsProcess;
    if (!workingDir.isEmpty())
        vcsProcess.setWorkingDirectory(workingDir);
276
    vcsProcess.setProcessEnvironment(processEnvironment());
cerf's avatar
cerf committed
277

278
    const Utils::FileName binary = settings()->binaryPath();
cerf's avatar
cerf committed
279

280
    VcsOutputWindow::appendCommand(workingDir, binary, args);
cerf's avatar
cerf committed
281

282
    vcsProcess.start(binary.toString(), args);
cerf's avatar
cerf committed
283 284

    if (!vcsProcess.waitForStarted()) {
285
        VcsOutputWindow::appendError(tr("Unable to start process \"%1\": %2")
286
                                         .arg(binary.toUserOutput(), vcsProcess.errorString()));
cerf's avatar
cerf committed
287 288 289 290 291 292
        return false;
    }

    vcsProcess.closeWriteChannel();

    QByteArray stdErr;
hjk's avatar
hjk committed
293
    const int timeoutSec = settings()->intValue(VcsBaseClientSettings::timeoutKey);
294
    if (!Utils::SynchronousProcess::readDataFromProcess(vcsProcess, timeoutSec * 1000,
cerf's avatar
cerf committed
295 296
                                                        output, &stdErr, true)) {
        Utils::SynchronousProcess::stopProcess(vcsProcess);
297
        VcsOutputWindow::appendError(tr("Timed out after %1s waiting for the process %2 to finish.")
298
                                         .arg(timeoutSec).arg(binary.toUserOutput()));
cerf's avatar
cerf committed
299 300 301
        return false;
    }
    if (!stdErr.isEmpty())
302
        VcsOutputWindow::appendError(QString::fromLocal8Bit(stdErr));
cerf's avatar
cerf committed
303 304 305 306

    return vcsProcess.exitStatus() == QProcess::NormalExit && vcsProcess.exitCode() == 0;
}

hjk's avatar
hjk committed
307
Utils::SynchronousProcessResponse VcsBaseClient::vcsSynchronousExec(
308 309 310
        const QString &workingDirectory,
        const QStringList &args,
        unsigned flags,
311
        QTextCodec *outputCodec) const
cerf's avatar
cerf committed
312
{
313
    const Utils::FileName binary = settings()->binaryPath();
hjk's avatar
hjk committed
314
    const int timeoutSec = settings()->intValue(VcsBaseClientSettings::timeoutKey);
hjk's avatar
hjk committed
315 316
    return VcsBasePlugin::runVcs(workingDirectory, binary, args,
                                 timeoutSec * 1000, flags, outputCodec);
cerf's avatar
cerf committed
317 318
}

hjk's avatar
hjk committed
319
void VcsBaseClient::annotate(const QString &workingDir, const QString &file,
320
                             const QString &revision /* = QString() */,
321 322
                             int lineNumber /* = -1 */,
                             const QStringList &extraOptions)
cerf's avatar
cerf committed
323 324 325
{
    const QString vcsCmdString = vcsCommandString(AnnotateCommand);
    QStringList args;
326
    args << vcsCmdString << revisionSpec(revision) << extraOptions << file;
hjk's avatar
hjk committed
327
    const Core::Id kind = vcsEditorKind(AnnotateCommand);
328
    const QString id = VcsBaseEditor::getSource(workingDir, QStringList(file));
cerf's avatar
cerf committed
329
    const QString title = vcsEditorTitle(vcsCmdString, id);
330
    const QString source = VcsBaseEditor::getSource(workingDir, file);
cerf's avatar
cerf committed
331

hjk's avatar
hjk committed
332 333
    VcsBaseEditorWidget *editor = createVcsEditor(kind, title, source, true,
                                                  vcsCmdString.toLatin1().constData(), id);
cerf's avatar
cerf committed
334

hjk's avatar
hjk committed
335
    VcsCommand *cmd = createCommand(workingDir, editor);
336 337
    cmd->setCookie(lineNumber);
    enqueueJob(cmd, args);
cerf's avatar
cerf committed
338 339
}

hjk's avatar
hjk committed
340
void VcsBaseClient::diff(const QString &workingDir, const QStringList &files,
341
                         const QStringList &extraOptions)
cerf's avatar
cerf committed
342 343
{
    const QString vcsCmdString = vcsCommandString(DiffCommand);
hjk's avatar
hjk committed
344
    const Core::Id kind = vcsEditorKind(DiffCommand);
345
    const QString id = VcsBaseEditor::getTitleId(workingDir, files);
cerf's avatar
cerf committed
346
    const QString title = vcsEditorTitle(vcsCmdString, id);
347
    const QString source = VcsBaseEditor::getSource(workingDir, files);
hjk's avatar
hjk committed
348 349
    VcsBaseEditorWidget *editor = createVcsEditor(kind, title, source, true,
                                                  vcsCmdString.toLatin1().constData(), id);
350
    editor->setWorkingDirectory(workingDir);
cerf's avatar
cerf committed
351

352 353 354
    VcsBaseEditorParameterWidget *paramWidget = editor->configurationWidget();
    if (!paramWidget && (paramWidget = createDiffEditor(workingDir, files, extraOptions))) {
        // editor has been just created, createVcsEditor() didn't set a configuration widget yet
355 356
        connect(editor, &VcsBaseEditorWidget::diffChunkReverted,
                paramWidget, &VcsBaseEditorParameterWidget::executeCommand);
357 358 359 360 361
        editor->setConfigurationWidget(paramWidget);
    }

    QStringList args;
    const QStringList paramArgs = paramWidget != 0 ? paramWidget->arguments() : QStringList();
362
    args << vcsCmdString << extraOptions << paramArgs << files;
363
    QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VcsBaseEditor::getCodec(source);
hjk's avatar
hjk committed
364
    VcsCommand *command = createCommand(workingDir, editor);
365
    command->setCodec(codec);
366
    enqueueJob(command, args, exitCodeInterpreter(DiffCommand, command));
cerf's avatar
cerf committed
367 368
}

hjk's avatar
hjk committed
369
void VcsBaseClient::log(const QString &workingDir, const QStringList &files,
370
                        const QStringList &extraOptions,
cerf's avatar
cerf committed
371 372 373
                        bool enableAnnotationContextMenu)
{
    const QString vcsCmdString = vcsCommandString(LogCommand);
hjk's avatar
hjk committed
374
    const Core::Id kind = vcsEditorKind(LogCommand);
375
    const QString id = VcsBaseEditor::getTitleId(workingDir, files);
cerf's avatar
cerf committed
376
    const QString title = vcsEditorTitle(vcsCmdString, id);
377
    const QString source = VcsBaseEditor::getSource(workingDir, files);
hjk's avatar
hjk committed
378 379
    VcsBaseEditorWidget *editor = createVcsEditor(kind, title, source, true,
                                                  vcsCmdString.toLatin1().constData(), id);
cerf's avatar
cerf committed
380 381
    editor->setFileLogAnnotateEnabled(enableAnnotationContextMenu);

382 383 384
    VcsBaseEditorParameterWidget *paramWidget = editor->configurationWidget();
    if (!paramWidget && (paramWidget = createLogEditor(workingDir, files, extraOptions))) {
        // editor has been just created, createVcsEditor() didn't set a configuration widget yet
385
        editor->setConfigurationWidget(paramWidget);
386
    }
387 388 389

    QStringList args;
    const QStringList paramArgs = paramWidget != 0 ? paramWidget->arguments() : QStringList();
390
    args << vcsCmdString << extraOptions << paramArgs << files;
391
    enqueueJob(createCommand(workingDir, editor), args);
cerf's avatar
cerf committed
392 393
}

hjk's avatar
hjk committed
394
void VcsBaseClient::revertFile(const QString &workingDir,
cerf's avatar
cerf committed
395
                               const QString &file,
396 397
                               const QString &revision,
                               const QStringList &extraOptions)
cerf's avatar
cerf committed
398 399
{
    QStringList args(vcsCommandString(RevertCommand));
400
    args << revisionSpec(revision) << extraOptions << file;
cerf's avatar
cerf committed
401
    // Indicate repository change or file list
hjk's avatar
hjk committed
402
    VcsCommand *cmd = createCommand(workingDir);
403 404 405
    cmd->setCookie(QStringList(workingDir + QLatin1Char('/') + file));
    connect(cmd, SIGNAL(success(QVariant)), this, SIGNAL(changed(QVariant)), Qt::QueuedConnection);
    enqueueJob(cmd, args);
cerf's avatar
cerf committed
406 407
}

hjk's avatar
hjk committed
408
void VcsBaseClient::revertAll(const QString &workingDir, const QString &revision,
409
                              const QStringList &extraOptions)
cerf's avatar
cerf committed
410 411
{
    QStringList args(vcsCommandString(RevertCommand));
412
    args << revisionSpec(revision) << extraOptions;
cerf's avatar
cerf committed
413
    // Indicate repository change or file list
hjk's avatar
hjk committed
414
    VcsCommand *cmd = createCommand(workingDir);
415 416 417
    cmd->setCookie(QStringList(workingDir));
    connect(cmd, SIGNAL(success(QVariant)), this, SIGNAL(changed(QVariant)), Qt::QueuedConnection);
    enqueueJob(createCommand(workingDir), args);
cerf's avatar
cerf committed
418 419
}

hjk's avatar
hjk committed
420
void VcsBaseClient::status(const QString &workingDir, const QString &file,
421
                           const QStringList &extraOptions)
cerf's avatar
cerf committed
422 423
{
    QStringList args(vcsCommandString(StatusCommand));
424
    args << extraOptions << file;
425
    VcsOutputWindow::setRepository(workingDir);
hjk's avatar
hjk committed
426
    VcsCommand *cmd = createCommand(workingDir, 0, VcsWindowOutputBind);
427
    connect(cmd, SIGNAL(finished(bool,int,QVariant)), VcsOutputWindow::instance(), SLOT(clearRepository()),
428 429
            Qt::QueuedConnection);
    enqueueJob(cmd, args);
cerf's avatar
cerf committed
430 431
}

hjk's avatar
hjk committed
432
void VcsBaseClient::emitParsedStatus(const QString &repository, const QStringList &extraOptions)
cerf's avatar
cerf committed
433 434
{
    QStringList args(vcsCommandString(StatusCommand));
435
    args << extraOptions;
hjk's avatar
hjk committed
436
    VcsCommand *cmd = createCommand(repository);
437
    connect(cmd, SIGNAL(output(QString)), this, SLOT(statusParser(QString)));
438
    enqueueJob(cmd, args);
cerf's avatar
cerf committed
439 440
}

hjk's avatar
hjk committed
441
QString VcsBaseClient::vcsCommandString(VcsCommandTag cmd) const
cerf's avatar
cerf committed
442
{
Tobias Hunger's avatar
Tobias Hunger committed
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
    switch (cmd) {
    case CreateRepositoryCommand: return QLatin1String("init");
    case CloneCommand: return QLatin1String("clone");
    case AddCommand: return QLatin1String("add");
    case RemoveCommand: return QLatin1String("remove");
    case MoveCommand: return QLatin1String("rename");
    case PullCommand: return QLatin1String("pull");
    case PushCommand: return QLatin1String("push");
    case CommitCommand: return QLatin1String("commit");
    case ImportCommand: return QLatin1String("import");
    case UpdateCommand: return QLatin1String("update");
    case RevertCommand: return QLatin1String("revert");
    case AnnotateCommand: return QLatin1String("annotate");
    case DiffCommand: return QLatin1String("diff");
    case LogCommand: return QLatin1String("log");
    case StatusCommand: return QLatin1String("status");
cerf's avatar
cerf committed
459
    }
Tobias Hunger's avatar
Tobias Hunger committed
460
    return QString();
cerf's avatar
cerf committed
461 462
}

hjk's avatar
hjk committed
463
Utils::ExitCodeInterpreter *VcsBaseClient::exitCodeInterpreter(VcsCommandTag cmd, QObject *parent) const
464 465 466 467 468 469
{
    Q_UNUSED(cmd)
    Q_UNUSED(parent)
    return 0;
}

hjk's avatar
hjk committed
470
void VcsBaseClient::import(const QString &repositoryRoot, const QStringList &files,
471
                           const QStringList &extraOptions)
cerf's avatar
cerf committed
472 473
{
    QStringList args(vcsCommandString(ImportCommand));
474
    args << extraOptions << files;
475
    enqueueJob(createCommand(repositoryRoot), args);
cerf's avatar
cerf committed
476 477
}

hjk's avatar
hjk committed
478
void VcsBaseClient::view(const QString &source, const QString &id,
479
                         const QStringList &extraOptions)
cerf's avatar
cerf committed
480
{
481 482
    QStringList args;
    args << extraOptions << revisionSpec(id);
hjk's avatar
hjk committed
483
    const Core::Id kind = vcsEditorKind(DiffCommand);
cerf's avatar
cerf committed
484 485
    const QString title = vcsEditorTitle(vcsCommandString(LogCommand), id);

hjk's avatar
hjk committed
486
    VcsBaseEditorWidget *editor = createVcsEditor(kind, title, source, true, "view", id);
cerf's avatar
cerf committed
487

488 489
    const QFileInfo fi(source);
    const QString workingDirPath = fi.isFile() ? fi.absolutePath() : source;
490
    enqueueJob(createCommand(workingDirPath, editor), args);
cerf's avatar
cerf committed
491 492
}

hjk's avatar
hjk committed
493
void VcsBaseClient::update(const QString &repositoryRoot, const QString &revision,
494
                           const QStringList &extraOptions)
cerf's avatar
cerf committed
495 496
{
    QStringList args(vcsCommandString(UpdateCommand));
497
    args << revisionSpec(revision) << extraOptions;
hjk's avatar
hjk committed
498
    VcsCommand *cmd = createCommand(repositoryRoot);
499 500 501
    cmd->setCookie(repositoryRoot);
    connect(cmd, SIGNAL(success(QVariant)), this, SIGNAL(changed(QVariant)), Qt::QueuedConnection);
    enqueueJob(cmd, args);
cerf's avatar
cerf committed
502 503
}

hjk's avatar
hjk committed
504
void VcsBaseClient::commit(const QString &repositoryRoot,
cerf's avatar
cerf committed
505 506
                           const QStringList &files,
                           const QString &commitMessageFile,
507
                           const QStringList &extraOptions)
cerf's avatar
cerf committed
508
{
509
    // Handling of commitMessageFile is a bit tricky :
hjk's avatar
hjk committed
510
    //   VcsBaseClient cannot do something with it because it doesn't know which
511 512 513
    //   option to use (-F ? but sub VCS clients might require a different option
    //   name like -l for hg ...)
    //
hjk's avatar
hjk committed
514
    //   So descendants of VcsBaseClient *must* redefine commit() and extend
515 516
    //   extraOptions with the usage for commitMessageFile (see BazaarClient::commit()
    //   for example)
cerf's avatar
cerf committed
517
    QStringList args(vcsCommandString(CommitCommand));
518
    args << extraOptions << files;
519 520 521 522
    VcsCommand *cmd = createCommand(repositoryRoot, 0, VcsWindowOutputBind);
    if (!commitMessageFile.isEmpty())
        connect(cmd, &VcsCommand::finished, [commitMessageFile]() { QFile(commitMessageFile).remove(); });
    enqueueJob(cmd, args);
cerf's avatar
cerf committed
523 524
}

hjk's avatar
hjk committed
525
VcsBaseClientSettings *VcsBaseClient::settings() const
526 527 528 529
{
    return d->m_clientSettings;
}

hjk's avatar
hjk committed
530
VcsBaseEditorParameterWidget *VcsBaseClient::createDiffEditor(const QString &workingDir,
531 532
                                                              const QStringList &files,
                                                              const QStringList &extraOptions)
533
{
534 535 536 537
    Q_UNUSED(workingDir);
    Q_UNUSED(files);
    Q_UNUSED(extraOptions);
    return 0;
538 539
}

hjk's avatar
hjk committed
540
VcsBaseEditorParameterWidget *VcsBaseClient::createLogEditor(const QString &workingDir,
541 542 543 544 545 546 547 548
                                                             const QStringList &files,
                                                             const QStringList &extraOptions)
{
    Q_UNUSED(workingDir);
    Q_UNUSED(files);
    Q_UNUSED(extraOptions);
    return 0;
}
549

hjk's avatar
hjk committed
550
QString VcsBaseClient::vcsEditorTitle(const QString &vcsCmd, const QString &sourceId) const
cerf's avatar
cerf committed
551
{
552 553
    const Utils::FileName binary = settings()->binaryPath();
    return binary.toFileInfo().baseName() +
554 555
            QLatin1Char(' ') + vcsCmd + QLatin1Char(' ') +
            QFileInfo(sourceId).fileName();
cerf's avatar
cerf committed
556 557
}

hjk's avatar
hjk committed
558 559 560 561
VcsBaseEditorWidget *VcsBaseClient::createVcsEditor(Core::Id kind, QString title,
                                                    const QString &source, bool setSourceCodec,
                                                    const char *registerDynamicProperty,
                                                    const QString &dynamicPropertyValue) const
cerf's avatar
cerf committed
562
{
hjk's avatar
hjk committed
563
    VcsBaseEditorWidget *baseEditor = 0;
hjk's avatar
hjk committed
564
    Core::IEditor *outputEditor = locateEditor(registerDynamicProperty, dynamicPropertyValue);
cerf's avatar
cerf committed
565 566 567
    const QString progressMsg = tr("Working...");
    if (outputEditor) {
        // Exists already
568
        outputEditor->document()->setContents(progressMsg.toUtf8());
569
        baseEditor = VcsBaseEditor::getVcsBaseEditor(outputEditor);
cerf's avatar
cerf committed
570
        QTC_ASSERT(baseEditor, return 0);
571
        Core::EditorManager::activateEditor(outputEditor);
cerf's avatar
cerf committed
572
    } else {
573
        outputEditor = Core::EditorManager::openEditorWithContents(kind, &title, progressMsg.toUtf8());
574
        outputEditor->document()->setProperty(registerDynamicProperty, dynamicPropertyValue);
575
        baseEditor = VcsBaseEditor::getVcsBaseEditor(outputEditor);
576 577
        connect(baseEditor, SIGNAL(annotateRevisionRequested(QString,QString,QString,int)),
                this, SLOT(annotateRevision(QString,QString,QString,int)));
cerf's avatar
cerf committed
578 579 580
        QTC_ASSERT(baseEditor, return 0);
        baseEditor->setSource(source);
        if (setSourceCodec)
581
            baseEditor->setCodec(VcsBaseEditor::getCodec(source));
cerf's avatar
cerf committed
582 583 584 585 586 587
    }

    baseEditor->setForceReadOnly(true);
    return baseEditor;
}

hjk's avatar
hjk committed
588
QProcessEnvironment VcsBaseClient::processEnvironment() const
589
{
590
    QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
hjk's avatar
hjk committed
591
    VcsBasePlugin::setProcessEnvironment(&environment, false);
592
    return environment;
593 594
}

hjk's avatar
hjk committed
595 596 597
VcsCommand *VcsBaseClient::createCommand(const QString &workingDirectory,
                                         VcsBaseEditorWidget *editor,
                                         JobOutputBindMode mode) const
cerf's avatar
cerf committed
598
{
hjk's avatar
hjk committed
599 600
    VcsCommand *cmd = new VcsCommand(d->m_clientSettings->binaryPath(), workingDirectory,
                                     processEnvironment());
hjk's avatar
hjk committed
601
    cmd->setDefaultTimeout(d->m_clientSettings->intValue(VcsBaseClientSettings::timeoutKey));
602 603 604
    if (editor)
        d->bindCommandToEditor(cmd, editor);
    if (mode == VcsWindowOutputBind) {
Orgad Shaneh's avatar
Orgad Shaneh committed
605 606 607
        cmd->addFlags(VcsBasePlugin::ShowStdOutInLogWindow);
        if (editor) // assume that the commands output is the important thing
            cmd->addFlags(VcsBasePlugin::SilentOutput);
608
    } else if (editor) {
609
        connect(cmd, SIGNAL(output(QString)), editor, SLOT(setPlainText(QString)));
cerf's avatar
cerf committed
610
    }
611 612 613 614

    return cmd;
}

hjk's avatar
hjk committed
615
void VcsBaseClient::enqueueJob(VcsCommand *cmd, const QStringList &args, Utils::ExitCodeInterpreter *interpreter)
616
{
617
    cmd->addJob(args, interpreter);
618 619 620
    cmd->execute();
}

hjk's avatar
hjk committed
621
void VcsBaseClient::resetCachedVcsInfo(const QString &workingDir)
622
{
hjk's avatar
hjk committed
623
    Core::VcsManager::resetVersionControlForDirectory(workingDir);
cerf's avatar
cerf committed
624 625
}

hjk's avatar
hjk committed
626
} // namespace VcsBase
627 628

#include "moc_vcsbaseclient.cpp"