vcsbaseclient.cpp 25.7 KB
Newer Older
cerf's avatar
cerf committed
1
2
/**************************************************************************
**
Eike Ziller's avatar
Eike Ziller committed
3
4
** Copyright (C) 2015 Brian McGillion and Hugues Delorme
** Contact: http://www.qt.io/licensing
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
** 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
Eike Ziller's avatar
Eike Ziller committed
12
13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** 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
**
Eike Ziller's avatar
Eike Ziller committed
25
26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company 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>
38
39
40
#include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/idocument.h>
cerf's avatar
cerf committed
41
42
43
44

#include <utils/qtcassert.h>
#include <utils/synchronousprocess.h>
#include <vcsbase/vcsbaseeditor.h>
45
#include <vcsbase/vcsoutputwindow.h>
cerf's avatar
cerf committed
46
47
#include <vcsbase/vcsbaseplugin.h>

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

59
/*!
hjk's avatar
hjk committed
60
    \class VcsBase::VcsBaseClient
61

62
63
    \brief The VcsBaseClient class is the base class for Mercurial and Bazaar
    'clients'.
64
65
66

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

hjk's avatar
hjk committed
67
    \sa VcsBase::VcsJobRunner
68
69
*/

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

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

80
81
82
class VcsBaseClientImplPrivate
{
public:
83
    VcsBaseClientImplPrivate(VcsBaseClientImpl *client, VcsBaseClientSettings *settings);
84
85
    ~VcsBaseClientImplPrivate();

86
87
    void bindCommandToEditor(VcsCommand *cmd, VcsBaseEditorWidget *editor);

88
    VcsBaseClientSettings *m_clientSettings;
89
    QSignalMapper *m_cmdFinishedMapper;
90
91
};

92
93
94
95
96
97
98
void VcsBaseClientImplPrivate::bindCommandToEditor(VcsCommand *cmd, VcsBaseEditorWidget *editor)
{
    editor->setCommand(cmd);
    QObject::connect(cmd, &VcsCommand::finished,
                     m_cmdFinishedMapper, static_cast<void (QSignalMapper::*)()>(&QSignalMapper::map));
    m_cmdFinishedMapper->setMapping(cmd, editor);
}
99

100
101
102
103
VcsBaseClientImplPrivate::VcsBaseClientImplPrivate(VcsBaseClientImpl *client,
                                                   VcsBaseClientSettings *settings) :
    m_clientSettings(settings),
    m_cmdFinishedMapper(new QSignalMapper(client))
104
105
106
107
108
109
110
111
112
{
    m_clientSettings->readSettings(Core::ICore::settings());
}

VcsBaseClientImplPrivate::~VcsBaseClientImplPrivate()
{
    delete m_clientSettings;
}

113
114
VcsBaseClientImpl::VcsBaseClientImpl(VcsBaseClientImpl *client, VcsBaseClientSettings *settings) :
    d(new VcsBaseClientImplPrivate(client, settings))
115
116
117
{
    connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested,
            this, &VcsBaseClientImpl::saveSettings);
118
119
120

    connect(d->m_cmdFinishedMapper, static_cast<void (QSignalMapper::*)(QWidget*)>(&QSignalMapper::mapped),
            this, &VcsBaseClientImpl::commandFinishedGotoLine);
121
122
123
124
125
126
127
128
129
130
131
132
}

VcsBaseClientImpl::~VcsBaseClientImpl()
{
    delete d;
}

VcsBaseClientSettings &VcsBaseClientImpl::settings() const
{
    return *d->m_clientSettings;
}

133
134
135
136
137
Utils::FileName VcsBaseClientImpl::vcsBinary() const
{
    return settings().binaryPath();
}

138
VcsCommand *VcsBaseClientImpl::createCommand(const QString &workingDirectory,
Tobias Hunger's avatar
Tobias Hunger committed
139
140
                                             VcsBaseEditorWidget *editor,
                                             JobOutputBindMode mode) const
141
{
Orgad Shaneh's avatar
Orgad Shaneh committed
142
    auto cmd = new VcsCommand(vcsBinary(), workingDirectory, processEnvironment());
143
    cmd->setDefaultTimeoutS(vcsTimeoutS());
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
    if (editor)
        d->bindCommandToEditor(cmd, editor);
    if (mode == VcsWindowOutputBind) {
        cmd->addFlags(VcsBasePlugin::ShowStdOutInLogWindow);
        if (editor) // assume that the commands output is the important thing
            cmd->addFlags(VcsBasePlugin::SilentOutput);
    } else if (editor) {
        connect(cmd, &VcsCommand::output, editor, &VcsBaseEditorWidget::setPlainText);
    }

    return cmd;
}

void VcsBaseClientImpl::enqueueJob(VcsCommand *cmd, const QStringList &args,
                                   Utils::ExitCodeInterpreter *interpreter)
{
160
    cmd->addJob(args, vcsTimeoutS(), interpreter);
161
162
163
164
165
166
167
168
169
170
    cmd->execute();
}

QProcessEnvironment VcsBaseClientImpl::processEnvironment() const
{
    QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
    VcsBasePlugin::setProcessEnvironment(&environment, false);
    return environment;
}

171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
QString VcsBaseClientImpl::commandOutputFromLocal8Bit(const QByteArray &a)
{
    return Utils::SynchronousProcess::normalizeNewlines(QString::fromLocal8Bit(a));
}

QStringList VcsBaseClientImpl::commandOutputLinesFromLocal8Bit(const QByteArray &a)
{
    QString output = commandOutputFromLocal8Bit(a);
    const QChar newLine = QLatin1Char('\n');
    if (output.endsWith(newLine))
        output.truncate(output.size() - 1);
    if (output.isEmpty())
        return QStringList();
    return output.split(newLine);
}

187
188
189
190
191
void VcsBaseClientImpl::resetCachedVcsInfo(const QString &workingDir)
{
    Core::VcsManager::resetVersionControlForDirectory(workingDir);
}

192
193
194
195
196
197
198
199
200
201
202
203
204
void VcsBaseClientImpl::annotateRevisionRequested(const QString &workingDirectory,
                                                  const QString &file, const QString &change,
                                                  int line)
{
    QString changeCopy = change;
    // This might be invoked with a verbose revision description
    // "SHA1 author subject" from the annotation context menu. Strip the rest.
    const int blankPos = changeCopy.indexOf(QLatin1Char(' '));
    if (blankPos != -1)
        changeCopy.truncate(blankPos);
    annotate(workingDirectory, file, changeCopy, line);
}

205
206
207
208
209
210
211
212
213
214
215
216
217
218
bool VcsBaseClientImpl::vcsFullySynchronousExec(const QString &workingDir, const QStringList &args,
                                                QByteArray *outputData, QByteArray *errorData,
                                                unsigned flags) const
{
    QByteArray internalErrorData;
    QScopedPointer<VcsCommand> command(createCommand(workingDir));
    command->addFlags(flags);
    bool result = command->runFullySynchronous(args, vcsTimeoutS(), outputData,
                                               errorData ? errorData : &internalErrorData);
    if (!internalErrorData.isEmpty())
        VcsOutputWindow::appendError(commandOutputFromLocal8Bit(internalErrorData));
    return result;
}

219
220
221
222
223
224
225
226
227
Utils::SynchronousProcessResponse VcsBaseClientImpl::vcsSynchronousExec(const QString &workingDir,
                                                                        const QStringList &args,
                                                                        unsigned flags,
                                                                        QTextCodec *outputCodec) const
{
    return VcsBasePlugin::runVcs(workingDir, vcsBinary(), args, vcsTimeoutS(), flags,
                                 outputCodec, processEnvironment());
}

228
int VcsBaseClientImpl::vcsTimeoutS() const
229
230
231
232
{
    return settings().intValue(VcsBaseClientSettings::timeoutKey);
}

233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
VcsBaseEditorWidget *VcsBaseClientImpl::createVcsEditor(Core::Id kind, QString title,
                                                        const QString &source, QTextCodec *codec,
                                                        const char *registerDynamicProperty,
                                                        const QString &dynamicPropertyValue) const
{
    VcsBaseEditorWidget *baseEditor = 0;
    Core::IEditor *outputEditor = locateEditor(registerDynamicProperty, dynamicPropertyValue);
    const QString progressMsg = tr("Working...");
    if (outputEditor) {
        // Exists already
        outputEditor->document()->setContents(progressMsg.toUtf8());
        baseEditor = VcsBaseEditor::getVcsBaseEditor(outputEditor);
        QTC_ASSERT(baseEditor, return 0);
        Core::EditorManager::activateEditor(outputEditor);
    } else {
        outputEditor = Core::EditorManager::openEditorWithContents(kind, &title, progressMsg.toUtf8());
        outputEditor->document()->setProperty(registerDynamicProperty, dynamicPropertyValue);
        baseEditor = VcsBaseEditor::getVcsBaseEditor(outputEditor);
251
        QTC_ASSERT(baseEditor, return 0);
252
253
254
255
256
257
258
259
260
261
262
        connect(baseEditor, &VcsBaseEditorWidget::annotateRevisionRequested,
                this, &VcsBaseClientImpl::annotateRevisionRequested);
        baseEditor->setSource(source);
        if (codec)
            baseEditor->setCodec(codec);
    }

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

263
264
265
266
267
void VcsBaseClientImpl::saveSettings()
{
    settings().writeSettings(Core::ICore::settings());
}

268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
void VcsBaseClientImpl::commandFinishedGotoLine(QWidget *editorObject)
{
    VcsBaseEditorWidget *editor = qobject_cast<VcsBaseEditorWidget *>(editorObject);
    VcsCommand *cmd = qobject_cast<VcsCommand *>(d->m_cmdFinishedMapper->mapping(editor));
    if (editor && cmd) {
        if (!cmd->lastExecutionSuccess()) {
            editor->reportCommandFinished(false, cmd->lastExecutionExitCode(), cmd->cookie());
        } else if (cmd->cookie().type() == QVariant::Int) {
            const int line = cmd->cookie().toInt();
            if (line >= 0)
                editor->gotoLine(line);
        }
        d->m_cmdFinishedMapper->removeMappings(cmd);
    }
}

hjk's avatar
hjk committed
284
class VcsBaseClientPrivate
Friedemann Kleint's avatar
Friedemann Kleint committed
285
286
{
public:
287
288
289
290
291
    VcsBaseEditorParameterWidget *createDiffEditor();
    VcsBaseEditorParameterWidget *createLogEditor();

    VcsBaseClient::ParameterWidgetCreator m_diffParamWidgetCreator;
    VcsBaseClient::ParameterWidgetCreator m_logParamWidgetCreator;
Friedemann Kleint's avatar
Friedemann Kleint committed
292
293
};

294
295
296
297
298
299
300
301
302
303
VcsBaseEditorParameterWidget *VcsBaseClientPrivate::createDiffEditor()
{
    return m_diffParamWidgetCreator ? m_diffParamWidgetCreator() : 0;
}

VcsBaseEditorParameterWidget *VcsBaseClientPrivate::createLogEditor()
{
    return m_logParamWidgetCreator ? m_logParamWidgetCreator() : 0;
}

hjk's avatar
hjk committed
304
VcsBaseClient::StatusItem::StatusItem(const QString &s, const QString &f) :
305
    flags(s), file(f)
306
{ }
307

hjk's avatar
hjk committed
308
VcsBaseClient::VcsBaseClient(VcsBaseClientSettings *settings) :
309
310
    VcsBaseClientImpl(this, settings),
    d(new VcsBaseClientPrivate)
cerf's avatar
cerf committed
311
312
313
314
{
    qRegisterMetaType<QVariant>();
}

hjk's avatar
hjk committed
315
VcsBaseClient::~VcsBaseClient()
cerf's avatar
cerf committed
316
{
hjk's avatar
hjk committed
317
    delete d;
cerf's avatar
cerf committed
318
319
}

hjk's avatar
hjk committed
320
bool VcsBaseClient::synchronousCreateRepository(const QString &workingDirectory,
321
                                                const QStringList &extraOptions)
cerf's avatar
cerf committed
322
{
323
324
    QStringList args(vcsCommandString(CreateRepositoryCommand));
    args << extraOptions;
cerf's avatar
cerf committed
325
326
327
    QByteArray outputData;
    if (!vcsFullySynchronousExec(workingDirectory, args, &outputData))
        return false;
328
    VcsOutputWindow::append(commandOutputFromLocal8Bit(outputData));
329
330
331

    resetCachedVcsInfo(workingDirectory);

cerf's avatar
cerf committed
332
333
334
    return true;
}

hjk's avatar
hjk committed
335
bool VcsBaseClient::synchronousClone(const QString &workingDir,
cerf's avatar
cerf committed
336
337
                                     const QString &srcLocation,
                                     const QString &dstLocation,
338
                                     const QStringList &extraOptions)
cerf's avatar
cerf committed
339
340
341
{
    QStringList args;
    args << vcsCommandString(CloneCommand)
342
         << extraOptions << srcLocation << dstLocation;
cerf's avatar
cerf committed
343
    QByteArray stdOut;
344
345
346
    const bool cloneOk = vcsFullySynchronousExec(workingDir, args, &stdOut);
    resetCachedVcsInfo(workingDir);
    return cloneOk;
cerf's avatar
cerf committed
347
348
}

hjk's avatar
hjk committed
349
bool VcsBaseClient::synchronousAdd(const QString &workingDir, const QString &filename,
350
                                   const QStringList &extraOptions)
cerf's avatar
cerf committed
351
352
{
    QStringList args;
353
    args << vcsCommandString(AddCommand) << extraOptions << filename;
cerf's avatar
cerf committed
354
355
356
357
    QByteArray stdOut;
    return vcsFullySynchronousExec(workingDir, args, &stdOut);
}

hjk's avatar
hjk committed
358
bool VcsBaseClient::synchronousRemove(const QString &workingDir, const QString &filename,
359
                                      const QStringList &extraOptions)
cerf's avatar
cerf committed
360
361
{
    QStringList args;
362
    args << vcsCommandString(RemoveCommand) << extraOptions << filename;
cerf's avatar
cerf committed
363
364
365
366
    QByteArray stdOut;
    return vcsFullySynchronousExec(workingDir, args, &stdOut);
}

hjk's avatar
hjk committed
367
bool VcsBaseClient::synchronousMove(const QString &workingDir,
368
369
                                    const QString &from, const QString &to,
                                    const QStringList &extraOptions)
cerf's avatar
cerf committed
370
371
{
    QStringList args;
372
    args << vcsCommandString(MoveCommand) << extraOptions << from << to;
cerf's avatar
cerf committed
373
374
375
376
    QByteArray stdOut;
    return vcsFullySynchronousExec(workingDir, args, &stdOut);
}

hjk's avatar
hjk committed
377
bool VcsBaseClient::synchronousPull(const QString &workingDir,
cerf's avatar
cerf committed
378
                                    const QString &srcLocation,
379
                                    const QStringList &extraOptions)
cerf's avatar
cerf committed
380
381
{
    QStringList args;
382
    args << vcsCommandString(PullCommand) << extraOptions << srcLocation;
cerf's avatar
cerf committed
383
384
    // Disable UNIX terminals to suppress SSH prompting
    const unsigned flags =
hjk's avatar
hjk committed
385
386
387
            VcsBasePlugin::SshPasswordPrompt
            | VcsBasePlugin::ShowStdOutInLogWindow
            | VcsBasePlugin::ShowSuccessMessage;
cerf's avatar
cerf committed
388
389
390
391
392
393
394
    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
395
bool VcsBaseClient::synchronousPush(const QString &workingDir,
cerf's avatar
cerf committed
396
                                    const QString &dstLocation,
397
                                    const QStringList &extraOptions)
cerf's avatar
cerf committed
398
399
{
    QStringList args;
400
    args << vcsCommandString(PushCommand) << extraOptions << dstLocation;
cerf's avatar
cerf committed
401
402
    // Disable UNIX terminals to suppress SSH prompting
    const unsigned flags =
hjk's avatar
hjk committed
403
404
405
            VcsBasePlugin::SshPasswordPrompt
            | VcsBasePlugin::ShowStdOutInLogWindow
            | VcsBasePlugin::ShowSuccessMessage;
cerf's avatar
cerf committed
406
407
408
409
    const Utils::SynchronousProcessResponse resp = vcsSynchronousExec(workingDir, args, flags);
    return resp.result == Utils::SynchronousProcessResponse::Finished;
}

hjk's avatar
hjk committed
410
void VcsBaseClient::annotate(const QString &workingDir, const QString &file,
411
                             const QString &revision /* = QString() */,
412
413
                             int lineNumber /* = -1 */,
                             const QStringList &extraOptions)
cerf's avatar
cerf committed
414
415
416
{
    const QString vcsCmdString = vcsCommandString(AnnotateCommand);
    QStringList args;
417
    args << vcsCmdString << revisionSpec(revision) << extraOptions << file;
hjk's avatar
hjk committed
418
    const Core::Id kind = vcsEditorKind(AnnotateCommand);
419
    const QString id = VcsBaseEditor::getSource(workingDir, QStringList(file));
cerf's avatar
cerf committed
420
    const QString title = vcsEditorTitle(vcsCmdString, id);
421
    const QString source = VcsBaseEditor::getSource(workingDir, file);
cerf's avatar
cerf committed
422

423
424
    VcsBaseEditorWidget *editor = createVcsEditor(kind, title, source,
                                                  VcsBaseEditor::getCodec(source),
hjk's avatar
hjk committed
425
                                                  vcsCmdString.toLatin1().constData(), id);
cerf's avatar
cerf committed
426

hjk's avatar
hjk committed
427
    VcsCommand *cmd = createCommand(workingDir, editor);
428
429
    cmd->setCookie(lineNumber);
    enqueueJob(cmd, args);
cerf's avatar
cerf committed
430
431
}

hjk's avatar
hjk committed
432
void VcsBaseClient::diff(const QString &workingDir, const QStringList &files,
433
                         const QStringList &extraOptions)
cerf's avatar
cerf committed
434
435
{
    const QString vcsCmdString = vcsCommandString(DiffCommand);
hjk's avatar
hjk committed
436
    const Core::Id kind = vcsEditorKind(DiffCommand);
437
    const QString id = VcsBaseEditor::getTitleId(workingDir, files);
cerf's avatar
cerf committed
438
    const QString title = vcsEditorTitle(vcsCmdString, id);
439
    const QString source = VcsBaseEditor::getSource(workingDir, files);
440
441
    VcsBaseEditorWidget *editor = createVcsEditor(kind, title, source,
                                                  VcsBaseEditor::getCodec(source),
hjk's avatar
hjk committed
442
                                                  vcsCmdString.toLatin1().constData(), id);
443
    editor->setWorkingDirectory(workingDir);
cerf's avatar
cerf committed
444

445
    VcsBaseEditorParameterWidget *paramWidget = editor->configurationWidget();
446
    if (!paramWidget && (paramWidget = d->createDiffEditor())) {
447
        // editor has been just created, createVcsEditor() didn't set a configuration widget yet
448
449
        connect(editor, &VcsBaseEditorWidget::diffChunkReverted,
                paramWidget, &VcsBaseEditorParameterWidget::executeCommand);
450
451
        connect(paramWidget, &VcsBaseEditorParameterWidget::commandExecutionRequested,
                [=] { diff(workingDir, files, extraOptions); } );
452
453
454
455
456
        editor->setConfigurationWidget(paramWidget);
    }

    QStringList args;
    const QStringList paramArgs = paramWidget != 0 ? paramWidget->arguments() : QStringList();
457
    args << vcsCmdString << extraOptions << paramArgs << files;
458
    QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(0) : VcsBaseEditor::getCodec(source);
hjk's avatar
hjk committed
459
    VcsCommand *command = createCommand(workingDir, editor);
460
    command->setCodec(codec);
461
    enqueueJob(command, args, exitCodeInterpreter(DiffCommand, command));
cerf's avatar
cerf committed
462
463
}

hjk's avatar
hjk committed
464
void VcsBaseClient::log(const QString &workingDir, const QStringList &files,
465
                        const QStringList &extraOptions,
cerf's avatar
cerf committed
466
467
468
                        bool enableAnnotationContextMenu)
{
    const QString vcsCmdString = vcsCommandString(LogCommand);
hjk's avatar
hjk committed
469
    const Core::Id kind = vcsEditorKind(LogCommand);
470
    const QString id = VcsBaseEditor::getTitleId(workingDir, files);
cerf's avatar
cerf committed
471
    const QString title = vcsEditorTitle(vcsCmdString, id);
472
    const QString source = VcsBaseEditor::getSource(workingDir, files);
473
474
    VcsBaseEditorWidget *editor = createVcsEditor(kind, title, source,
                                                  VcsBaseEditor::getCodec(source),
hjk's avatar
hjk committed
475
                                                  vcsCmdString.toLatin1().constData(), id);
cerf's avatar
cerf committed
476
477
    editor->setFileLogAnnotateEnabled(enableAnnotationContextMenu);

478
    VcsBaseEditorParameterWidget *paramWidget = editor->configurationWidget();
479
    if (!paramWidget && (paramWidget = d->createLogEditor())) {
480
        // editor has been just created, createVcsEditor() didn't set a configuration widget yet
481
        connect(paramWidget, &VcsBaseEditorParameterWidget::commandExecutionRequested,
482
                [=]() { this->log(workingDir, files, extraOptions, enableAnnotationContextMenu); } );
483
        editor->setConfigurationWidget(paramWidget);
484
    }
485
486
487

    QStringList args;
    const QStringList paramArgs = paramWidget != 0 ? paramWidget->arguments() : QStringList();
488
    args << vcsCmdString << extraOptions << paramArgs << files;
489
    enqueueJob(createCommand(workingDir, editor), args);
cerf's avatar
cerf committed
490
491
}

hjk's avatar
hjk committed
492
void VcsBaseClient::revertFile(const QString &workingDir,
cerf's avatar
cerf committed
493
                               const QString &file,
494
495
                               const QString &revision,
                               const QStringList &extraOptions)
cerf's avatar
cerf committed
496
497
{
    QStringList args(vcsCommandString(RevertCommand));
498
    args << revisionSpec(revision) << extraOptions << file;
cerf's avatar
cerf committed
499
    // Indicate repository change or file list
hjk's avatar
hjk committed
500
    VcsCommand *cmd = createCommand(workingDir);
501
    cmd->setCookie(QStringList(workingDir + QLatin1Char('/') + file));
502
    connect(cmd, &VcsCommand::success, this, &VcsBaseClient::changed, Qt::QueuedConnection);
503
    enqueueJob(cmd, args);
cerf's avatar
cerf committed
504
505
}

hjk's avatar
hjk committed
506
void VcsBaseClient::revertAll(const QString &workingDir, const QString &revision,
507
                              const QStringList &extraOptions)
cerf's avatar
cerf committed
508
509
{
    QStringList args(vcsCommandString(RevertCommand));
510
    args << revisionSpec(revision) << extraOptions;
cerf's avatar
cerf committed
511
    // Indicate repository change or file list
hjk's avatar
hjk committed
512
    VcsCommand *cmd = createCommand(workingDir);
513
    cmd->setCookie(QStringList(workingDir));
514
    connect(cmd, &VcsCommand::success, this, &VcsBaseClient::changed, Qt::QueuedConnection);
515
    enqueueJob(createCommand(workingDir), args);
cerf's avatar
cerf committed
516
517
}

hjk's avatar
hjk committed
518
void VcsBaseClient::status(const QString &workingDir, const QString &file,
519
                           const QStringList &extraOptions)
cerf's avatar
cerf committed
520
521
{
    QStringList args(vcsCommandString(StatusCommand));
522
    args << extraOptions << file;
523
    VcsOutputWindow::setRepository(workingDir);
hjk's avatar
hjk committed
524
    VcsCommand *cmd = createCommand(workingDir, 0, VcsWindowOutputBind);
525
526
    connect(cmd, &VcsCommand::finished,
            VcsOutputWindow::instance(), &VcsOutputWindow::clearRepository,
527
528
            Qt::QueuedConnection);
    enqueueJob(cmd, args);
cerf's avatar
cerf committed
529
530
}

hjk's avatar
hjk committed
531
void VcsBaseClient::emitParsedStatus(const QString &repository, const QStringList &extraOptions)
cerf's avatar
cerf committed
532
533
{
    QStringList args(vcsCommandString(StatusCommand));
534
    args << extraOptions;
hjk's avatar
hjk committed
535
    VcsCommand *cmd = createCommand(repository);
536
    connect(cmd, &VcsCommand::output, this, &VcsBaseClient::statusParser);
537
    enqueueJob(cmd, args);
cerf's avatar
cerf committed
538
539
}

hjk's avatar
hjk committed
540
QString VcsBaseClient::vcsCommandString(VcsCommandTag cmd) const
cerf's avatar
cerf committed
541
{
Tobias Hunger's avatar
Tobias Hunger committed
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
    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
558
    }
Tobias Hunger's avatar
Tobias Hunger committed
559
    return QString();
cerf's avatar
cerf committed
560
561
}

hjk's avatar
hjk committed
562
Utils::ExitCodeInterpreter *VcsBaseClient::exitCodeInterpreter(VcsCommandTag cmd, QObject *parent) const
563
564
565
566
567
568
{
    Q_UNUSED(cmd)
    Q_UNUSED(parent)
    return 0;
}

569
570
571
572
573
574
575
576
577
578
void VcsBaseClient::setDiffParameterWidgetCreator(ParameterWidgetCreator creator)
{
    d->m_diffParamWidgetCreator = std::move(creator);
}

void VcsBaseClient::setLogParameterWidgetCreator(ParameterWidgetCreator creator)
{
    d->m_logParamWidgetCreator = std::move(creator);
}

hjk's avatar
hjk committed
579
void VcsBaseClient::import(const QString &repositoryRoot, const QStringList &files,
580
                           const QStringList &extraOptions)
cerf's avatar
cerf committed
581
582
{
    QStringList args(vcsCommandString(ImportCommand));
583
    args << extraOptions << files;
584
    enqueueJob(createCommand(repositoryRoot), args);
cerf's avatar
cerf committed
585
586
}

hjk's avatar
hjk committed
587
void VcsBaseClient::view(const QString &source, const QString &id,
588
                         const QStringList &extraOptions)
cerf's avatar
cerf committed
589
{
590
591
    QStringList args;
    args << extraOptions << revisionSpec(id);
hjk's avatar
hjk committed
592
    const Core::Id kind = vcsEditorKind(DiffCommand);
cerf's avatar
cerf committed
593
594
    const QString title = vcsEditorTitle(vcsCommandString(LogCommand), id);

595
596
    VcsBaseEditorWidget *editor = createVcsEditor(kind, title, source,
                                                  VcsBaseEditor::getCodec(source), "view", id);
cerf's avatar
cerf committed
597

598
599
    const QFileInfo fi(source);
    const QString workingDirPath = fi.isFile() ? fi.absolutePath() : source;
600
    enqueueJob(createCommand(workingDirPath, editor), args);
cerf's avatar
cerf committed
601
602
}

hjk's avatar
hjk committed
603
void VcsBaseClient::update(const QString &repositoryRoot, const QString &revision,
604
                           const QStringList &extraOptions)
cerf's avatar
cerf committed
605
606
{
    QStringList args(vcsCommandString(UpdateCommand));
607
    args << revisionSpec(revision) << extraOptions;
hjk's avatar
hjk committed
608
    VcsCommand *cmd = createCommand(repositoryRoot);
609
    cmd->setCookie(repositoryRoot);
610
    connect(cmd, &VcsCommand::success, this, &VcsBaseClient::changed, Qt::QueuedConnection);
611
    enqueueJob(cmd, args);
cerf's avatar
cerf committed
612
613
}

hjk's avatar
hjk committed
614
void VcsBaseClient::commit(const QString &repositoryRoot,
cerf's avatar
cerf committed
615
616
                           const QStringList &files,
                           const QString &commitMessageFile,
617
                           const QStringList &extraOptions)
cerf's avatar
cerf committed
618
{
619
    // Handling of commitMessageFile is a bit tricky :
hjk's avatar
hjk committed
620
    //   VcsBaseClient cannot do something with it because it doesn't know which
621
622
623
    //   option to use (-F ? but sub VCS clients might require a different option
    //   name like -l for hg ...)
    //
hjk's avatar
hjk committed
624
    //   So descendants of VcsBaseClient *must* redefine commit() and extend
625
626
    //   extraOptions with the usage for commitMessageFile (see BazaarClient::commit()
    //   for example)
cerf's avatar
cerf committed
627
    QStringList args(vcsCommandString(CommitCommand));
628
    args << extraOptions << files;
629
630
631
632
    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
633
634
}

hjk's avatar
hjk committed
635
QString VcsBaseClient::vcsEditorTitle(const QString &vcsCmd, const QString &sourceId) const
cerf's avatar
cerf committed
636
{
637
    return vcsBinary().toFileInfo().baseName() +
638
            QLatin1Char(' ') + vcsCmd + QLatin1Char(' ') +
639
            Utils::FileName::fromString(sourceId).fileName();
cerf's avatar
cerf committed
640
641
}

642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
void VcsBaseClient::statusParser(const QString &text)
{
    QList<VcsBaseClient::StatusItem> lineInfoList;

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

    foreach (const QString &string, rawStatusList) {
        const VcsBaseClient::StatusItem lineInfo = parseStatusLine(string);
        if (!lineInfo.flags.isEmpty() && !lineInfo.file.isEmpty())
            lineInfoList.append(lineInfo);
    }

    emit parsedStatus(lineInfoList);
}

hjk's avatar
hjk committed
657
} // namespace VcsBase
658
659

#include "moc_vcsbaseclient.cpp"